2017-10-18 23:46:47 +02:00
|
|
|
//===- LoopUnroll.cpp - Loop unroller pass --------------------------------===//
|
2005-04-22 01:48:37 +02:00
|
|
|
//
|
2019-01-19 09:50:56 +01:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2005-04-22 01:48:37 +02:00
|
|
|
//
|
2004-04-18 07:20:17 +02:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This pass implements a simple loop unroller. It works best when loops have
|
|
|
|
// been canonicalized by the -indvars pass, allowing it to determine the trip
|
|
|
|
// counts of loops easily.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2016-07-20 01:54:23 +02:00
|
|
|
#include "llvm/Transforms/Scalar/LoopUnrollPass.h"
|
2017-10-18 23:46:47 +02:00
|
|
|
#include "llvm/ADT/DenseMap.h"
|
|
|
|
#include "llvm/ADT/DenseMapInfo.h"
|
|
|
|
#include "llvm/ADT/DenseSet.h"
|
|
|
|
#include "llvm/ADT/None.h"
|
|
|
|
#include "llvm/ADT/Optional.h"
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2015-02-13 04:57:40 +01:00
|
|
|
#include "llvm/ADT/SetVector.h"
|
2017-10-18 23:46:47 +02:00
|
|
|
#include "llvm/ADT/SmallPtrSet.h"
|
|
|
|
#include "llvm/ADT/SmallVector.h"
|
|
|
|
#include "llvm/ADT/StringRef.h"
|
2016-12-19 09:22:17 +01:00
|
|
|
#include "llvm/Analysis/AssumptionCache.h"
|
2019-04-15 18:49:00 +02:00
|
|
|
#include "llvm/Analysis/BlockFrequencyInfo.h"
|
2011-01-02 08:35:53 +01:00
|
|
|
#include "llvm/Analysis/CodeMetrics.h"
|
2019-04-15 18:49:00 +02:00
|
|
|
#include "llvm/Analysis/LazyBlockFrequencyInfo.h"
|
2017-10-18 23:46:47 +02:00
|
|
|
#include "llvm/Analysis/LoopAnalysisManager.h"
|
|
|
|
#include "llvm/Analysis/LoopInfo.h"
|
2012-12-03 17:50:05 +01:00
|
|
|
#include "llvm/Analysis/LoopPass.h"
|
2016-02-09 00:03:59 +01:00
|
|
|
#include "llvm/Analysis/LoopUnrollAnalyzer.h"
|
2017-10-10 01:19:02 +02:00
|
|
|
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
|
2017-08-04 01:42:58 +02:00
|
|
|
#include "llvm/Analysis/ProfileSummaryInfo.h"
|
2010-07-26 20:11:16 +02:00
|
|
|
#include "llvm/Analysis/ScalarEvolution.h"
|
2017-10-18 23:46:47 +02:00
|
|
|
#include "llvm/Analysis/TargetTransformInfo.h"
|
|
|
|
#include "llvm/IR/BasicBlock.h"
|
|
|
|
#include "llvm/IR/CFG.h"
|
|
|
|
#include "llvm/IR/Constant.h"
|
|
|
|
#include "llvm/IR/Constants.h"
|
|
|
|
#include "llvm/IR/DiagnosticInfo.h"
|
2014-01-13 10:26:24 +01:00
|
|
|
#include "llvm/IR/Dominators.h"
|
2017-10-18 23:46:47 +02:00
|
|
|
#include "llvm/IR/Function.h"
|
|
|
|
#include "llvm/IR/Instruction.h"
|
|
|
|
#include "llvm/IR/Instructions.h"
|
2013-01-02 12:36:10 +01:00
|
|
|
#include "llvm/IR/IntrinsicInst.h"
|
2014-06-17 01:53:02 +02:00
|
|
|
#include "llvm/IR/Metadata.h"
|
2017-10-18 23:46:47 +02:00
|
|
|
#include "llvm/IR/PassManager.h"
|
Sink all InitializePasses.h includes
This file lists every pass in LLVM, and is included by Pass.h, which is
very popular. Every time we add, remove, or rename a pass in LLVM, it
caused lots of recompilation.
I found this fact by looking at this table, which is sorted by the
number of times a file was changed over the last 100,000 git commits
multiplied by the number of object files that depend on it in the
current checkout:
recompiles touches affected_files header
342380 95 3604 llvm/include/llvm/ADT/STLExtras.h
314730 234 1345 llvm/include/llvm/InitializePasses.h
307036 118 2602 llvm/include/llvm/ADT/APInt.h
213049 59 3611 llvm/include/llvm/Support/MathExtras.h
170422 47 3626 llvm/include/llvm/Support/Compiler.h
162225 45 3605 llvm/include/llvm/ADT/Optional.h
158319 63 2513 llvm/include/llvm/ADT/Triple.h
140322 39 3598 llvm/include/llvm/ADT/StringRef.h
137647 59 2333 llvm/include/llvm/Support/Error.h
131619 73 1803 llvm/include/llvm/Support/FileSystem.h
Before this change, touching InitializePasses.h would cause 1345 files
to recompile. After this change, touching it only causes 550 compiles in
an incremental rebuild.
Reviewers: bkramer, asbirlea, bollu, jdoerfert
Differential Revision: https://reviews.llvm.org/D70211
2019-11-13 22:15:01 +01:00
|
|
|
#include "llvm/InitializePasses.h"
|
2017-10-18 23:46:47 +02:00
|
|
|
#include "llvm/Pass.h"
|
|
|
|
#include "llvm/Support/Casting.h"
|
2004-09-02 00:55:40 +02:00
|
|
|
#include "llvm/Support/CommandLine.h"
|
|
|
|
#include "llvm/Support/Debug.h"
|
2017-10-18 23:46:47 +02:00
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
2009-07-25 02:23:56 +02:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2016-05-05 02:54:54 +02:00
|
|
|
#include "llvm/Transforms/Scalar.h"
|
2017-01-11 10:43:56 +01:00
|
|
|
#include "llvm/Transforms/Scalar/LoopPassManager.h"
|
2018-03-28 19:44:36 +02:00
|
|
|
#include "llvm/Transforms/Utils.h"
|
2020-07-31 20:31:58 +02:00
|
|
|
#include "llvm/Transforms/Utils/LoopPeel.h"
|
2017-10-18 23:46:47 +02:00
|
|
|
#include "llvm/Transforms/Utils/LoopSimplify.h"
|
[LPM] Factor all of the loop analysis usage updates into a common helper
routine.
We were getting this wrong in small ways and generally being very
inconsistent about it across loop passes. Instead, let's have a common
place where we do this. One minor downside is that this will require
some analyses like SCEV in more places than they are strictly needed.
However, this seems benign as these analyses are complete no-ops, and
without this consistency we can in many cases end up with the legacy
pass manager scheduling deciding to split up a loop pass pipeline in
order to run the function analysis half-way through. It is very, very
annoying to fix these without just being very pedantic across the board.
The only loop passes I've not updated here are ones that use
AU.setPreservesAll() such as IVUsers (an analysis) and the pass printer.
They seemed less relevant.
With this patch, almost all of the problems in PR24804 around loop pass
pipelines are fixed. The one remaining issue is that we run simplify-cfg
and instcombine in the middle of the loop pass pipeline. We've recently
added some loop variants of these passes that would seem substantially
cleaner to use, but this at least gets us much closer to the previous
state. Notably, the seven loop pass managers is down to three.
I've not updated the loop passes using LoopAccessAnalysis because that
analysis hasn't been fully wired into LoopSimplify/LCSSA, and it isn't
clear that those transforms want to support those forms anyways. They
all run late anyways, so this is harmless. Similarly, LSR is left alone
because it already carefully manages its forms and doesn't need to get
fused into a single loop pass manager with a bunch of other loop passes.
LoopReroll didn't use loop simplified form previously, and I've updated
the test case to match the trivially different output.
Finally, I've also factored all the pass initialization for the passes
that use this technique as well, so that should be done regularly and
reliably.
Thanks to James for the help reviewing and thinking about this stuff,
and Ben for help thinking about it as well!
Differential Revision: http://reviews.llvm.org/D17435
llvm-svn: 261316
2016-02-19 11:45:18 +01:00
|
|
|
#include "llvm/Transforms/Utils/LoopUtils.h"
|
2019-04-15 18:49:00 +02:00
|
|
|
#include "llvm/Transforms/Utils/SizeOpts.h"
|
2008-05-14 02:24:14 +02:00
|
|
|
#include "llvm/Transforms/Utils/UnrollLoop.h"
|
2017-10-18 23:46:47 +02:00
|
|
|
#include <algorithm>
|
|
|
|
#include <cassert>
|
|
|
|
#include <cstdint>
|
|
|
|
#include <limits>
|
|
|
|
#include <string>
|
|
|
|
#include <tuple>
|
2016-05-27 16:27:24 +02:00
|
|
|
#include <utility>
|
2004-04-18 07:20:17 +02:00
|
|
|
|
2008-05-14 02:24:14 +02:00
|
|
|
using namespace llvm;
|
2004-04-18 07:20:17 +02:00
|
|
|
|
2014-04-22 04:55:47 +02:00
|
|
|
#define DEBUG_TYPE "loop-unroll"
|
|
|
|
|
2019-05-23 23:52:59 +02:00
|
|
|
cl::opt<bool> llvm::ForgetSCEVInLoopUnroll(
|
|
|
|
"forget-scev-loop-unroll", cl::init(false), cl::Hidden,
|
|
|
|
cl::desc("Forget everything in SCEV when doing LoopUnroll, instead of just"
|
2020-10-17 15:20:55 +02:00
|
|
|
" the current top-most loop. This is sometimes preferred to reduce"
|
2019-05-23 23:52:59 +02:00
|
|
|
" compile time."));
|
|
|
|
|
2008-05-13 02:00:25 +02:00
|
|
|
static cl::opt<unsigned>
|
2016-01-12 01:55:26 +01:00
|
|
|
UnrollThreshold("unroll-threshold", cl::Hidden,
|
2017-01-18 00:39:33 +01:00
|
|
|
cl::desc("The cost threshold for loop unrolling"));
|
|
|
|
|
2020-08-11 15:19:35 +02:00
|
|
|
static cl::opt<unsigned>
|
|
|
|
UnrollOptSizeThreshold(
|
|
|
|
"unroll-optsize-threshold", cl::init(0), cl::Hidden,
|
|
|
|
cl::desc("The cost threshold for loop unrolling when optimizing for "
|
|
|
|
"size"));
|
|
|
|
|
2017-01-18 00:39:33 +01:00
|
|
|
static cl::opt<unsigned> UnrollPartialThreshold(
|
|
|
|
"unroll-partial-threshold", cl::Hidden,
|
|
|
|
cl::desc("The cost threshold for partial loop unrolling"));
|
[Unroll] Rework the naming and structure of the new unroll heuristics.
The new naming is (to me) much easier to understand. Here is a summary
of the new state of the world:
- '*Threshold' is the threshold for full unrolling. It is measured
against the estimated unrolled cost as computed by getUserCost in TTI
(or CodeMetrics, etc). We will exceed this threshold when unrolling
loops where unrolling exposes a significant degree of simplification
of the logic within the loop.
- '*PercentDynamicCostSavedThreshold' is the percentage of the loop's
estimated dynamic execution cost which needs to be saved by unrolling
to apply a discount to the estimated unrolled cost.
- '*DynamicCostSavingsDiscount' is the discount applied to the estimated
unrolling cost when the dynamic savings are expected to be high.
When actually analyzing the loop, we now produce both an estimated
unrolled cost, and an estimated rolled cost. The rolled cost is notably
a dynamic estimate based on our analysis of the expected execution of
each iteration.
While we're still working to build up the infrastructure for making
these estimates, to me it is much more clear *how* to make them better
when they have reasonably descriptive names. For example, we may want to
apply estimated (from heuristics or profiles) dynamic execution weights
to the *dynamic* cost estimates. If we start doing that, we would also
need to track the static unrolled cost and the dynamic unrolled cost, as
only the latter could reasonably be weighted by profile information.
This patch is sadly not without functionality change for the new unroll
analysis logic. Buried in the heuristic management were several things
that surprised me. For example, we never subtracted the optimized
instruction count off when comparing against the unroll heursistics!
I don't know if this just got lost somewhere along the way or what, but
with the new accounting of things, this is much easier to keep track of
and we use the post-simplification cost estimate to compare to the
thresholds, and use the dynamic cost reduction ratio to select whether
we can exceed the baseline threshold.
The old values of these flags also don't necessarily make sense. My
impression is that none of these thresholds or discounts have been tuned
yet, and so they're just arbitrary placehold numbers. As such, I've not
bothered to adjust for the fact that this is now a discount and not
a tow-tier threshold model. We need to tune all these values once the
logic is ready to be enabled.
Differential Revision: http://reviews.llvm.org/D9966
llvm-svn: 239164
2015-06-05 19:01:43 +02:00
|
|
|
|
2016-12-30 01:50:28 +01:00
|
|
|
static cl::opt<unsigned> UnrollMaxPercentThresholdBoost(
|
|
|
|
"unroll-max-percent-threshold-boost", cl::init(400), cl::Hidden,
|
|
|
|
cl::desc("The maximum 'boost' (represented as a percentage >= 100) applied "
|
|
|
|
"to the threshold when aggressively unrolling a loop due to the "
|
|
|
|
"dynamic cost savings. If completely unrolling a loop will reduce "
|
|
|
|
"the total runtime from X to Y, we boost the loop unroll "
|
|
|
|
"threshold to DefaultThreshold*std::min(MaxPercentThresholdBoost, "
|
|
|
|
"X/Y). This limit avoids excessive code bloat."));
|
2007-05-11 22:53:41 +02:00
|
|
|
|
2015-02-05 03:34:00 +01:00
|
|
|
static cl::opt<unsigned> UnrollMaxIterationsCountToAnalyze(
|
2016-05-25 01:00:05 +02:00
|
|
|
"unroll-max-iteration-count-to-analyze", cl::init(10), cl::Hidden,
|
2015-02-05 03:34:00 +01:00
|
|
|
cl::desc("Don't allow loop unrolling to simulate more than this number of"
|
|
|
|
"iterations when checking full unroll profitability"));
|
|
|
|
|
2016-05-05 02:54:54 +02:00
|
|
|
static cl::opt<unsigned> UnrollCount(
|
|
|
|
"unroll-count", cl::Hidden,
|
|
|
|
cl::desc("Use this unroll count for all loops including those with "
|
|
|
|
"unroll_count pragma values, for testing purposes"));
|
2004-04-18 07:20:17 +02:00
|
|
|
|
2016-05-05 02:54:54 +02:00
|
|
|
static cl::opt<unsigned> UnrollMaxCount(
|
|
|
|
"unroll-max-count", cl::Hidden,
|
|
|
|
cl::desc("Set the max unroll count for partial and runtime unrolling, for"
|
|
|
|
"testing purposes"));
|
2016-04-06 18:57:25 +02:00
|
|
|
|
2016-05-05 02:54:54 +02:00
|
|
|
static cl::opt<unsigned> UnrollFullMaxCount(
|
|
|
|
"unroll-full-max-count", cl::Hidden,
|
|
|
|
cl::desc(
|
|
|
|
"Set the max unroll count for full unrolling, for testing purposes"));
|
2016-04-06 18:57:25 +02:00
|
|
|
|
2008-07-29 15:21:23 +02:00
|
|
|
static cl::opt<bool>
|
2016-05-05 02:54:54 +02:00
|
|
|
UnrollAllowPartial("unroll-allow-partial", cl::Hidden,
|
|
|
|
cl::desc("Allows loops to be partially unrolled until "
|
|
|
|
"-unroll-threshold loop size is reached."));
|
2008-07-29 15:21:23 +02:00
|
|
|
|
2016-05-28 01:15:06 +02:00
|
|
|
static cl::opt<bool> UnrollAllowRemainder(
|
|
|
|
"unroll-allow-remainder", cl::Hidden,
|
|
|
|
cl::desc("Allow generation of a loop remainder (extra iterations) "
|
|
|
|
"when unrolling a loop."));
|
|
|
|
|
2011-12-09 07:19:40 +01:00
|
|
|
static cl::opt<bool>
|
2016-05-05 02:54:54 +02:00
|
|
|
UnrollRuntime("unroll-runtime", cl::ZeroOrMore, cl::Hidden,
|
|
|
|
cl::desc("Unroll loops with run-time trip counts"));
|
2014-06-17 01:53:02 +02:00
|
|
|
|
2016-10-12 23:29:38 +02:00
|
|
|
static cl::opt<unsigned> UnrollMaxUpperBound(
|
|
|
|
"unroll-max-upperbound", cl::init(8), cl::Hidden,
|
|
|
|
cl::desc(
|
|
|
|
"The max of trip count upper bound that is considered in unrolling"));
|
|
|
|
|
2016-05-05 02:54:54 +02:00
|
|
|
static cl::opt<unsigned> PragmaUnrollThreshold(
|
|
|
|
"pragma-unroll-threshold", cl::init(16 * 1024), cl::Hidden,
|
|
|
|
cl::desc("Unrolled size limit for loops with an unroll(full) or "
|
|
|
|
"unroll_count pragma."));
|
2016-01-12 01:55:26 +01:00
|
|
|
|
2016-11-17 02:17:02 +01:00
|
|
|
static cl::opt<unsigned> FlatLoopTripCountThreshold(
|
|
|
|
"flat-loop-tripcount-threshold", cl::init(5), cl::Hidden,
|
|
|
|
cl::desc("If the runtime tripcount for the loop is lower than the "
|
|
|
|
"threshold, the loop is considered as flat and will be less "
|
|
|
|
"aggressively unrolled."));
|
|
|
|
|
2017-08-14 11:25:26 +02:00
|
|
|
static cl::opt<bool> UnrollUnrollRemainder(
|
|
|
|
"unroll-remainder", cl::Hidden,
|
|
|
|
cl::desc("Allow the loop remainder to be unrolled."));
|
|
|
|
|
2017-01-25 03:49:01 +01:00
|
|
|
// This option isn't ever intended to be enabled, it serves to allow
|
|
|
|
// experiments to check the assumptions about when this kind of revisit is
|
|
|
|
// necessary.
|
|
|
|
static cl::opt<bool> UnrollRevisitChildLoops(
|
|
|
|
"unroll-revisit-child-loops", cl::Hidden,
|
|
|
|
cl::desc("Enqueue and re-visit child loops in the loop PM after unrolling. "
|
|
|
|
"This shouldn't typically be needed as child loops (or their "
|
|
|
|
"clones) were already visited."));
|
|
|
|
|
[llvm] Make new pass manager's OptimizationLevel a class
Summary:
The old pass manager separated speed optimization and size optimization
levels into two unsigned values. Coallescing both in an enum in the new
pass manager may lead to unintentional casts and comparisons.
In particular, taking a look at how the loop unroll passes were constructed
previously, the Os/Oz are now (==new pass manager) treated just like O3,
likely unintentionally.
This change disallows raw comparisons between optimization levels, to
avoid such unintended effects. As an effect, the O{s|z} behavior changes
for loop unrolling and loop unroll and jam, matching O2 rather than O3.
The change also parameterizes the threshold values used for loop
unrolling, primarily to aid testing.
Reviewers: tejohnson, davidxl
Reviewed By: tejohnson
Subscribers: zzheng, ychen, mehdi_amini, hiraditya, steven_wu, dexonsmith, dang, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D72547
2020-01-16 17:51:50 +01:00
|
|
|
static cl::opt<unsigned> UnrollThresholdAggressive(
|
|
|
|
"unroll-threshold-aggressive", cl::init(300), cl::Hidden,
|
|
|
|
cl::desc("Threshold (max size of unrolled loop) to use in aggressive (O3) "
|
|
|
|
"optimizations"));
|
|
|
|
static cl::opt<unsigned>
|
|
|
|
UnrollThresholdDefault("unroll-threshold-default", cl::init(150),
|
|
|
|
cl::Hidden,
|
|
|
|
cl::desc("Default threshold (max size of unrolled "
|
|
|
|
"loop), used in all but O3 optimizations"));
|
|
|
|
|
2016-01-12 01:55:26 +01:00
|
|
|
/// A magic value for use with the Threshold parameter to indicate
|
|
|
|
/// that the loop unroll should be performed regardless of how much
|
|
|
|
/// code expansion would result.
|
2017-10-18 23:46:47 +02:00
|
|
|
static const unsigned NoThreshold = std::numeric_limits<unsigned>::max();
|
2016-01-12 01:55:26 +01:00
|
|
|
|
|
|
|
/// Gather the various unrolling parameters based on the defaults, compiler
|
2016-05-28 01:15:06 +02:00
|
|
|
/// flags, TTI overrides and user specified parameters.
|
2018-07-01 14:47:30 +02:00
|
|
|
TargetTransformInfo::UnrollingPreferences llvm::gatherUnrollingPreferences(
|
2019-04-15 18:49:00 +02:00
|
|
|
Loop *L, ScalarEvolution &SE, const TargetTransformInfo &TTI,
|
|
|
|
BlockFrequencyInfo *BFI, ProfileSummaryInfo *PSI, int OptLevel,
|
Increases full-unroll threshold.
Summary:
The default threshold for fully unroll is too conservative. This patch doubles the full-unroll threshold
This change will affect the following speccpu2006 benchmarks (performance numbers were collected from Intel Sandybridge):
Performance:
403 0.11%
433 0.51%
445 0.48%
447 3.50%
453 1.49%
464 0.75%
Code size:
403 0.56%
433 0.96%
445 2.16%
447 2.96%
453 0.94%
464 8.02%
The compiler time overhead is similar with code size.
Reviewers: davidxl, mkuper, mzolotukhin, hfinkel, chandlerc
Reviewed By: hfinkel, chandlerc
Subscribers: mehdi_amini, zzheng, efriedma, haicheng, hfinkel, llvm-commits
Differential Revision: https://reviews.llvm.org/D28368
llvm-svn: 295538
2017-02-18 04:46:51 +01:00
|
|
|
Optional<unsigned> UserThreshold, Optional<unsigned> UserCount,
|
|
|
|
Optional<bool> UserAllowPartial, Optional<bool> UserRuntime,
|
[NFC] Separate Peeling Properties into its own struct (re-land after minor fix)
Summary:
This patch separates the peeling specific parameters from the UnrollingPreferences,
and creates a new struct called PeelingPreferences. Functions which used the
UnrollingPreferences struct for peeling have been updated to use the PeelingPreferences struct.
Author: sidbav (Sidharth Baveja)
Reviewers: Whitney (Whitney Tsang), Meinersbur (Michael Kruse), skatkov (Serguei Katkov), ashlykov (Arkady Shlykov), bogner (Justin Bogner), hfinkel (Hal Finkel), anhtuyen (Anh Tuyen Tran), nikic (Nikita Popov)
Reviewed By: Meinersbur (Michael Kruse)
Subscribers: fhahn (Florian Hahn), hiraditya (Aditya Kumar), llvm-commits, LLVM
Tag: LLVM
Differential Revision: https://reviews.llvm.org/D80580
2020-07-10 20:38:08 +02:00
|
|
|
Optional<bool> UserUpperBound, Optional<unsigned> UserFullUnrollMaxCount) {
|
2016-01-12 01:55:26 +01:00
|
|
|
TargetTransformInfo::UnrollingPreferences UP;
|
|
|
|
|
|
|
|
// Set up the defaults
|
[llvm] Make new pass manager's OptimizationLevel a class
Summary:
The old pass manager separated speed optimization and size optimization
levels into two unsigned values. Coallescing both in an enum in the new
pass manager may lead to unintentional casts and comparisons.
In particular, taking a look at how the loop unroll passes were constructed
previously, the Os/Oz are now (==new pass manager) treated just like O3,
likely unintentionally.
This change disallows raw comparisons between optimization levels, to
avoid such unintended effects. As an effect, the O{s|z} behavior changes
for loop unrolling and loop unroll and jam, matching O2 rather than O3.
The change also parameterizes the threshold values used for loop
unrolling, primarily to aid testing.
Reviewers: tejohnson, davidxl
Reviewed By: tejohnson
Subscribers: zzheng, ychen, mehdi_amini, hiraditya, steven_wu, dexonsmith, dang, cfe-commits, llvm-commits
Tags: #clang, #llvm
Differential Revision: https://reviews.llvm.org/D72547
2020-01-16 17:51:50 +01:00
|
|
|
UP.Threshold =
|
|
|
|
OptLevel > 2 ? UnrollThresholdAggressive : UnrollThresholdDefault;
|
2016-12-30 01:50:28 +01:00
|
|
|
UP.MaxPercentThresholdBoost = 400;
|
2020-08-11 15:19:35 +02:00
|
|
|
UP.OptSizeThreshold = UnrollOptSizeThreshold;
|
2017-01-18 00:39:33 +01:00
|
|
|
UP.PartialThreshold = 150;
|
2020-08-11 15:19:35 +02:00
|
|
|
UP.PartialOptSizeThreshold = UnrollOptSizeThreshold;
|
2016-01-12 01:55:26 +01:00
|
|
|
UP.Count = 0;
|
2016-09-28 11:41:38 +02:00
|
|
|
UP.DefaultUnrollRuntimeCount = 8;
|
2017-10-18 23:46:47 +02:00
|
|
|
UP.MaxCount = std::numeric_limits<unsigned>::max();
|
|
|
|
UP.FullUnrollMaxCount = std::numeric_limits<unsigned>::max();
|
2016-11-09 20:56:39 +01:00
|
|
|
UP.BEInsns = 2;
|
2016-01-12 01:55:26 +01:00
|
|
|
UP.Partial = false;
|
|
|
|
UP.Runtime = false;
|
2016-05-28 01:15:06 +02:00
|
|
|
UP.AllowRemainder = true;
|
2017-08-14 11:25:26 +02:00
|
|
|
UP.UnrollRemainder = false;
|
2016-01-12 01:55:26 +01:00
|
|
|
UP.AllowExpensiveTripCount = false;
|
2016-05-28 01:15:06 +02:00
|
|
|
UP.Force = false;
|
2016-10-12 23:29:38 +02:00
|
|
|
UP.UpperBound = false;
|
2018-07-01 14:47:30 +02:00
|
|
|
UP.UnrollAndJam = false;
|
|
|
|
UP.UnrollAndJamInnerLoopThreshold = 60;
|
[AMDGPU] Increase max iterations count to analyze complete unroll
Summary: In some cases inner loops may not get boosts so try to analyze them deeper.
Reviewers: rampitec, mzolotukhin
Reviewed By: rampitec
Subscribers: arsenm, kzhuravl, jvesely, wdng, nhaehnle, yaxunl, dstuttard, tpr, t-tye, hiraditya, zzheng, kerbowa, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D81204
2020-06-05 01:18:18 +02:00
|
|
|
UP.MaxIterationsCountToAnalyze = UnrollMaxIterationsCountToAnalyze;
|
2016-01-12 01:55:26 +01:00
|
|
|
|
|
|
|
// Override with any target specific settings
|
[LoopUnroll] Pass SCEV to getUnrollingPreferences hook. NFCI.
Reviewers: sanjoy, anna, reames, apilipenko, igor-laevsky, mkuper
Subscribers: jholewinski, arsenm, mzolotukhin, nemanjai, nhaehnle, javed.absar, mcrosier, llvm-commits
Differential Revision: https://reviews.llvm.org/D34531
llvm-svn: 306554
2017-06-28 17:53:17 +02:00
|
|
|
TTI.getUnrollingPreferences(L, SE, UP);
|
2016-01-12 01:55:26 +01:00
|
|
|
|
|
|
|
// Apply size attributes
|
2019-04-15 18:49:00 +02:00
|
|
|
bool OptForSize = L->getHeader()->getParent()->hasOptSize() ||
|
2021-01-06 23:42:26 +01:00
|
|
|
// Let unroll hints / pragmas take precedence over PGSO.
|
|
|
|
(hasUnrollTransformation(L) != TM_ForcedByUser &&
|
|
|
|
llvm::shouldOptimizeForSize(L->getHeader(), PSI, BFI,
|
|
|
|
PGSOQueryType::IRPass));
|
2019-04-15 18:49:00 +02:00
|
|
|
if (OptForSize) {
|
2016-01-12 01:55:26 +01:00
|
|
|
UP.Threshold = UP.OptSizeThreshold;
|
|
|
|
UP.PartialThreshold = UP.PartialOptSizeThreshold;
|
2019-04-17 17:57:43 +02:00
|
|
|
UP.MaxPercentThresholdBoost = 100;
|
2016-01-12 01:55:26 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Apply any user values specified by cl::opt
|
2017-01-18 00:39:33 +01:00
|
|
|
if (UnrollThreshold.getNumOccurrences() > 0)
|
2016-01-12 01:55:26 +01:00
|
|
|
UP.Threshold = UnrollThreshold;
|
2017-01-18 00:39:33 +01:00
|
|
|
if (UnrollPartialThreshold.getNumOccurrences() > 0)
|
|
|
|
UP.PartialThreshold = UnrollPartialThreshold;
|
2016-12-30 01:50:28 +01:00
|
|
|
if (UnrollMaxPercentThresholdBoost.getNumOccurrences() > 0)
|
|
|
|
UP.MaxPercentThresholdBoost = UnrollMaxPercentThresholdBoost;
|
2016-04-06 18:57:25 +02:00
|
|
|
if (UnrollMaxCount.getNumOccurrences() > 0)
|
|
|
|
UP.MaxCount = UnrollMaxCount;
|
|
|
|
if (UnrollFullMaxCount.getNumOccurrences() > 0)
|
|
|
|
UP.FullUnrollMaxCount = UnrollFullMaxCount;
|
2016-01-12 01:55:26 +01:00
|
|
|
if (UnrollAllowPartial.getNumOccurrences() > 0)
|
|
|
|
UP.Partial = UnrollAllowPartial;
|
2016-05-28 01:15:06 +02:00
|
|
|
if (UnrollAllowRemainder.getNumOccurrences() > 0)
|
|
|
|
UP.AllowRemainder = UnrollAllowRemainder;
|
2016-01-12 01:55:26 +01:00
|
|
|
if (UnrollRuntime.getNumOccurrences() > 0)
|
|
|
|
UP.Runtime = UnrollRuntime;
|
2016-10-12 23:29:38 +02:00
|
|
|
if (UnrollMaxUpperBound == 0)
|
|
|
|
UP.UpperBound = false;
|
2017-08-14 11:25:26 +02:00
|
|
|
if (UnrollUnrollRemainder.getNumOccurrences() > 0)
|
|
|
|
UP.UnrollRemainder = UnrollUnrollRemainder;
|
[AMDGPU] Increase max iterations count to analyze complete unroll
Summary: In some cases inner loops may not get boosts so try to analyze them deeper.
Reviewers: rampitec, mzolotukhin
Reviewed By: rampitec
Subscribers: arsenm, kzhuravl, jvesely, wdng, nhaehnle, yaxunl, dstuttard, tpr, t-tye, hiraditya, zzheng, kerbowa, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D81204
2020-06-05 01:18:18 +02:00
|
|
|
if (UnrollMaxIterationsCountToAnalyze.getNumOccurrences() > 0)
|
|
|
|
UP.MaxIterationsCountToAnalyze = UnrollMaxIterationsCountToAnalyze;
|
2016-01-12 01:55:26 +01:00
|
|
|
|
|
|
|
// Apply user values provided by argument
|
|
|
|
if (UserThreshold.hasValue()) {
|
|
|
|
UP.Threshold = *UserThreshold;
|
|
|
|
UP.PartialThreshold = *UserThreshold;
|
|
|
|
}
|
|
|
|
if (UserCount.hasValue())
|
|
|
|
UP.Count = *UserCount;
|
|
|
|
if (UserAllowPartial.hasValue())
|
|
|
|
UP.Partial = *UserAllowPartial;
|
|
|
|
if (UserRuntime.hasValue())
|
|
|
|
UP.Runtime = *UserRuntime;
|
2016-10-12 23:29:38 +02:00
|
|
|
if (UserUpperBound.hasValue())
|
|
|
|
UP.UpperBound = *UserUpperBound;
|
2019-09-19 08:57:29 +02:00
|
|
|
if (UserFullUnrollMaxCount.hasValue())
|
|
|
|
UP.FullUnrollMaxCount = *UserFullUnrollMaxCount;
|
2016-01-12 01:55:26 +01:00
|
|
|
|
|
|
|
return UP;
|
|
|
|
}
|
|
|
|
|
2016-05-13 23:23:25 +02:00
|
|
|
namespace {
|
2017-10-18 23:46:47 +02:00
|
|
|
|
2016-05-13 23:23:25 +02:00
|
|
|
/// A struct to densely store the state of an instruction after unrolling at
|
|
|
|
/// each iteration.
|
|
|
|
///
|
|
|
|
/// This is designed to work like a tuple of <Instruction *, int> for the
|
|
|
|
/// purposes of hashing and lookup, but to be able to associate two boolean
|
|
|
|
/// states with each key.
|
|
|
|
struct UnrolledInstState {
|
|
|
|
Instruction *I;
|
|
|
|
int Iteration : 30;
|
|
|
|
unsigned IsFree : 1;
|
|
|
|
unsigned IsCounted : 1;
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Hashing and equality testing for a set of the instruction states.
|
|
|
|
struct UnrolledInstStateKeyInfo {
|
2017-10-18 23:46:47 +02:00
|
|
|
using PtrInfo = DenseMapInfo<Instruction *>;
|
|
|
|
using PairInfo = DenseMapInfo<std::pair<Instruction *, int>>;
|
|
|
|
|
2016-05-13 23:23:25 +02:00
|
|
|
static inline UnrolledInstState getEmptyKey() {
|
|
|
|
return {PtrInfo::getEmptyKey(), 0, 0, 0};
|
|
|
|
}
|
2017-10-18 23:46:47 +02:00
|
|
|
|
2016-05-13 23:23:25 +02:00
|
|
|
static inline UnrolledInstState getTombstoneKey() {
|
|
|
|
return {PtrInfo::getTombstoneKey(), 0, 0, 0};
|
|
|
|
}
|
2017-10-18 23:46:47 +02:00
|
|
|
|
2016-05-13 23:23:25 +02:00
|
|
|
static inline unsigned getHashValue(const UnrolledInstState &S) {
|
|
|
|
return PairInfo::getHashValue({S.I, S.Iteration});
|
|
|
|
}
|
2017-10-18 23:46:47 +02:00
|
|
|
|
2016-05-13 23:23:25 +02:00
|
|
|
static inline bool isEqual(const UnrolledInstState &LHS,
|
|
|
|
const UnrolledInstState &RHS) {
|
|
|
|
return PairInfo::isEqual({LHS.I, LHS.Iteration}, {RHS.I, RHS.Iteration});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-05-22 19:41:35 +02:00
|
|
|
struct EstimatedUnrollCost {
|
2018-05-01 17:54:18 +02:00
|
|
|
/// The estimated cost after unrolling.
|
2016-12-02 04:17:07 +01:00
|
|
|
unsigned UnrolledCost;
|
2015-02-13 03:10:56 +01:00
|
|
|
|
2018-05-01 17:54:18 +02:00
|
|
|
/// The estimated dynamic cost of executing the instructions in the
|
[Unroll] Rework the naming and structure of the new unroll heuristics.
The new naming is (to me) much easier to understand. Here is a summary
of the new state of the world:
- '*Threshold' is the threshold for full unrolling. It is measured
against the estimated unrolled cost as computed by getUserCost in TTI
(or CodeMetrics, etc). We will exceed this threshold when unrolling
loops where unrolling exposes a significant degree of simplification
of the logic within the loop.
- '*PercentDynamicCostSavedThreshold' is the percentage of the loop's
estimated dynamic execution cost which needs to be saved by unrolling
to apply a discount to the estimated unrolled cost.
- '*DynamicCostSavingsDiscount' is the discount applied to the estimated
unrolling cost when the dynamic savings are expected to be high.
When actually analyzing the loop, we now produce both an estimated
unrolled cost, and an estimated rolled cost. The rolled cost is notably
a dynamic estimate based on our analysis of the expected execution of
each iteration.
While we're still working to build up the infrastructure for making
these estimates, to me it is much more clear *how* to make them better
when they have reasonably descriptive names. For example, we may want to
apply estimated (from heuristics or profiles) dynamic execution weights
to the *dynamic* cost estimates. If we start doing that, we would also
need to track the static unrolled cost and the dynamic unrolled cost, as
only the latter could reasonably be weighted by profile information.
This patch is sadly not without functionality change for the new unroll
analysis logic. Buried in the heuristic management were several things
that surprised me. For example, we never subtracted the optimized
instruction count off when comparing against the unroll heursistics!
I don't know if this just got lost somewhere along the way or what, but
with the new accounting of things, this is much easier to keep track of
and we use the post-simplification cost estimate to compare to the
thresholds, and use the dynamic cost reduction ratio to select whether
we can exceed the baseline threshold.
The old values of these flags also don't necessarily make sense. My
impression is that none of these thresholds or discounts have been tuned
yet, and so they're just arbitrary placehold numbers. As such, I've not
bothered to adjust for the fact that this is now a discount and not
a tow-tier threshold model. We need to tune all these values once the
logic is ready to be enabled.
Differential Revision: http://reviews.llvm.org/D9966
llvm-svn: 239164
2015-06-05 19:01:43 +02:00
|
|
|
/// rolled form.
|
2016-12-02 04:17:07 +01:00
|
|
|
unsigned RolledDynamicCost;
|
2015-05-22 19:41:35 +02:00
|
|
|
};
|
2017-10-18 23:46:47 +02:00
|
|
|
|
|
|
|
} // end anonymous namespace
|
2015-05-12 19:20:03 +02:00
|
|
|
|
2018-05-01 17:54:18 +02:00
|
|
|
/// Figure out if the loop is worth full unrolling.
|
2015-05-22 19:41:35 +02:00
|
|
|
///
|
|
|
|
/// Complete loop unrolling can make some loads constant, and we need to know
|
|
|
|
/// if that would expose any further optimization opportunities. This routine
|
2015-06-12 00:17:39 +02:00
|
|
|
/// estimates this optimization. It computes cost of unrolled loop
|
|
|
|
/// (UnrolledCost) and dynamic cost of the original loop (RolledDynamicCost). By
|
|
|
|
/// dynamic cost we mean that we won't count costs of blocks that are known not
|
|
|
|
/// to be executed (i.e. if we have a branch in the loop and we know that at the
|
|
|
|
/// given iteration its condition would be resolved to true, we won't add up the
|
|
|
|
/// cost of the 'false'-block).
|
|
|
|
/// \returns Optional value, holding the RolledDynamicCost and UnrolledCost. If
|
|
|
|
/// the analysis failed (no benefits expected from the unrolling, or the loop is
|
|
|
|
/// too big to analyze), the returned value is None.
|
2018-03-15 10:59:15 +01:00
|
|
|
static Optional<EstimatedUnrollCost> analyzeLoopUnrollCost(
|
|
|
|
const Loop *L, unsigned TripCount, DominatorTree &DT, ScalarEvolution &SE,
|
|
|
|
const SmallPtrSetImpl<const Value *> &EphValues,
|
[AMDGPU] Increase max iterations count to analyze complete unroll
Summary: In some cases inner loops may not get boosts so try to analyze them deeper.
Reviewers: rampitec, mzolotukhin
Reviewed By: rampitec
Subscribers: arsenm, kzhuravl, jvesely, wdng, nhaehnle, yaxunl, dstuttard, tpr, t-tye, hiraditya, zzheng, kerbowa, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D81204
2020-06-05 01:18:18 +02:00
|
|
|
const TargetTransformInfo &TTI, unsigned MaxUnrolledLoopSize,
|
|
|
|
unsigned MaxIterationsCountToAnalyze) {
|
2015-05-22 19:41:35 +02:00
|
|
|
// We want to be able to scale offsets by the trip count and add more offsets
|
|
|
|
// to them without checking for overflows, and we already don't want to
|
|
|
|
// analyze *massive* trip counts, so we force the max to be reasonably small.
|
[AMDGPU] Increase max iterations count to analyze complete unroll
Summary: In some cases inner loops may not get boosts so try to analyze them deeper.
Reviewers: rampitec, mzolotukhin
Reviewed By: rampitec
Subscribers: arsenm, kzhuravl, jvesely, wdng, nhaehnle, yaxunl, dstuttard, tpr, t-tye, hiraditya, zzheng, kerbowa, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D81204
2020-06-05 01:18:18 +02:00
|
|
|
assert(MaxIterationsCountToAnalyze <
|
2017-10-19 17:00:31 +02:00
|
|
|
(unsigned)(std::numeric_limits<int>::max() / 2) &&
|
2015-05-22 19:41:35 +02:00
|
|
|
"The unroll iterations max is too large!");
|
|
|
|
|
2016-05-13 23:23:25 +02:00
|
|
|
// Only analyze inner loops. We can't properly estimate cost of nested loops
|
|
|
|
// and we won't visit inner loops again anyway.
|
2020-09-22 22:28:00 +02:00
|
|
|
if (!L->isInnermost())
|
2016-05-13 23:23:25 +02:00
|
|
|
return None;
|
|
|
|
|
2015-05-22 19:41:35 +02:00
|
|
|
// Don't simulate loops with a big or unknown tripcount
|
[AMDGPU] Increase max iterations count to analyze complete unroll
Summary: In some cases inner loops may not get boosts so try to analyze them deeper.
Reviewers: rampitec, mzolotukhin
Reviewed By: rampitec
Subscribers: arsenm, kzhuravl, jvesely, wdng, nhaehnle, yaxunl, dstuttard, tpr, t-tye, hiraditya, zzheng, kerbowa, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D81204
2020-06-05 01:18:18 +02:00
|
|
|
if (!TripCount || TripCount > MaxIterationsCountToAnalyze)
|
2015-05-22 19:41:35 +02:00
|
|
|
return None;
|
|
|
|
|
|
|
|
SmallSetVector<BasicBlock *, 16> BBWorklist;
|
2016-05-13 23:23:25 +02:00
|
|
|
SmallSetVector<std::pair<BasicBlock *, BasicBlock *>, 4> ExitWorklist;
|
2021-05-26 17:40:01 +02:00
|
|
|
DenseMap<Value *, Value *> SimplifiedValues;
|
|
|
|
SmallVector<std::pair<Value *, Value *>, 4> SimplifiedInputValues;
|
2015-02-13 03:17:39 +01:00
|
|
|
|
[Unroll] Rework the naming and structure of the new unroll heuristics.
The new naming is (to me) much easier to understand. Here is a summary
of the new state of the world:
- '*Threshold' is the threshold for full unrolling. It is measured
against the estimated unrolled cost as computed by getUserCost in TTI
(or CodeMetrics, etc). We will exceed this threshold when unrolling
loops where unrolling exposes a significant degree of simplification
of the logic within the loop.
- '*PercentDynamicCostSavedThreshold' is the percentage of the loop's
estimated dynamic execution cost which needs to be saved by unrolling
to apply a discount to the estimated unrolled cost.
- '*DynamicCostSavingsDiscount' is the discount applied to the estimated
unrolling cost when the dynamic savings are expected to be high.
When actually analyzing the loop, we now produce both an estimated
unrolled cost, and an estimated rolled cost. The rolled cost is notably
a dynamic estimate based on our analysis of the expected execution of
each iteration.
While we're still working to build up the infrastructure for making
these estimates, to me it is much more clear *how* to make them better
when they have reasonably descriptive names. For example, we may want to
apply estimated (from heuristics or profiles) dynamic execution weights
to the *dynamic* cost estimates. If we start doing that, we would also
need to track the static unrolled cost and the dynamic unrolled cost, as
only the latter could reasonably be weighted by profile information.
This patch is sadly not without functionality change for the new unroll
analysis logic. Buried in the heuristic management were several things
that surprised me. For example, we never subtracted the optimized
instruction count off when comparing against the unroll heursistics!
I don't know if this just got lost somewhere along the way or what, but
with the new accounting of things, this is much easier to keep track of
and we use the post-simplification cost estimate to compare to the
thresholds, and use the dynamic cost reduction ratio to select whether
we can exceed the baseline threshold.
The old values of these flags also don't necessarily make sense. My
impression is that none of these thresholds or discounts have been tuned
yet, and so they're just arbitrary placehold numbers. As such, I've not
bothered to adjust for the fact that this is now a discount and not
a tow-tier threshold model. We need to tune all these values once the
logic is ready to be enabled.
Differential Revision: http://reviews.llvm.org/D9966
llvm-svn: 239164
2015-06-05 19:01:43 +02:00
|
|
|
// The estimated cost of the unrolled form of the loop. We try to estimate
|
|
|
|
// this by simplifying as much as we can while computing the estimate.
|
2021-02-04 12:32:45 +01:00
|
|
|
InstructionCost UnrolledCost = 0;
|
2016-05-13 23:23:25 +02:00
|
|
|
|
[Unroll] Rework the naming and structure of the new unroll heuristics.
The new naming is (to me) much easier to understand. Here is a summary
of the new state of the world:
- '*Threshold' is the threshold for full unrolling. It is measured
against the estimated unrolled cost as computed by getUserCost in TTI
(or CodeMetrics, etc). We will exceed this threshold when unrolling
loops where unrolling exposes a significant degree of simplification
of the logic within the loop.
- '*PercentDynamicCostSavedThreshold' is the percentage of the loop's
estimated dynamic execution cost which needs to be saved by unrolling
to apply a discount to the estimated unrolled cost.
- '*DynamicCostSavingsDiscount' is the discount applied to the estimated
unrolling cost when the dynamic savings are expected to be high.
When actually analyzing the loop, we now produce both an estimated
unrolled cost, and an estimated rolled cost. The rolled cost is notably
a dynamic estimate based on our analysis of the expected execution of
each iteration.
While we're still working to build up the infrastructure for making
these estimates, to me it is much more clear *how* to make them better
when they have reasonably descriptive names. For example, we may want to
apply estimated (from heuristics or profiles) dynamic execution weights
to the *dynamic* cost estimates. If we start doing that, we would also
need to track the static unrolled cost and the dynamic unrolled cost, as
only the latter could reasonably be weighted by profile information.
This patch is sadly not without functionality change for the new unroll
analysis logic. Buried in the heuristic management were several things
that surprised me. For example, we never subtracted the optimized
instruction count off when comparing against the unroll heursistics!
I don't know if this just got lost somewhere along the way or what, but
with the new accounting of things, this is much easier to keep track of
and we use the post-simplification cost estimate to compare to the
thresholds, and use the dynamic cost reduction ratio to select whether
we can exceed the baseline threshold.
The old values of these flags also don't necessarily make sense. My
impression is that none of these thresholds or discounts have been tuned
yet, and so they're just arbitrary placehold numbers. As such, I've not
bothered to adjust for the fact that this is now a discount and not
a tow-tier threshold model. We need to tune all these values once the
logic is ready to be enabled.
Differential Revision: http://reviews.llvm.org/D9966
llvm-svn: 239164
2015-06-05 19:01:43 +02:00
|
|
|
// We also track the estimated dynamic (that is, actually executed) cost in
|
|
|
|
// the rolled form. This helps identify cases when the savings from unrolling
|
|
|
|
// aren't just exposing dead control flows, but actual reduced dynamic
|
|
|
|
// instructions due to the simplifications which we expect to occur after
|
|
|
|
// unrolling.
|
2021-02-04 12:32:45 +01:00
|
|
|
InstructionCost RolledDynamicCost = 0;
|
2015-05-22 19:41:35 +02:00
|
|
|
|
2016-05-13 23:23:25 +02:00
|
|
|
// We track the simplification of each instruction in each iteration. We use
|
|
|
|
// this to recursively merge costs into the unrolled cost on-demand so that
|
|
|
|
// we don't count the cost of any dead code. This is essentially a map from
|
|
|
|
// <instruction, int> to <bool, bool>, but stored as a densely packed struct.
|
|
|
|
DenseSet<UnrolledInstState, UnrolledInstStateKeyInfo> InstCostMap;
|
|
|
|
|
|
|
|
// A small worklist used to accumulate cost of instructions from each
|
|
|
|
// observable and reached root in the loop.
|
|
|
|
SmallVector<Instruction *, 16> CostWorklist;
|
|
|
|
|
|
|
|
// PHI-used worklist used between iterations while accumulating cost.
|
|
|
|
SmallVector<Instruction *, 4> PHIUsedList;
|
|
|
|
|
|
|
|
// Helper function to accumulate cost for instructions in the loop.
|
|
|
|
auto AddCostRecursively = [&](Instruction &RootI, int Iteration) {
|
|
|
|
assert(Iteration >= 0 && "Cannot have a negative iteration!");
|
|
|
|
assert(CostWorklist.empty() && "Must start with an empty cost list");
|
|
|
|
assert(PHIUsedList.empty() && "Must start with an empty phi used list");
|
|
|
|
CostWorklist.push_back(&RootI);
|
2020-08-11 15:19:35 +02:00
|
|
|
TargetTransformInfo::TargetCostKind CostKind =
|
|
|
|
RootI.getFunction()->hasMinSize() ?
|
|
|
|
TargetTransformInfo::TCK_CodeSize :
|
|
|
|
TargetTransformInfo::TCK_SizeAndLatency;
|
2016-05-13 23:23:25 +02:00
|
|
|
for (;; --Iteration) {
|
|
|
|
do {
|
|
|
|
Instruction *I = CostWorklist.pop_back_val();
|
|
|
|
|
|
|
|
// InstCostMap only uses I and Iteration as a key, the other two values
|
|
|
|
// don't matter here.
|
|
|
|
auto CostIter = InstCostMap.find({I, Iteration, 0, 0});
|
|
|
|
if (CostIter == InstCostMap.end())
|
|
|
|
// If an input to a PHI node comes from a dead path through the loop
|
|
|
|
// we may have no cost data for it here. What that actually means is
|
|
|
|
// that it is free.
|
|
|
|
continue;
|
|
|
|
auto &Cost = *CostIter;
|
|
|
|
if (Cost.IsCounted)
|
|
|
|
// Already counted this instruction.
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Mark that we are counting the cost of this instruction now.
|
|
|
|
Cost.IsCounted = true;
|
|
|
|
|
|
|
|
// If this is a PHI node in the loop header, just add it to the PHI set.
|
|
|
|
if (auto *PhiI = dyn_cast<PHINode>(I))
|
|
|
|
if (PhiI->getParent() == L->getHeader()) {
|
|
|
|
assert(Cost.IsFree && "Loop PHIs shouldn't be evaluated as they "
|
|
|
|
"inherently simplify during unrolling.");
|
|
|
|
if (Iteration == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Push the incoming value from the backedge into the PHI used list
|
|
|
|
// if it is an in-loop instruction. We'll use this to populate the
|
|
|
|
// cost worklist for the next iteration (as we count backwards).
|
|
|
|
if (auto *OpI = dyn_cast<Instruction>(
|
|
|
|
PhiI->getIncomingValueForBlock(L->getLoopLatch())))
|
|
|
|
if (L->contains(OpI))
|
|
|
|
PHIUsedList.push_back(OpI);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// First accumulate the cost of this instruction.
|
|
|
|
if (!Cost.IsFree) {
|
2020-08-11 15:19:35 +02:00
|
|
|
UnrolledCost += TTI.getUserCost(I, CostKind);
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << "Adding cost of instruction (iteration "
|
|
|
|
<< Iteration << "): ");
|
|
|
|
LLVM_DEBUG(I->dump());
|
2016-05-13 23:23:25 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// We must count the cost of every operand which is not free,
|
|
|
|
// recursively. If we reach a loop PHI node, simply add it to the set
|
|
|
|
// to be considered on the next iteration (backwards!).
|
|
|
|
for (Value *Op : I->operands()) {
|
|
|
|
// Check whether this operand is free due to being a constant or
|
|
|
|
// outside the loop.
|
|
|
|
auto *OpI = dyn_cast<Instruction>(Op);
|
|
|
|
if (!OpI || !L->contains(OpI))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Otherwise accumulate its cost.
|
|
|
|
CostWorklist.push_back(OpI);
|
|
|
|
}
|
|
|
|
} while (!CostWorklist.empty());
|
|
|
|
|
|
|
|
if (PHIUsedList.empty())
|
|
|
|
// We've exhausted the search.
|
|
|
|
break;
|
|
|
|
|
|
|
|
assert(Iteration > 0 &&
|
|
|
|
"Cannot track PHI-used values past the first iteration!");
|
|
|
|
CostWorklist.append(PHIUsedList.begin(), PHIUsedList.end());
|
|
|
|
PHIUsedList.clear();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2015-08-03 22:32:27 +02:00
|
|
|
// Ensure that we don't violate the loop structure invariants relied on by
|
|
|
|
// this analysis.
|
|
|
|
assert(L->isLoopSimplifyForm() && "Must put loop into normal form first.");
|
|
|
|
assert(L->isLCSSAForm(DT) &&
|
|
|
|
"Must have loops in LCSSA form to track live-out values.");
|
|
|
|
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << "Starting LoopUnroll profitability analysis...\n");
|
2015-07-28 22:07:29 +02:00
|
|
|
|
2020-08-11 15:19:35 +02:00
|
|
|
TargetTransformInfo::TargetCostKind CostKind =
|
|
|
|
L->getHeader()->getParent()->hasMinSize() ?
|
|
|
|
TargetTransformInfo::TCK_CodeSize : TargetTransformInfo::TCK_SizeAndLatency;
|
2015-05-22 19:41:35 +02:00
|
|
|
// Simulate execution of each iteration of the loop counting instructions,
|
|
|
|
// which would be simplified.
|
|
|
|
// Since the same load will take different values on different iterations,
|
|
|
|
// we literally have to go through all loop's iterations.
|
|
|
|
for (unsigned Iteration = 0; Iteration < TripCount; ++Iteration) {
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << " Analyzing iteration " << Iteration << "\n");
|
2015-08-03 22:32:27 +02:00
|
|
|
|
|
|
|
// Prepare for the iteration by collecting any simplified entry or backedge
|
|
|
|
// inputs.
|
2021-01-07 10:43:33 +01:00
|
|
|
for (Instruction &I : *L->getHeader()) {
|
|
|
|
auto *PHI = dyn_cast<PHINode>(&I);
|
|
|
|
if (!PHI)
|
|
|
|
break;
|
|
|
|
|
2015-08-03 22:32:27 +02:00
|
|
|
// The loop header PHI nodes must have exactly two input: one from the
|
|
|
|
// loop preheader and one from the loop latch.
|
|
|
|
assert(
|
2021-01-07 10:43:33 +01:00
|
|
|
PHI->getNumIncomingValues() == 2 &&
|
2015-08-03 22:32:27 +02:00
|
|
|
"Must have an incoming value only for the preheader and the latch.");
|
|
|
|
|
2021-01-07 10:43:33 +01:00
|
|
|
Value *V = PHI->getIncomingValueForBlock(
|
2015-08-03 22:32:27 +02:00
|
|
|
Iteration == 0 ? L->getLoopPreheader() : L->getLoopLatch());
|
2021-05-26 17:40:01 +02:00
|
|
|
if (Iteration != 0 && SimplifiedValues.count(V))
|
|
|
|
V = SimplifiedValues.lookup(V);
|
|
|
|
SimplifiedInputValues.push_back({PHI, V});
|
2015-08-03 22:32:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Now clear and re-populate the map for the next iteration.
|
2015-05-22 19:41:35 +02:00
|
|
|
SimplifiedValues.clear();
|
2015-08-03 22:32:27 +02:00
|
|
|
while (!SimplifiedInputValues.empty())
|
|
|
|
SimplifiedValues.insert(SimplifiedInputValues.pop_back_val());
|
|
|
|
|
2016-02-26 03:57:05 +01:00
|
|
|
UnrolledInstAnalyzer Analyzer(Iteration, SimplifiedValues, SE, L);
|
2015-05-22 19:41:35 +02:00
|
|
|
|
|
|
|
BBWorklist.clear();
|
|
|
|
BBWorklist.insert(L->getHeader());
|
|
|
|
// Note that we *must not* cache the size, this loop grows the worklist.
|
|
|
|
for (unsigned Idx = 0; Idx != BBWorklist.size(); ++Idx) {
|
|
|
|
BasicBlock *BB = BBWorklist[Idx];
|
|
|
|
|
|
|
|
// Visit all instructions in the given basic block and try to simplify
|
|
|
|
// it. We don't change the actual IR, just count optimization
|
|
|
|
// opportunities.
|
|
|
|
for (Instruction &I : *BB) {
|
2018-03-15 10:59:15 +01:00
|
|
|
// These won't get into the final code - don't even try calculating the
|
|
|
|
// cost for them.
|
|
|
|
if (isa<DbgInfoIntrinsic>(I) || EphValues.count(&I))
|
2016-09-30 20:30:04 +02:00
|
|
|
continue;
|
|
|
|
|
2016-05-13 23:23:25 +02:00
|
|
|
// Track this instruction's expected baseline cost when executing the
|
|
|
|
// rolled loop form.
|
2020-08-11 15:19:35 +02:00
|
|
|
RolledDynamicCost += TTI.getUserCost(&I, CostKind);
|
2015-05-22 19:41:35 +02:00
|
|
|
|
|
|
|
// Visit the instruction to analyze its loop cost after unrolling,
|
2016-05-13 23:23:25 +02:00
|
|
|
// and if the visitor returns true, mark the instruction as free after
|
|
|
|
// unrolling and continue.
|
|
|
|
bool IsFree = Analyzer.visit(I);
|
|
|
|
bool Inserted = InstCostMap.insert({&I, (int)Iteration,
|
|
|
|
(unsigned)IsFree,
|
|
|
|
/*IsCounted*/ false}).second;
|
|
|
|
(void)Inserted;
|
|
|
|
assert(Inserted && "Cannot have a state for an unvisited instruction!");
|
|
|
|
|
|
|
|
if (IsFree)
|
|
|
|
continue;
|
[Unroll] Rework the naming and structure of the new unroll heuristics.
The new naming is (to me) much easier to understand. Here is a summary
of the new state of the world:
- '*Threshold' is the threshold for full unrolling. It is measured
against the estimated unrolled cost as computed by getUserCost in TTI
(or CodeMetrics, etc). We will exceed this threshold when unrolling
loops where unrolling exposes a significant degree of simplification
of the logic within the loop.
- '*PercentDynamicCostSavedThreshold' is the percentage of the loop's
estimated dynamic execution cost which needs to be saved by unrolling
to apply a discount to the estimated unrolled cost.
- '*DynamicCostSavingsDiscount' is the discount applied to the estimated
unrolling cost when the dynamic savings are expected to be high.
When actually analyzing the loop, we now produce both an estimated
unrolled cost, and an estimated rolled cost. The rolled cost is notably
a dynamic estimate based on our analysis of the expected execution of
each iteration.
While we're still working to build up the infrastructure for making
these estimates, to me it is much more clear *how* to make them better
when they have reasonably descriptive names. For example, we may want to
apply estimated (from heuristics or profiles) dynamic execution weights
to the *dynamic* cost estimates. If we start doing that, we would also
need to track the static unrolled cost and the dynamic unrolled cost, as
only the latter could reasonably be weighted by profile information.
This patch is sadly not without functionality change for the new unroll
analysis logic. Buried in the heuristic management were several things
that surprised me. For example, we never subtracted the optimized
instruction count off when comparing against the unroll heursistics!
I don't know if this just got lost somewhere along the way or what, but
with the new accounting of things, this is much easier to keep track of
and we use the post-simplification cost estimate to compare to the
thresholds, and use the dynamic cost reduction ratio to select whether
we can exceed the baseline threshold.
The old values of these flags also don't necessarily make sense. My
impression is that none of these thresholds or discounts have been tuned
yet, and so they're just arbitrary placehold numbers. As such, I've not
bothered to adjust for the fact that this is now a discount and not
a tow-tier threshold model. We need to tune all these values once the
logic is ready to be enabled.
Differential Revision: http://reviews.llvm.org/D9966
llvm-svn: 239164
2015-06-05 19:01:43 +02:00
|
|
|
|
2016-05-13 23:23:25 +02:00
|
|
|
// Can't properly model a cost of a call.
|
|
|
|
// FIXME: With a proper cost model we should be able to do it.
|
2018-06-26 20:51:17 +02:00
|
|
|
if (auto *CI = dyn_cast<CallInst>(&I)) {
|
|
|
|
const Function *Callee = CI->getCalledFunction();
|
|
|
|
if (!Callee || TTI.isLoweredToCall(Callee)) {
|
|
|
|
LLVM_DEBUG(dbgs() << "Can't analyze cost of loop with call\n");
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
}
|
2015-05-22 19:41:35 +02:00
|
|
|
|
2016-08-18 00:42:58 +02:00
|
|
|
// If the instruction might have a side-effect recursively account for
|
|
|
|
// the cost of it and all the instructions leading up to it.
|
|
|
|
if (I.mayHaveSideEffects())
|
|
|
|
AddCostRecursively(I, Iteration);
|
|
|
|
|
2015-05-22 19:41:35 +02:00
|
|
|
// If unrolled body turns out to be too big, bail out.
|
2015-07-28 22:07:29 +02:00
|
|
|
if (UnrolledCost > MaxUnrolledLoopSize) {
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << " Exceeded threshold.. exiting.\n"
|
|
|
|
<< " UnrolledCost: " << UnrolledCost
|
|
|
|
<< ", MaxUnrolledLoopSize: " << MaxUnrolledLoopSize
|
|
|
|
<< "\n");
|
2015-05-22 19:41:35 +02:00
|
|
|
return None;
|
2015-07-28 22:07:29 +02:00
|
|
|
}
|
[unroll] Make the unroll cost analysis terminate deterministically and
reasonably quickly.
I don't have a reduced test case, but for a version of FFMPEG, this
makes the loop unroller start finishing at all (after over 15 minutes of
running, it hadn't terminated for me, no idea if it was a true infloop
or just exponential work).
The key thing here is to check the DeadInstructions set when pulling
things off the worklist. Without this, we would re-walk the user list of
already dead instructions again and again and again. Consider phi nodes
with many, many operands and other patterns.
The other important aspect of this is that because we would keep
re-visiting instructions that were already known dead, we kept adding
their cost savings to this! This would cause our cost savings to be
*insanely* inflated from this.
While I was here, I also rotated the operand walk out of the worklist
loop to make the code easier to read. There is still work to be done to
minimize worklist traffic because we don't de-duplicate operands. This
means we may add the same instruction onto the worklist 1000s of times
if it shows up in 1000s of operansd to a PHI node for example.
Still, with this patch, the ffmpeg testcase I have finishes quickly and
I can't measure the runtime impact of the unroll analysis any more. I'll
probably try to do a few more cleanups to this code, but not sure how
much cleanup I can justify right now.
llvm-svn: 229038
2015-02-13 04:40:58 +01:00
|
|
|
}
|
2015-05-12 19:20:03 +02:00
|
|
|
|
2018-10-15 12:04:59 +02:00
|
|
|
Instruction *TI = BB->getTerminator();
|
2015-07-24 03:53:04 +02:00
|
|
|
|
2021-05-26 17:40:01 +02:00
|
|
|
auto getSimplifiedConstant = [&](Value *V) -> Constant * {
|
|
|
|
if (SimplifiedValues.count(V))
|
|
|
|
V = SimplifiedValues.lookup(V);
|
|
|
|
return dyn_cast<Constant>(V);
|
|
|
|
};
|
|
|
|
|
2015-07-24 03:53:04 +02:00
|
|
|
// Add in the live successors by first checking whether we have terminator
|
|
|
|
// that may be simplified based on the values simplified by this call.
|
2016-05-26 23:42:51 +02:00
|
|
|
BasicBlock *KnownSucc = nullptr;
|
2015-07-24 03:53:04 +02:00
|
|
|
if (BranchInst *BI = dyn_cast<BranchInst>(TI)) {
|
|
|
|
if (BI->isConditional()) {
|
2021-05-26 17:40:01 +02:00
|
|
|
if (auto *SimpleCond = getSimplifiedConstant(BI->getCondition())) {
|
2015-07-29 20:10:29 +02:00
|
|
|
// Just take the first successor if condition is undef
|
|
|
|
if (isa<UndefValue>(SimpleCond))
|
2016-05-26 23:42:51 +02:00
|
|
|
KnownSucc = BI->getSuccessor(0);
|
|
|
|
else if (ConstantInt *SimpleCondVal =
|
|
|
|
dyn_cast<ConstantInt>(SimpleCond))
|
|
|
|
KnownSucc = BI->getSuccessor(SimpleCondVal->isZero() ? 1 : 0);
|
2015-07-24 03:53:04 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (SwitchInst *SI = dyn_cast<SwitchInst>(TI)) {
|
2021-05-26 17:40:01 +02:00
|
|
|
if (auto *SimpleCond = getSimplifiedConstant(SI->getCondition())) {
|
2015-07-29 20:10:29 +02:00
|
|
|
// Just take the first successor if condition is undef
|
|
|
|
if (isa<UndefValue>(SimpleCond))
|
2016-05-26 23:42:51 +02:00
|
|
|
KnownSucc = SI->getSuccessor(0);
|
|
|
|
else if (ConstantInt *SimpleCondVal =
|
|
|
|
dyn_cast<ConstantInt>(SimpleCond))
|
2017-04-12 09:27:28 +02:00
|
|
|
KnownSucc = SI->findCaseValue(SimpleCondVal)->getCaseSuccessor();
|
2015-07-24 03:53:04 +02:00
|
|
|
}
|
|
|
|
}
|
2016-05-26 23:42:51 +02:00
|
|
|
if (KnownSucc) {
|
|
|
|
if (L->contains(KnownSucc))
|
|
|
|
BBWorklist.insert(KnownSucc);
|
|
|
|
else
|
|
|
|
ExitWorklist.insert({BB, KnownSucc});
|
|
|
|
continue;
|
|
|
|
}
|
2015-07-24 03:53:04 +02:00
|
|
|
|
2015-05-22 19:41:35 +02:00
|
|
|
// Add BB's successors to the worklist.
|
|
|
|
for (BasicBlock *Succ : successors(BB))
|
|
|
|
if (L->contains(Succ))
|
|
|
|
BBWorklist.insert(Succ);
|
2016-05-13 23:23:25 +02:00
|
|
|
else
|
|
|
|
ExitWorklist.insert({BB, Succ});
|
2016-05-18 23:20:12 +02:00
|
|
|
AddCostRecursively(*TI, Iteration);
|
2015-02-05 03:34:00 +01:00
|
|
|
}
|
2015-05-22 19:41:35 +02:00
|
|
|
|
|
|
|
// If we found no optimization opportunities on the first iteration, we
|
|
|
|
// won't find them on later ones too.
|
2015-07-28 22:07:29 +02:00
|
|
|
if (UnrolledCost == RolledDynamicCost) {
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << " No opportunities found.. exiting.\n"
|
|
|
|
<< " UnrolledCost: " << UnrolledCost << "\n");
|
2015-05-22 19:41:35 +02:00
|
|
|
return None;
|
2015-07-28 22:07:29 +02:00
|
|
|
}
|
2015-02-05 03:34:00 +01:00
|
|
|
}
|
2016-05-13 23:23:25 +02:00
|
|
|
|
|
|
|
while (!ExitWorklist.empty()) {
|
|
|
|
BasicBlock *ExitingBB, *ExitBB;
|
|
|
|
std::tie(ExitingBB, ExitBB) = ExitWorklist.pop_back_val();
|
|
|
|
|
2021-01-07 10:43:33 +01:00
|
|
|
for (Instruction &I : *ExitBB) {
|
|
|
|
auto *PN = dyn_cast<PHINode>(&I);
|
|
|
|
if (!PN)
|
|
|
|
break;
|
|
|
|
|
|
|
|
Value *Op = PN->getIncomingValueForBlock(ExitingBB);
|
2016-05-13 23:23:25 +02:00
|
|
|
if (auto *OpI = dyn_cast<Instruction>(Op))
|
|
|
|
if (L->contains(OpI))
|
|
|
|
AddCostRecursively(*OpI, TripCount - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-04 12:32:45 +01:00
|
|
|
assert(UnrolledCost.isValid() && RolledDynamicCost.isValid() &&
|
|
|
|
"All instructions must have a valid cost, whether the "
|
|
|
|
"loop is rolled or unrolled.");
|
|
|
|
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << "Analysis finished:\n"
|
|
|
|
<< "UnrolledCost: " << UnrolledCost << ", "
|
|
|
|
<< "RolledDynamicCost: " << RolledDynamicCost << "\n");
|
2021-02-04 12:32:45 +01:00
|
|
|
return {{unsigned(*UnrolledCost.getValue()),
|
|
|
|
unsigned(*RolledDynamicCost.getValue())}};
|
2015-05-22 19:41:35 +02:00
|
|
|
}
|
2015-02-05 03:34:00 +01:00
|
|
|
|
2007-05-08 17:14:19 +02:00
|
|
|
/// ApproximateLoopSize - Approximate the size of the loop.
|
2018-07-01 14:47:30 +02:00
|
|
|
unsigned llvm::ApproximateLoopSize(
|
|
|
|
const Loop *L, unsigned &NumCalls, bool &NotDuplicatable, bool &Convergent,
|
|
|
|
const TargetTransformInfo &TTI,
|
|
|
|
const SmallPtrSetImpl<const Value *> &EphValues, unsigned BEInsns) {
|
2009-10-31 15:54:17 +01:00
|
|
|
CodeMetrics Metrics;
|
2016-03-08 20:06:12 +01:00
|
|
|
for (BasicBlock *BB : L->blocks())
|
|
|
|
Metrics.analyzeBasicBlock(BB, TTI, EphValues);
|
2010-09-09 22:32:23 +02:00
|
|
|
NumCalls = Metrics.NumInlineCandidates;
|
2012-12-20 17:04:27 +01:00
|
|
|
NotDuplicatable = Metrics.notDuplicatable;
|
2016-03-15 00:15:34 +01:00
|
|
|
Convergent = Metrics.convergent;
|
2011-07-23 02:29:16 +02:00
|
|
|
|
2010-09-09 21:07:31 +02:00
|
|
|
unsigned LoopSize = Metrics.NumInsts;
|
2011-07-23 02:29:16 +02:00
|
|
|
|
2010-09-09 21:07:31 +02:00
|
|
|
// Don't allow an estimate of size zero. This would allows unrolling of loops
|
|
|
|
// with huge iteration counts, which is a compile time problem even if it's
|
2015-01-10 01:30:55 +01:00
|
|
|
// not a problem for code quality. Also, the code using this size may assume
|
|
|
|
// that each loop has at least three instructions (likely a conditional
|
|
|
|
// branch, a comparison feeding that branch, and some kind of loop increment
|
|
|
|
// feeding that comparison instruction).
|
2016-11-09 20:56:39 +01:00
|
|
|
LoopSize = std::max(LoopSize, BEInsns + 1);
|
2011-07-23 02:29:16 +02:00
|
|
|
|
2010-09-09 21:07:31 +02:00
|
|
|
return LoopSize;
|
2004-04-18 07:20:17 +02:00
|
|
|
}
|
|
|
|
|
2014-07-23 19:31:37 +02:00
|
|
|
// Returns the loop hint metadata node with the given name (for example,
|
|
|
|
// "llvm.loop.unroll.count"). If no such metadata node exists, then nullptr is
|
|
|
|
// returned.
|
2020-02-05 01:29:04 +01:00
|
|
|
static MDNode *getUnrollMetadataForLoop(const Loop *L, StringRef Name) {
|
2015-02-02 21:41:11 +01:00
|
|
|
if (MDNode *LoopID = L->getLoopID())
|
|
|
|
return GetUnrollMetadata(LoopID, Name);
|
|
|
|
return nullptr;
|
2014-06-17 01:53:02 +02:00
|
|
|
}
|
|
|
|
|
2014-07-23 19:31:37 +02:00
|
|
|
// Returns true if the loop has an unroll(full) pragma.
|
2020-02-05 01:29:04 +01:00
|
|
|
static bool hasUnrollFullPragma(const Loop *L) {
|
|
|
|
return getUnrollMetadataForLoop(L, "llvm.loop.unroll.full");
|
2014-06-17 01:53:02 +02:00
|
|
|
}
|
|
|
|
|
2015-08-10 19:28:08 +02:00
|
|
|
// Returns true if the loop has an unroll(enable) pragma. This metadata is used
|
|
|
|
// for both "#pragma unroll" and "#pragma clang loop unroll(enable)" directives.
|
2020-02-05 01:29:04 +01:00
|
|
|
static bool hasUnrollEnablePragma(const Loop *L) {
|
|
|
|
return getUnrollMetadataForLoop(L, "llvm.loop.unroll.enable");
|
2015-08-10 19:28:08 +02:00
|
|
|
}
|
|
|
|
|
2015-03-09 07:14:18 +01:00
|
|
|
// Returns true if the loop has an runtime unroll(disable) pragma.
|
2020-02-05 01:29:04 +01:00
|
|
|
static bool hasRuntimeUnrollDisablePragma(const Loop *L) {
|
|
|
|
return getUnrollMetadataForLoop(L, "llvm.loop.unroll.runtime.disable");
|
2015-03-09 07:14:18 +01:00
|
|
|
}
|
|
|
|
|
2014-06-17 01:53:02 +02:00
|
|
|
// If loop has an unroll_count pragma return the (necessarily
|
|
|
|
// positive) value from the pragma. Otherwise return 0.
|
2020-02-05 01:29:04 +01:00
|
|
|
static unsigned unrollCountPragmaValue(const Loop *L) {
|
|
|
|
MDNode *MD = getUnrollMetadataForLoop(L, "llvm.loop.unroll.count");
|
2014-07-23 19:31:37 +02:00
|
|
|
if (MD) {
|
|
|
|
assert(MD->getNumOperands() == 2 &&
|
|
|
|
"Unroll count hint metadata should have two operands.");
|
IR: Split Metadata from Value
Split `Metadata` away from the `Value` class hierarchy, as part of
PR21532. Assembly and bitcode changes are in the wings, but this is the
bulk of the change for the IR C++ API.
I have a follow-up patch prepared for `clang`. If this breaks other
sub-projects, I apologize in advance :(. Help me compile it on Darwin
I'll try to fix it. FWIW, the errors should be easy to fix, so it may
be simpler to just fix it yourself.
This breaks the build for all metadata-related code that's out-of-tree.
Rest assured the transition is mechanical and the compiler should catch
almost all of the problems.
Here's a quick guide for updating your code:
- `Metadata` is the root of a class hierarchy with three main classes:
`MDNode`, `MDString`, and `ValueAsMetadata`. It is distinct from
the `Value` class hierarchy. It is typeless -- i.e., instances do
*not* have a `Type`.
- `MDNode`'s operands are all `Metadata *` (instead of `Value *`).
- `TrackingVH<MDNode>` and `WeakVH` referring to metadata can be
replaced with `TrackingMDNodeRef` and `TrackingMDRef`, respectively.
If you're referring solely to resolved `MDNode`s -- post graph
construction -- just use `MDNode*`.
- `MDNode` (and the rest of `Metadata`) have only limited support for
`replaceAllUsesWith()`.
As long as an `MDNode` is pointing at a forward declaration -- the
result of `MDNode::getTemporary()` -- it maintains a side map of its
uses and can RAUW itself. Once the forward declarations are fully
resolved RAUW support is dropped on the ground. This means that
uniquing collisions on changing operands cause nodes to become
"distinct". (This already happened fairly commonly, whenever an
operand went to null.)
If you're constructing complex (non self-reference) `MDNode` cycles,
you need to call `MDNode::resolveCycles()` on each node (or on a
top-level node that somehow references all of the nodes). Also,
don't do that. Metadata cycles (and the RAUW machinery needed to
construct them) are expensive.
- An `MDNode` can only refer to a `Constant` through a bridge called
`ConstantAsMetadata` (one of the subclasses of `ValueAsMetadata`).
As a side effect, accessing an operand of an `MDNode` that is known
to be, e.g., `ConstantInt`, takes three steps: first, cast from
`Metadata` to `ConstantAsMetadata`; second, extract the `Constant`;
third, cast down to `ConstantInt`.
The eventual goal is to introduce `MDInt`/`MDFloat`/etc. and have
metadata schema owners transition away from using `Constant`s when
the type isn't important (and they don't care about referring to
`GlobalValue`s).
In the meantime, I've added transitional API to the `mdconst`
namespace that matches semantics with the old code, in order to
avoid adding the error-prone three-step equivalent to every call
site. If your old code was:
MDNode *N = foo();
bar(isa <ConstantInt>(N->getOperand(0)));
baz(cast <ConstantInt>(N->getOperand(1)));
bak(cast_or_null <ConstantInt>(N->getOperand(2)));
bat(dyn_cast <ConstantInt>(N->getOperand(3)));
bay(dyn_cast_or_null<ConstantInt>(N->getOperand(4)));
you can trivially match its semantics with:
MDNode *N = foo();
bar(mdconst::hasa <ConstantInt>(N->getOperand(0)));
baz(mdconst::extract <ConstantInt>(N->getOperand(1)));
bak(mdconst::extract_or_null <ConstantInt>(N->getOperand(2)));
bat(mdconst::dyn_extract <ConstantInt>(N->getOperand(3)));
bay(mdconst::dyn_extract_or_null<ConstantInt>(N->getOperand(4)));
and when you transition your metadata schema to `MDInt`:
MDNode *N = foo();
bar(isa <MDInt>(N->getOperand(0)));
baz(cast <MDInt>(N->getOperand(1)));
bak(cast_or_null <MDInt>(N->getOperand(2)));
bat(dyn_cast <MDInt>(N->getOperand(3)));
bay(dyn_cast_or_null<MDInt>(N->getOperand(4)));
- A `CallInst` -- specifically, intrinsic instructions -- can refer to
metadata through a bridge called `MetadataAsValue`. This is a
subclass of `Value` where `getType()->isMetadataTy()`.
`MetadataAsValue` is the *only* class that can legally refer to a
`LocalAsMetadata`, which is a bridged form of non-`Constant` values
like `Argument` and `Instruction`. It can also refer to any other
`Metadata` subclass.
(I'll break all your testcases in a follow-up commit, when I propagate
this change to assembly.)
llvm-svn: 223802
2014-12-09 19:38:53 +01:00
|
|
|
unsigned Count =
|
|
|
|
mdconst::extract<ConstantInt>(MD->getOperand(1))->getZExtValue();
|
2014-06-17 01:53:02 +02:00
|
|
|
assert(Count >= 1 && "Unroll count must be positive.");
|
|
|
|
return Count;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-12-30 01:50:28 +01:00
|
|
|
// Computes the boosting factor for complete unrolling.
|
|
|
|
// If fully unrolling the loop would save a lot of RolledDynamicCost, it would
|
|
|
|
// be beneficial to fully unroll the loop even if unrolledcost is large. We
|
|
|
|
// use (RolledDynamicCost / UnrolledCost) to model the unroll benefits to adjust
|
|
|
|
// the unroll threshold.
|
|
|
|
static unsigned getFullUnrollBoostingFactor(const EstimatedUnrollCost &Cost,
|
|
|
|
unsigned MaxPercentThresholdBoost) {
|
2017-10-18 23:46:47 +02:00
|
|
|
if (Cost.RolledDynamicCost >= std::numeric_limits<unsigned>::max() / 100)
|
2016-12-30 01:50:28 +01:00
|
|
|
return 100;
|
|
|
|
else if (Cost.UnrolledCost != 0)
|
|
|
|
// The boosting factor is RolledDynamicCost / UnrolledCost
|
|
|
|
return std::min(100 * Cost.RolledDynamicCost / Cost.UnrolledCost,
|
|
|
|
MaxPercentThresholdBoost);
|
|
|
|
else
|
|
|
|
return MaxPercentThresholdBoost;
|
2015-05-12 19:20:03 +02:00
|
|
|
}
|
|
|
|
|
2021-05-15 03:40:23 +02:00
|
|
|
// Produce an estimate of the unrolled cost of the specified loop. This
|
|
|
|
// is used to a) produce a cost estimate for partial unrolling and b) to
|
|
|
|
// cheaply estimate cost for full unrolling when we don't want to symbolically
|
|
|
|
// evaluate all iterations.
|
|
|
|
class UnrollCostEstimator {
|
|
|
|
const unsigned LoopSize;
|
|
|
|
|
|
|
|
public:
|
2021-05-15 05:58:12 +02:00
|
|
|
UnrollCostEstimator(Loop &L, unsigned LoopSize) : LoopSize(LoopSize) {}
|
2021-05-15 03:40:23 +02:00
|
|
|
|
|
|
|
// Returns loop size estimation for unrolled loop, given the unrolling
|
|
|
|
// configuration specified by UP.
|
|
|
|
uint64_t getUnrolledLoopSize(TargetTransformInfo::UnrollingPreferences &UP) {
|
|
|
|
assert(LoopSize >= UP.BEInsns &&
|
|
|
|
"LoopSize should not be less than BEInsns!");
|
|
|
|
return (uint64_t)(LoopSize - UP.BEInsns) * UP.Count + UP.BEInsns;
|
|
|
|
}
|
|
|
|
};
|
2016-11-09 20:56:39 +01:00
|
|
|
|
2016-05-28 01:15:06 +02:00
|
|
|
// Returns true if unroll count was set explicitly.
|
|
|
|
// Calculates unroll count and writes it to UP.Count.
|
[Unroll/UnrollAndJam/Vectorizer/Distribute] Add followup loop attributes.
When multiple loop transformation are defined in a loop's metadata, their order of execution is defined by the order of their respective passes in the pass pipeline. For instance, e.g.
#pragma clang loop unroll_and_jam(enable)
#pragma clang loop distribute(enable)
is the same as
#pragma clang loop distribute(enable)
#pragma clang loop unroll_and_jam(enable)
and will try to loop-distribute before Unroll-And-Jam because the LoopDistribute pass is scheduled after UnrollAndJam pass. UnrollAndJamPass only supports one inner loop, i.e. it will necessarily fail after loop distribution. It is not possible to specify another execution order. Also,t the order of passes in the pipeline is subject to change between versions of LLVM, optimization options and which pass manager is used.
This patch adds 'followup' attributes to various loop transformation passes. These attributes define which attributes the resulting loop of a transformation should have. For instance,
!0 = !{!0, !1, !2}
!1 = !{!"llvm.loop.unroll_and_jam.enable"}
!2 = !{!"llvm.loop.unroll_and_jam.followup_inner", !3}
!3 = !{!"llvm.loop.distribute.enable"}
defines a loop ID (!0) to be unrolled-and-jammed (!1) and then the attribute !3 to be added to the jammed inner loop, which contains the instruction to distribute the inner loop.
Currently, in both pass managers, pass execution is in a fixed order and UnrollAndJamPass will not execute again after LoopDistribute. We hope to fix this in the future by allowing pass managers to run passes until a fixpoint is reached, use Polly to perform these transformations, or add a loop transformation pass which takes the order issue into account.
For mandatory/forced transformations (e.g. by having been declared by #pragma omp simd), the user must be notified when a transformation could not be performed. It is not possible that the responsible pass emits such a warning because the transformation might be 'hidden' in a followup attribute when it is executed, or it is not present in the pipeline at all. For this reason, this patche introduces a WarnMissedTransformations pass, to warn about orphaned transformations.
Since this changes the user-visible diagnostic message when a transformation is applied, two test cases in the clang repository need to be updated.
To ensure that no other transformation is executed before the intended one, the attribute `llvm.loop.disable_nonforced` can be added which should disable transformation heuristics before the intended transformation is applied. E.g. it would be surprising if a loop is distributed before a #pragma unroll_and_jam is applied.
With more supported code transformations (loop fusion, interchange, stripmining, offloading, etc.), transformations can be used as building blocks for more complex transformations (e.g. stripmining+stripmining+interchange -> tiling).
Reviewed By: hfinkel, dmgreen
Differential Revision: https://reviews.llvm.org/D49281
Differential Revision: https://reviews.llvm.org/D55288
llvm-svn: 348944
2018-12-12 18:32:52 +01:00
|
|
|
// Unless IgnoreUser is true, will also use metadata and command-line options
|
|
|
|
// that are specific to to the LoopUnroll pass (which, for instance, are
|
|
|
|
// irrelevant for the LoopUnrollAndJam pass).
|
|
|
|
// FIXME: This function is used by LoopUnroll and LoopUnrollAndJam, but consumes
|
|
|
|
// many LoopUnroll-specific options. The shared functionality should be
|
|
|
|
// refactored into it own function.
|
2018-07-01 14:47:30 +02:00
|
|
|
bool llvm::computeUnrollCount(
|
2016-10-12 23:29:38 +02:00
|
|
|
Loop *L, const TargetTransformInfo &TTI, DominatorTree &DT, LoopInfo *LI,
|
2018-03-15 10:59:15 +01:00
|
|
|
ScalarEvolution &SE, const SmallPtrSetImpl<const Value *> &EphValues,
|
2021-06-19 10:09:12 +02:00
|
|
|
OptimizationRemarkEmitter *ORE, unsigned TripCount, unsigned MaxTripCount,
|
|
|
|
bool MaxOrZero, unsigned TripMultiple, unsigned LoopSize,
|
[NFC] Separate Peeling Properties into its own struct (re-land after minor fix)
Summary:
This patch separates the peeling specific parameters from the UnrollingPreferences,
and creates a new struct called PeelingPreferences. Functions which used the
UnrollingPreferences struct for peeling have been updated to use the PeelingPreferences struct.
Author: sidbav (Sidharth Baveja)
Reviewers: Whitney (Whitney Tsang), Meinersbur (Michael Kruse), skatkov (Serguei Katkov), ashlykov (Arkady Shlykov), bogner (Justin Bogner), hfinkel (Hal Finkel), anhtuyen (Anh Tuyen Tran), nikic (Nikita Popov)
Reviewed By: Meinersbur (Michael Kruse)
Subscribers: fhahn (Florian Hahn), hiraditya (Aditya Kumar), llvm-commits, LLVM
Tag: LLVM
Differential Revision: https://reviews.llvm.org/D80580
2020-07-10 20:38:08 +02:00
|
|
|
TargetTransformInfo::UnrollingPreferences &UP,
|
|
|
|
TargetTransformInfo::PeelingPreferences &PP, bool &UseUpperBound) {
|
[Unroll/UnrollAndJam/Vectorizer/Distribute] Add followup loop attributes.
When multiple loop transformation are defined in a loop's metadata, their order of execution is defined by the order of their respective passes in the pass pipeline. For instance, e.g.
#pragma clang loop unroll_and_jam(enable)
#pragma clang loop distribute(enable)
is the same as
#pragma clang loop distribute(enable)
#pragma clang loop unroll_and_jam(enable)
and will try to loop-distribute before Unroll-And-Jam because the LoopDistribute pass is scheduled after UnrollAndJam pass. UnrollAndJamPass only supports one inner loop, i.e. it will necessarily fail after loop distribution. It is not possible to specify another execution order. Also,t the order of passes in the pipeline is subject to change between versions of LLVM, optimization options and which pass manager is used.
This patch adds 'followup' attributes to various loop transformation passes. These attributes define which attributes the resulting loop of a transformation should have. For instance,
!0 = !{!0, !1, !2}
!1 = !{!"llvm.loop.unroll_and_jam.enable"}
!2 = !{!"llvm.loop.unroll_and_jam.followup_inner", !3}
!3 = !{!"llvm.loop.distribute.enable"}
defines a loop ID (!0) to be unrolled-and-jammed (!1) and then the attribute !3 to be added to the jammed inner loop, which contains the instruction to distribute the inner loop.
Currently, in both pass managers, pass execution is in a fixed order and UnrollAndJamPass will not execute again after LoopDistribute. We hope to fix this in the future by allowing pass managers to run passes until a fixpoint is reached, use Polly to perform these transformations, or add a loop transformation pass which takes the order issue into account.
For mandatory/forced transformations (e.g. by having been declared by #pragma omp simd), the user must be notified when a transformation could not be performed. It is not possible that the responsible pass emits such a warning because the transformation might be 'hidden' in a followup attribute when it is executed, or it is not present in the pipeline at all. For this reason, this patche introduces a WarnMissedTransformations pass, to warn about orphaned transformations.
Since this changes the user-visible diagnostic message when a transformation is applied, two test cases in the clang repository need to be updated.
To ensure that no other transformation is executed before the intended one, the attribute `llvm.loop.disable_nonforced` can be added which should disable transformation heuristics before the intended transformation is applied. E.g. it would be surprising if a loop is distributed before a #pragma unroll_and_jam is applied.
With more supported code transformations (loop fusion, interchange, stripmining, offloading, etc.), transformations can be used as building blocks for more complex transformations (e.g. stripmining+stripmining+interchange -> tiling).
Reviewed By: hfinkel, dmgreen
Differential Revision: https://reviews.llvm.org/D49281
Differential Revision: https://reviews.llvm.org/D55288
llvm-svn: 348944
2018-12-12 18:32:52 +01:00
|
|
|
|
2021-05-15 03:40:23 +02:00
|
|
|
UnrollCostEstimator UCE(*L, LoopSize);
|
|
|
|
|
2021-05-29 18:33:31 +02:00
|
|
|
// Use an explicit peel count that has been specified for testing. In this
|
|
|
|
// case it's not permitted to also specify an explicit unroll count.
|
|
|
|
if (PP.PeelCount) {
|
|
|
|
if (UnrollCount.getNumOccurrences() > 0) {
|
|
|
|
report_fatal_error("Cannot specify both explicit peel count and "
|
|
|
|
"explicit unroll count");
|
|
|
|
}
|
|
|
|
UP.Count = 1;
|
|
|
|
UP.Runtime = false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-05-28 01:15:06 +02:00
|
|
|
// Check for explicit Count.
|
|
|
|
// 1st priority is unroll count set by "unroll-count" option.
|
|
|
|
bool UserUnrollCount = UnrollCount.getNumOccurrences() > 0;
|
|
|
|
if (UserUnrollCount) {
|
|
|
|
UP.Count = UnrollCount;
|
|
|
|
UP.AllowExpensiveTripCount = true;
|
|
|
|
UP.Force = true;
|
2021-05-15 03:40:23 +02:00
|
|
|
if (UP.AllowRemainder && UCE.getUnrolledLoopSize(UP) < UP.Threshold)
|
2016-05-28 01:15:06 +02:00
|
|
|
return true;
|
|
|
|
}
|
2011-07-23 02:29:16 +02:00
|
|
|
|
2016-05-28 01:15:06 +02:00
|
|
|
// 2nd priority is unroll count set by pragma.
|
2020-02-05 01:29:04 +01:00
|
|
|
unsigned PragmaCount = unrollCountPragmaValue(L);
|
2016-05-28 01:15:06 +02:00
|
|
|
if (PragmaCount > 0) {
|
|
|
|
UP.Count = PragmaCount;
|
|
|
|
UP.Runtime = true;
|
|
|
|
UP.AllowExpensiveTripCount = true;
|
|
|
|
UP.Force = true;
|
2018-03-02 17:22:32 +01:00
|
|
|
if ((UP.AllowRemainder || (TripMultiple % PragmaCount == 0)) &&
|
2021-05-15 03:40:23 +02:00
|
|
|
UCE.getUnrolledLoopSize(UP) < PragmaUnrollThreshold)
|
2016-05-28 01:15:06 +02:00
|
|
|
return true;
|
2014-04-01 20:50:30 +02:00
|
|
|
}
|
2020-02-05 01:29:04 +01:00
|
|
|
bool PragmaFullUnroll = hasUnrollFullPragma(L);
|
2016-05-28 01:15:06 +02:00
|
|
|
if (PragmaFullUnroll && TripCount != 0) {
|
|
|
|
UP.Count = TripCount;
|
2021-05-15 03:40:23 +02:00
|
|
|
if (UCE.getUnrolledLoopSize(UP) < PragmaUnrollThreshold)
|
2016-05-28 01:15:06 +02:00
|
|
|
return false;
|
2011-08-12 01:36:16 +02:00
|
|
|
}
|
2013-09-11 21:25:43 +02:00
|
|
|
|
2020-02-05 01:29:04 +01:00
|
|
|
bool PragmaEnableUnroll = hasUnrollEnablePragma(L);
|
2016-05-28 01:15:06 +02:00
|
|
|
bool ExplicitUnroll = PragmaCount > 0 || PragmaFullUnroll ||
|
|
|
|
PragmaEnableUnroll || UserUnrollCount;
|
2014-06-17 01:53:02 +02:00
|
|
|
|
2016-05-28 01:15:06 +02:00
|
|
|
if (ExplicitUnroll && TripCount != 0) {
|
|
|
|
// If the loop has an unrolling pragma, we want to be more aggressive with
|
2018-07-01 14:47:30 +02:00
|
|
|
// unrolling limits. Set thresholds to at least the PragmaUnrollThreshold
|
|
|
|
// value which is larger than the default limits.
|
2016-05-28 01:15:06 +02:00
|
|
|
UP.Threshold = std::max<unsigned>(UP.Threshold, PragmaUnrollThreshold);
|
|
|
|
UP.PartialThreshold =
|
|
|
|
std::max<unsigned>(UP.PartialThreshold, PragmaUnrollThreshold);
|
2014-06-17 01:53:02 +02:00
|
|
|
}
|
2011-12-09 07:19:40 +01:00
|
|
|
|
2016-05-28 01:15:06 +02:00
|
|
|
// 3rd priority is full unroll count.
|
2016-10-12 23:29:38 +02:00
|
|
|
// Full unroll makes sense only when TripCount or its upper bound could be
|
|
|
|
// statically calculated.
|
2016-05-28 01:15:06 +02:00
|
|
|
// Also we need to check if we exceed FullUnrollMaxCount.
|
2019-09-26 23:40:27 +02:00
|
|
|
|
|
|
|
// We can unroll by the upper bound amount if it's generally allowed or if
|
|
|
|
// we know that the loop is executed either the upper bound or zero times.
|
|
|
|
// (MaxOrZero unrolling keeps only the first loop test, so the number of
|
|
|
|
// loop tests remains the same compared to the non-unrolled version, whereas
|
|
|
|
// the generic upper bound unrolling keeps all but the last loop test so the
|
|
|
|
// number of loop tests goes up which may end up being worse on targets with
|
|
|
|
// constrained branch predictor resources so is controlled by an option.)
|
|
|
|
// In addition we only unroll small upper bounds.
|
|
|
|
unsigned FullUnrollMaxTripCount = MaxTripCount;
|
|
|
|
if (!(UP.UpperBound || MaxOrZero) ||
|
|
|
|
FullUnrollMaxTripCount > UnrollMaxUpperBound)
|
|
|
|
FullUnrollMaxTripCount = 0;
|
|
|
|
|
|
|
|
// UnrollByMaxCount and ExactTripCount cannot both be non zero since we only
|
2016-10-12 23:29:38 +02:00
|
|
|
// compute the former when the latter is zero.
|
|
|
|
unsigned ExactTripCount = TripCount;
|
2019-09-26 23:40:27 +02:00
|
|
|
assert((ExactTripCount == 0 || FullUnrollMaxTripCount == 0) &&
|
|
|
|
"ExtractTripCount and UnrollByMaxCount cannot both be non zero.");
|
|
|
|
|
|
|
|
unsigned FullUnrollTripCount =
|
|
|
|
ExactTripCount ? ExactTripCount : FullUnrollMaxTripCount;
|
2016-11-09 20:56:39 +01:00
|
|
|
UP.Count = FullUnrollTripCount;
|
2016-10-12 23:29:38 +02:00
|
|
|
if (FullUnrollTripCount && FullUnrollTripCount <= UP.FullUnrollMaxCount) {
|
2016-05-28 01:15:06 +02:00
|
|
|
// When computing the unrolled size, note that BEInsns are not replicated
|
|
|
|
// like the rest of the loop body.
|
2021-05-15 03:40:23 +02:00
|
|
|
if (UCE.getUnrolledLoopSize(UP) < UP.Threshold) {
|
2019-09-26 23:40:27 +02:00
|
|
|
UseUpperBound = (FullUnrollMaxTripCount == FullUnrollTripCount);
|
2016-05-28 01:15:06 +02:00
|
|
|
return ExplicitUnroll;
|
2015-05-12 19:20:03 +02:00
|
|
|
} else {
|
|
|
|
// The loop isn't that small, but we still can fully unroll it if that
|
|
|
|
// helps to remove a significant number of instructions.
|
|
|
|
// To check that, run additional analysis on the loop.
|
2016-01-12 01:55:26 +01:00
|
|
|
if (Optional<EstimatedUnrollCost> Cost = analyzeLoopUnrollCost(
|
2018-03-15 10:59:15 +01:00
|
|
|
L, FullUnrollTripCount, DT, SE, EphValues, TTI,
|
[AMDGPU] Increase max iterations count to analyze complete unroll
Summary: In some cases inner loops may not get boosts so try to analyze them deeper.
Reviewers: rampitec, mzolotukhin
Reviewed By: rampitec
Subscribers: arsenm, kzhuravl, jvesely, wdng, nhaehnle, yaxunl, dstuttard, tpr, t-tye, hiraditya, zzheng, kerbowa, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D81204
2020-06-05 01:18:18 +02:00
|
|
|
UP.Threshold * UP.MaxPercentThresholdBoost / 100,
|
|
|
|
UP.MaxIterationsCountToAnalyze)) {
|
2016-12-30 01:50:28 +01:00
|
|
|
unsigned Boost =
|
|
|
|
getFullUnrollBoostingFactor(*Cost, UP.MaxPercentThresholdBoost);
|
|
|
|
if (Cost->UnrolledCost < UP.Threshold * Boost / 100) {
|
2019-09-26 23:40:27 +02:00
|
|
|
UseUpperBound = (FullUnrollMaxTripCount == FullUnrollTripCount);
|
2016-05-28 01:15:06 +02:00
|
|
|
return ExplicitUnroll;
|
2015-05-22 19:41:35 +02:00
|
|
|
}
|
2016-12-30 01:50:28 +01:00
|
|
|
}
|
2014-06-17 01:53:02 +02:00
|
|
|
}
|
2007-05-11 22:53:41 +02:00
|
|
|
}
|
2007-03-03 00:31:34 +01:00
|
|
|
|
2018-10-05 11:39:07 +02:00
|
|
|
// 4th priority is loop peeling.
|
2020-07-31 20:31:58 +02:00
|
|
|
computePeelCount(L, LoopSize, PP, TripCount, SE, UP.Threshold);
|
[NFC] Separate Peeling Properties into its own struct (re-land after minor fix)
Summary:
This patch separates the peeling specific parameters from the UnrollingPreferences,
and creates a new struct called PeelingPreferences. Functions which used the
UnrollingPreferences struct for peeling have been updated to use the PeelingPreferences struct.
Author: sidbav (Sidharth Baveja)
Reviewers: Whitney (Whitney Tsang), Meinersbur (Michael Kruse), skatkov (Serguei Katkov), ashlykov (Arkady Shlykov), bogner (Justin Bogner), hfinkel (Hal Finkel), anhtuyen (Anh Tuyen Tran), nikic (Nikita Popov)
Reviewed By: Meinersbur (Michael Kruse)
Subscribers: fhahn (Florian Hahn), hiraditya (Aditya Kumar), llvm-commits, LLVM
Tag: LLVM
Differential Revision: https://reviews.llvm.org/D80580
2020-07-10 20:38:08 +02:00
|
|
|
if (PP.PeelCount) {
|
[LoopUnrolling] Re-prioritize Peeling and Partial unrolling
Summary:
In current implementation the loop peeling happens after trip-count based partial unrolling and may
sometimes not happen at all due to it (for example, if trip count is known, but UP.Partial = false). This
is generally bad, the more than there are some situations where peeling is profitable even if the partial
unrolling is disabled.
This patch is a NFC which reorders peeling and partial unrolling application and prepares the code for
implementation of the said optimizations.
Patch by Max Kazantsev!
Reviewers: sanjoy, anna, reames, apilipenko, igor-laevsky, mkuper
Reviewed By: mkuper
Subscribers: mkuper, llvm-commits, mzolotukhin
Differential Revision: https://reviews.llvm.org/D30243
llvm-svn: 296897
2017-03-03 19:19:10 +01:00
|
|
|
UP.Runtime = false;
|
|
|
|
UP.Count = 1;
|
|
|
|
return ExplicitUnroll;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 5th priority is partial unrolling.
|
2018-06-14 07:41:49 +02:00
|
|
|
// Try partial unroll only when TripCount could be statically calculated.
|
2016-05-28 01:15:06 +02:00
|
|
|
if (TripCount) {
|
|
|
|
UP.Partial |= ExplicitUnroll;
|
|
|
|
if (!UP.Partial) {
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << " will not try to unroll partially because "
|
|
|
|
<< "-unroll-allow-partial not given\n");
|
2016-05-28 01:15:06 +02:00
|
|
|
UP.Count = 0;
|
2012-12-20 17:04:27 +01:00
|
|
|
return false;
|
|
|
|
}
|
2016-10-27 20:40:02 +02:00
|
|
|
if (UP.Count == 0)
|
|
|
|
UP.Count = TripCount;
|
2016-05-28 01:15:06 +02:00
|
|
|
if (UP.PartialThreshold != NoThreshold) {
|
2014-06-17 01:53:02 +02:00
|
|
|
// Reduce unroll count to be modulo of TripCount for partial unrolling.
|
2021-05-15 03:40:23 +02:00
|
|
|
if (UCE.getUnrolledLoopSize(UP) > UP.PartialThreshold)
|
2016-11-09 20:56:39 +01:00
|
|
|
UP.Count =
|
|
|
|
(std::max(UP.PartialThreshold, UP.BEInsns + 1) - UP.BEInsns) /
|
|
|
|
(LoopSize - UP.BEInsns);
|
2016-05-28 01:15:06 +02:00
|
|
|
if (UP.Count > UP.MaxCount)
|
|
|
|
UP.Count = UP.MaxCount;
|
|
|
|
while (UP.Count != 0 && TripCount % UP.Count != 0)
|
|
|
|
UP.Count--;
|
|
|
|
if (UP.AllowRemainder && UP.Count <= 1) {
|
2016-04-04 21:24:46 +02:00
|
|
|
// If there is no Count that is modulo of TripCount, set Count to
|
|
|
|
// largest power-of-two factor that satisfies the threshold limit.
|
2016-04-06 18:43:45 +02:00
|
|
|
// As we'll create fixup loop, do the type of unrolling only if
|
2016-05-28 01:15:06 +02:00
|
|
|
// remainder loop is allowed.
|
2016-09-28 11:41:38 +02:00
|
|
|
UP.Count = UP.DefaultUnrollRuntimeCount;
|
2016-11-09 20:56:39 +01:00
|
|
|
while (UP.Count != 0 &&
|
2021-05-15 03:40:23 +02:00
|
|
|
UCE.getUnrolledLoopSize(UP) > UP.PartialThreshold)
|
2016-05-28 01:15:06 +02:00
|
|
|
UP.Count >>= 1;
|
2016-04-04 21:24:46 +02:00
|
|
|
}
|
2016-05-28 01:15:06 +02:00
|
|
|
if (UP.Count < 2) {
|
|
|
|
if (PragmaEnableUnroll)
|
2017-10-11 19:12:59 +02:00
|
|
|
ORE->emit([&]() {
|
|
|
|
return OptimizationRemarkMissed(DEBUG_TYPE,
|
|
|
|
"UnrollAsDirectedTooLarge",
|
|
|
|
L->getStartLoc(), L->getHeader())
|
|
|
|
<< "Unable to unroll loop as directed by unroll(enable) "
|
|
|
|
"pragma "
|
|
|
|
"because unrolled size is too large.";
|
|
|
|
});
|
2016-05-28 01:15:06 +02:00
|
|
|
UP.Count = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
UP.Count = TripCount;
|
2014-06-17 01:53:02 +02:00
|
|
|
}
|
2017-06-28 19:01:15 +02:00
|
|
|
if (UP.Count > UP.MaxCount)
|
|
|
|
UP.Count = UP.MaxCount;
|
2016-05-28 01:15:06 +02:00
|
|
|
if ((PragmaFullUnroll || PragmaEnableUnroll) && TripCount &&
|
|
|
|
UP.Count != TripCount)
|
2017-10-11 19:12:59 +02:00
|
|
|
ORE->emit([&]() {
|
|
|
|
return OptimizationRemarkMissed(DEBUG_TYPE,
|
|
|
|
"FullUnrollAsDirectedTooLarge",
|
|
|
|
L->getStartLoc(), L->getHeader())
|
|
|
|
<< "Unable to fully unroll loop as directed by unroll pragma "
|
|
|
|
"because "
|
|
|
|
"unrolled size is too large.";
|
|
|
|
});
|
2019-09-26 23:40:27 +02:00
|
|
|
LLVM_DEBUG(dbgs() << " partially unrolling with count: " << UP.Count
|
|
|
|
<< "\n");
|
2016-05-28 01:15:06 +02:00
|
|
|
return ExplicitUnroll;
|
|
|
|
}
|
|
|
|
assert(TripCount == 0 &&
|
|
|
|
"All cases when TripCount is constant should be covered here.");
|
|
|
|
if (PragmaFullUnroll)
|
2017-10-11 19:12:59 +02:00
|
|
|
ORE->emit([&]() {
|
|
|
|
return OptimizationRemarkMissed(
|
|
|
|
DEBUG_TYPE, "CantFullUnrollAsDirectedRuntimeTripCount",
|
|
|
|
L->getStartLoc(), L->getHeader())
|
|
|
|
<< "Unable to fully unroll loop as directed by unroll(full) "
|
|
|
|
"pragma "
|
|
|
|
"because loop has a runtime trip count.";
|
|
|
|
});
|
2016-05-28 01:15:06 +02:00
|
|
|
|
2016-11-30 22:13:57 +01:00
|
|
|
// 6th priority is runtime unrolling.
|
2016-05-28 01:15:06 +02:00
|
|
|
// Don't unroll a runtime trip count loop when it is disabled.
|
2020-02-05 01:29:04 +01:00
|
|
|
if (hasRuntimeUnrollDisablePragma(L)) {
|
2016-05-28 01:15:06 +02:00
|
|
|
UP.Count = 0;
|
|
|
|
return false;
|
|
|
|
}
|
2018-07-30 21:41:25 +02:00
|
|
|
|
2019-09-26 23:40:27 +02:00
|
|
|
// Don't unroll a small upper bound loop unless user or TTI asked to do so.
|
|
|
|
if (MaxTripCount && !UP.Force && MaxTripCount < UnrollMaxUpperBound) {
|
|
|
|
UP.Count = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-11-30 22:13:57 +01:00
|
|
|
// Check if the runtime trip count is too small when profile is available.
|
2017-12-22 02:33:52 +01:00
|
|
|
if (L->getHeader()->getParent()->hasProfileData()) {
|
2016-11-30 22:13:57 +01:00
|
|
|
if (auto ProfileTripCount = getLoopEstimatedTripCount(L)) {
|
|
|
|
if (*ProfileTripCount < FlatLoopTripCountThreshold)
|
|
|
|
return false;
|
|
|
|
else
|
|
|
|
UP.AllowExpensiveTripCount = true;
|
|
|
|
}
|
2018-07-30 21:41:25 +02:00
|
|
|
}
|
2016-11-30 22:13:57 +01:00
|
|
|
|
2016-05-28 01:15:06 +02:00
|
|
|
// Reduce count based on the type of unrolling and the threshold values.
|
|
|
|
UP.Runtime |= PragmaEnableUnroll || PragmaCount > 0 || UserUnrollCount;
|
|
|
|
if (!UP.Runtime) {
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(
|
|
|
|
dbgs() << " will not try to unroll loop with runtime trip count "
|
|
|
|
<< "-unroll-runtime not given\n");
|
2016-05-28 01:15:06 +02:00
|
|
|
UP.Count = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (UP.Count == 0)
|
2016-09-28 11:41:38 +02:00
|
|
|
UP.Count = UP.DefaultUnrollRuntimeCount;
|
2016-05-28 01:15:06 +02:00
|
|
|
|
|
|
|
// Reduce unroll count to be the largest power-of-two factor of
|
|
|
|
// the original count which satisfies the threshold limit.
|
2016-11-09 20:56:39 +01:00
|
|
|
while (UP.Count != 0 &&
|
2021-05-15 03:40:23 +02:00
|
|
|
UCE.getUnrolledLoopSize(UP) > UP.PartialThreshold)
|
2016-05-28 01:15:06 +02:00
|
|
|
UP.Count >>= 1;
|
2014-06-17 01:53:02 +02:00
|
|
|
|
2016-05-28 02:14:58 +02:00
|
|
|
#ifndef NDEBUG
|
2016-05-28 01:15:06 +02:00
|
|
|
unsigned OrigCount = UP.Count;
|
2016-05-28 02:14:58 +02:00
|
|
|
#endif
|
2016-05-28 01:15:06 +02:00
|
|
|
|
|
|
|
if (!UP.AllowRemainder && UP.Count != 0 && (TripMultiple % UP.Count) != 0) {
|
|
|
|
while (UP.Count != 0 && TripMultiple % UP.Count != 0)
|
|
|
|
UP.Count >>= 1;
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(
|
|
|
|
dbgs() << "Remainder loop is restricted (that could architecture "
|
|
|
|
"specific or because the loop contains a convergent "
|
|
|
|
"instruction), so unroll count must divide the trip "
|
|
|
|
"multiple, "
|
|
|
|
<< TripMultiple << ". Reducing unroll count from " << OrigCount
|
|
|
|
<< " to " << UP.Count << ".\n");
|
2017-10-18 23:46:47 +02:00
|
|
|
|
2016-09-30 05:44:16 +02:00
|
|
|
using namespace ore;
|
2017-10-18 23:46:47 +02:00
|
|
|
|
2016-05-28 01:15:06 +02:00
|
|
|
if (PragmaCount > 0 && !UP.AllowRemainder)
|
2017-10-11 19:12:59 +02:00
|
|
|
ORE->emit([&]() {
|
|
|
|
return OptimizationRemarkMissed(DEBUG_TYPE,
|
|
|
|
"DifferentUnrollCountFromDirected",
|
|
|
|
L->getStartLoc(), L->getHeader())
|
|
|
|
<< "Unable to unroll loop the number of times directed by "
|
|
|
|
"unroll_count pragma because remainder loop is restricted "
|
|
|
|
"(that could architecture specific or because the loop "
|
|
|
|
"contains a convergent instruction) and so must have an "
|
|
|
|
"unroll "
|
|
|
|
"count that divides the loop trip multiple of "
|
|
|
|
<< NV("TripMultiple", TripMultiple) << ". Unrolling instead "
|
|
|
|
<< NV("UnrollCount", UP.Count) << " time(s).";
|
|
|
|
});
|
2004-04-18 07:20:17 +02:00
|
|
|
}
|
2005-04-22 01:48:37 +02:00
|
|
|
|
2016-05-28 01:15:06 +02:00
|
|
|
if (UP.Count > UP.MaxCount)
|
|
|
|
UP.Count = UP.MaxCount;
|
2019-09-26 23:40:27 +02:00
|
|
|
|
|
|
|
if (MaxTripCount && UP.Count > MaxTripCount)
|
|
|
|
UP.Count = MaxTripCount;
|
|
|
|
|
|
|
|
LLVM_DEBUG(dbgs() << " runtime unrolling with count: " << UP.Count
|
2018-05-14 14:53:11 +02:00
|
|
|
<< "\n");
|
2016-05-28 01:15:06 +02:00
|
|
|
if (UP.Count < 2)
|
|
|
|
UP.Count = 0;
|
|
|
|
return ExplicitUnroll;
|
|
|
|
}
|
|
|
|
|
2017-09-27 23:45:22 +02:00
|
|
|
static LoopUnrollResult tryToUnrollLoop(
|
2017-08-03 19:52:38 +02:00
|
|
|
Loop *L, DominatorTree &DT, LoopInfo *LI, ScalarEvolution &SE,
|
|
|
|
const TargetTransformInfo &TTI, AssumptionCache &AC,
|
2019-08-02 11:32:52 +02:00
|
|
|
OptimizationRemarkEmitter &ORE, BlockFrequencyInfo *BFI,
|
|
|
|
ProfileSummaryInfo *PSI, bool PreserveLCSSA, int OptLevel,
|
2019-04-12 21:16:07 +02:00
|
|
|
bool OnlyWhenForced, bool ForgetAllSCEV, Optional<unsigned> ProvidedCount,
|
2018-12-18 18:16:05 +01:00
|
|
|
Optional<unsigned> ProvidedThreshold, Optional<bool> ProvidedAllowPartial,
|
|
|
|
Optional<bool> ProvidedRuntime, Optional<bool> ProvidedUpperBound,
|
2019-08-02 11:32:52 +02:00
|
|
|
Optional<bool> ProvidedAllowPeeling,
|
2019-09-19 08:57:29 +02:00
|
|
|
Optional<bool> ProvidedAllowProfileBasedPeeling,
|
|
|
|
Optional<unsigned> ProvidedFullUnrollMaxCount) {
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << "Loop Unroll: F["
|
|
|
|
<< L->getHeader()->getParent()->getName() << "] Loop %"
|
|
|
|
<< L->getHeader()->getName() << "\n");
|
2018-12-18 18:16:05 +01:00
|
|
|
TransformationMode TM = hasUnrollTransformation(L);
|
|
|
|
if (TM & TM_Disable)
|
2017-09-27 23:45:22 +02:00
|
|
|
return LoopUnrollResult::Unmodified;
|
2017-10-18 23:46:47 +02:00
|
|
|
if (!L->isLoopSimplifyForm()) {
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(
|
2016-11-23 20:39:26 +01:00
|
|
|
dbgs() << " Not unrolling loop which is not in loop-simplify form.\n");
|
2017-09-27 23:45:22 +02:00
|
|
|
return LoopUnrollResult::Unmodified;
|
2014-06-17 01:53:02 +02:00
|
|
|
}
|
|
|
|
|
2020-10-17 15:20:55 +02:00
|
|
|
// When automatic unrolling is disabled, do not unroll unless overridden for
|
2018-12-18 18:16:05 +01:00
|
|
|
// this loop.
|
|
|
|
if (OnlyWhenForced && !(TM & TM_Enable))
|
|
|
|
return LoopUnrollResult::Unmodified;
|
|
|
|
|
2019-04-17 17:57:43 +02:00
|
|
|
bool OptForSize = L->getHeader()->getParent()->hasOptSize();
|
2016-05-28 01:15:06 +02:00
|
|
|
unsigned NumInlineCandidates;
|
|
|
|
bool NotDuplicatable;
|
|
|
|
bool Convergent;
|
2016-11-09 20:56:39 +01:00
|
|
|
TargetTransformInfo::UnrollingPreferences UP = gatherUnrollingPreferences(
|
2019-04-15 18:49:00 +02:00
|
|
|
L, SE, TTI, BFI, PSI, OptLevel, ProvidedThreshold, ProvidedCount,
|
2017-08-03 19:52:38 +02:00
|
|
|
ProvidedAllowPartial, ProvidedRuntime, ProvidedUpperBound,
|
2019-09-19 08:57:29 +02:00
|
|
|
ProvidedFullUnrollMaxCount);
|
[NFC] Separate Peeling Properties into its own struct (re-land after minor fix)
Summary:
This patch separates the peeling specific parameters from the UnrollingPreferences,
and creates a new struct called PeelingPreferences. Functions which used the
UnrollingPreferences struct for peeling have been updated to use the PeelingPreferences struct.
Author: sidbav (Sidharth Baveja)
Reviewers: Whitney (Whitney Tsang), Meinersbur (Michael Kruse), skatkov (Serguei Katkov), ashlykov (Arkady Shlykov), bogner (Justin Bogner), hfinkel (Hal Finkel), anhtuyen (Anh Tuyen Tran), nikic (Nikita Popov)
Reviewed By: Meinersbur (Michael Kruse)
Subscribers: fhahn (Florian Hahn), hiraditya (Aditya Kumar), llvm-commits, LLVM
Tag: LLVM
Differential Revision: https://reviews.llvm.org/D80580
2020-07-10 20:38:08 +02:00
|
|
|
TargetTransformInfo::PeelingPreferences PP = gatherPeelingPreferences(
|
2020-07-31 20:31:58 +02:00
|
|
|
L, SE, TTI, ProvidedAllowPeeling, ProvidedAllowProfileBasedPeeling, true);
|
2019-04-17 17:57:43 +02:00
|
|
|
|
|
|
|
// Exit early if unrolling is disabled. For OptForSize, we pick the loop size
|
|
|
|
// as threshold later on.
|
|
|
|
if (UP.Threshold == 0 && (!UP.Partial || UP.PartialThreshold == 0) &&
|
|
|
|
!OptForSize)
|
2017-09-27 23:45:22 +02:00
|
|
|
return LoopUnrollResult::Unmodified;
|
2018-03-15 10:59:15 +01:00
|
|
|
|
|
|
|
SmallPtrSet<const Value *, 32> EphValues;
|
|
|
|
CodeMetrics::collectEphemeralValues(L, &AC, EphValues);
|
|
|
|
|
|
|
|
unsigned LoopSize =
|
|
|
|
ApproximateLoopSize(L, NumInlineCandidates, NotDuplicatable, Convergent,
|
|
|
|
TTI, EphValues, UP.BEInsns);
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << " Loop Size = " << LoopSize << "\n");
|
2016-05-28 01:15:06 +02:00
|
|
|
if (NotDuplicatable) {
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << " Not unrolling loop which contains non-duplicatable"
|
|
|
|
<< " instructions.\n");
|
2017-09-27 23:45:22 +02:00
|
|
|
return LoopUnrollResult::Unmodified;
|
2016-05-28 01:15:06 +02:00
|
|
|
}
|
2019-04-17 17:57:43 +02:00
|
|
|
|
2019-09-17 11:02:48 +02:00
|
|
|
// When optimizing for size, use LoopSize + 1 as threshold (we use < Threshold
|
|
|
|
// later), to (fully) unroll loops, if it does not increase code size.
|
2019-04-17 17:57:43 +02:00
|
|
|
if (OptForSize)
|
2019-09-17 11:02:48 +02:00
|
|
|
UP.Threshold = std::max(UP.Threshold, LoopSize + 1);
|
2019-04-17 17:57:43 +02:00
|
|
|
|
2016-05-28 01:15:06 +02:00
|
|
|
if (NumInlineCandidates != 0) {
|
2018-05-14 14:53:11 +02:00
|
|
|
LLVM_DEBUG(dbgs() << " Not unrolling loop with inlinable calls.\n");
|
2017-09-27 23:45:22 +02:00
|
|
|
return LoopUnrollResult::Unmodified;
|
2016-05-28 01:15:06 +02:00
|
|
|
}
|
|
|
|
|
2021-06-19 09:44:28 +02:00
|
|
|
// Find the smallest exact trip count for any exit. This is an upper bound
|
|
|
|
// on the loop trip count, but an exit at an earlier iteration is still
|
|
|
|
// possible. An unroll by the smallest exact trip count guarantees that all
|
|
|
|
// brnaches relating to at least one exit can be eliminated. This is unlike
|
|
|
|
// the max trip count, which only guarantees that the backedge can be broken.
|
2016-05-28 01:15:06 +02:00
|
|
|
unsigned TripCount = 0;
|
|
|
|
unsigned TripMultiple = 1;
|
2021-06-19 09:44:28 +02:00
|
|
|
SmallVector<BasicBlock *, 8> ExitingBlocks;
|
|
|
|
L->getExitingBlocks(ExitingBlocks);
|
|
|
|
for (BasicBlock *ExitingBlock : ExitingBlocks)
|
|
|
|
if (unsigned TC = SE.getSmallConstantTripCount(L, ExitingBlock))
|
|
|
|
if (!TripCount || TC < TripCount)
|
|
|
|
TripCount = TripMultiple = TC;
|
|
|
|
|
|
|
|
if (!TripCount) {
|
|
|
|
// If no exact trip count is known, determine the trip multiple of either
|
|
|
|
// the loop latch or the single exiting block.
|
|
|
|
// TODO: Relax for multiple exits.
|
|
|
|
BasicBlock *ExitingBlock = L->getLoopLatch();
|
|
|
|
if (!ExitingBlock || !L->isLoopExiting(ExitingBlock))
|
|
|
|
ExitingBlock = L->getExitingBlock();
|
|
|
|
if (ExitingBlock)
|
|
|
|
TripMultiple = SE.getSmallConstantTripMultiple(L, ExitingBlock);
|
2016-05-28 01:15:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// If the loop contains a convergent operation, the prelude we'd add
|
|
|
|
// to do the first few instructions before we hit the unrolled loop
|
|
|
|
// is unsafe -- it adds a control-flow dependency to the convergent
|
2020-10-17 15:20:55 +02:00
|
|
|
// operation. Therefore restrict remainder loop (try unrolling without).
|
2016-05-28 01:15:06 +02:00
|
|
|
//
|
|
|
|
// TODO: This is quite conservative. In practice, convergent_op()
|
|
|
|
// is likely to be called unconditionally in the loop. In this
|
|
|
|
// case, the program would be ill-formed (on most architectures)
|
|
|
|
// unless n were the same on all threads in a thread group.
|
|
|
|
// Assuming n is the same on all threads, any kind of unrolling is
|
|
|
|
// safe. But currently llvm's notion of convergence isn't powerful
|
|
|
|
// enough to express this.
|
|
|
|
if (Convergent)
|
|
|
|
UP.AllowRemainder = false;
|
|
|
|
|
2016-10-21 13:08:48 +02:00
|
|
|
// Try to find the trip count upper bound if we cannot find the exact trip
|
|
|
|
// count.
|
2019-09-26 23:40:27 +02:00
|
|
|
unsigned MaxTripCount = 0;
|
2016-10-21 13:08:48 +02:00
|
|
|
bool MaxOrZero = false;
|
|
|
|
if (!TripCount) {
|
[LoopUnroll] Pass SCEV to getUnrollingPreferences hook. NFCI.
Reviewers: sanjoy, anna, reames, apilipenko, igor-laevsky, mkuper
Subscribers: jholewinski, arsenm, mzolotukhin, nemanjai, nhaehnle, javed.absar, mcrosier, llvm-commits
Differential Revision: https://reviews.llvm.org/D34531
llvm-svn: 306554
2017-06-28 17:53:17 +02:00
|
|
|
MaxTripCount = SE.getSmallConstantMaxTripCount(L);
|
|
|
|
MaxOrZero = SE.isBackedgeTakenCountMaxOrZero(L);
|
2016-10-12 23:29:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// computeUnrollCount() decides whether it is beneficial to use upper bound to
|
|
|
|
// fully unroll the loop.
|
|
|
|
bool UseUpperBound = false;
|
2018-03-15 10:59:15 +01:00
|
|
|
bool IsCountSetExplicitly = computeUnrollCount(
|
2019-09-26 23:40:27 +02:00
|
|
|
L, TTI, DT, LI, SE, EphValues, &ORE, TripCount, MaxTripCount, MaxOrZero,
|
[NFC] Separate Peeling Properties into its own struct (re-land after minor fix)
Summary:
This patch separates the peeling specific parameters from the UnrollingPreferences,
and creates a new struct called PeelingPreferences. Functions which used the
UnrollingPreferences struct for peeling have been updated to use the PeelingPreferences struct.
Author: sidbav (Sidharth Baveja)
Reviewers: Whitney (Whitney Tsang), Meinersbur (Michael Kruse), skatkov (Serguei Katkov), ashlykov (Arkady Shlykov), bogner (Justin Bogner), hfinkel (Hal Finkel), anhtuyen (Anh Tuyen Tran), nikic (Nikita Popov)
Reviewed By: Meinersbur (Michael Kruse)
Subscribers: fhahn (Florian Hahn), hiraditya (Aditya Kumar), llvm-commits, LLVM
Tag: LLVM
Differential Revision: https://reviews.llvm.org/D80580
2020-07-10 20:38:08 +02:00
|
|
|
TripMultiple, LoopSize, UP, PP, UseUpperBound);
|
2016-05-28 01:15:06 +02:00
|
|
|
if (!UP.Count)
|
2017-09-27 23:45:22 +02:00
|
|
|
return LoopUnrollResult::Unmodified;
|
2016-05-28 01:15:06 +02:00
|
|
|
|
2021-05-29 18:33:31 +02:00
|
|
|
if (PP.PeelCount) {
|
|
|
|
assert(UP.Count == 1 && "Cannot perform peel and unroll in the same step");
|
|
|
|
LLVM_DEBUG(dbgs() << "PEELING loop %" << L->getHeader()->getName()
|
|
|
|
<< " with iteration count " << PP.PeelCount << "!\n");
|
|
|
|
ORE.emit([&]() {
|
|
|
|
return OptimizationRemark(DEBUG_TYPE, "Peeled", L->getStartLoc(),
|
|
|
|
L->getHeader())
|
|
|
|
<< " peeled loop by " << ore::NV("PeelCount", PP.PeelCount)
|
|
|
|
<< " iterations";
|
|
|
|
});
|
|
|
|
|
|
|
|
if (peelLoop(L, PP.PeelCount, LI, &SE, &DT, &AC, PreserveLCSSA)) {
|
|
|
|
simplifyLoopAfterUnroll(L, true, LI, &SE, &DT, &AC, &TTI);
|
|
|
|
// If the loop was peeled, we already "used up" the profile information
|
|
|
|
// we had, so we don't want to unroll or peel again.
|
|
|
|
if (PP.PeelProfiledIterations)
|
|
|
|
L->setLoopAlreadyUnrolled();
|
|
|
|
return LoopUnrollResult::PartiallyUnrolled;
|
|
|
|
}
|
|
|
|
return LoopUnrollResult::Unmodified;
|
|
|
|
}
|
|
|
|
|
2021-06-17 21:49:29 +02:00
|
|
|
// At this point, UP.Runtime indicates that run-time unrolling is allowed.
|
|
|
|
// However, we only want to actually perform it if we don't know the trip
|
|
|
|
// count and the unroll count doesn't divide the known trip multiple.
|
|
|
|
// TODO: This decision should probably be pushed up into
|
|
|
|
// computeUnrollCount().
|
|
|
|
UP.Runtime &= TripCount == 0 && TripMultiple % UP.Count != 0;
|
|
|
|
|
[Unroll/UnrollAndJam/Vectorizer/Distribute] Add followup loop attributes.
When multiple loop transformation are defined in a loop's metadata, their order of execution is defined by the order of their respective passes in the pass pipeline. For instance, e.g.
#pragma clang loop unroll_and_jam(enable)
#pragma clang loop distribute(enable)
is the same as
#pragma clang loop distribute(enable)
#pragma clang loop unroll_and_jam(enable)
and will try to loop-distribute before Unroll-And-Jam because the LoopDistribute pass is scheduled after UnrollAndJam pass. UnrollAndJamPass only supports one inner loop, i.e. it will necessarily fail after loop distribution. It is not possible to specify another execution order. Also,t the order of passes in the pipeline is subject to change between versions of LLVM, optimization options and which pass manager is used.
This patch adds 'followup' attributes to various loop transformation passes. These attributes define which attributes the resulting loop of a transformation should have. For instance,
!0 = !{!0, !1, !2}
!1 = !{!"llvm.loop.unroll_and_jam.enable"}
!2 = !{!"llvm.loop.unroll_and_jam.followup_inner", !3}
!3 = !{!"llvm.loop.distribute.enable"}
defines a loop ID (!0) to be unrolled-and-jammed (!1) and then the attribute !3 to be added to the jammed inner loop, which contains the instruction to distribute the inner loop.
Currently, in both pass managers, pass execution is in a fixed order and UnrollAndJamPass will not execute again after LoopDistribute. We hope to fix this in the future by allowing pass managers to run passes until a fixpoint is reached, use Polly to perform these transformations, or add a loop transformation pass which takes the order issue into account.
For mandatory/forced transformations (e.g. by having been declared by #pragma omp simd), the user must be notified when a transformation could not be performed. It is not possible that the responsible pass emits such a warning because the transformation might be 'hidden' in a followup attribute when it is executed, or it is not present in the pipeline at all. For this reason, this patche introduces a WarnMissedTransformations pass, to warn about orphaned transformations.
Since this changes the user-visible diagnostic message when a transformation is applied, two test cases in the clang repository need to be updated.
To ensure that no other transformation is executed before the intended one, the attribute `llvm.loop.disable_nonforced` can be added which should disable transformation heuristics before the intended transformation is applied. E.g. it would be surprising if a loop is distributed before a #pragma unroll_and_jam is applied.
With more supported code transformations (loop fusion, interchange, stripmining, offloading, etc.), transformations can be used as building blocks for more complex transformations (e.g. stripmining+stripmining+interchange -> tiling).
Reviewed By: hfinkel, dmgreen
Differential Revision: https://reviews.llvm.org/D49281
Differential Revision: https://reviews.llvm.org/D55288
llvm-svn: 348944
2018-12-12 18:32:52 +01:00
|
|
|
// Save loop properties before it is transformed.
|
|
|
|
MDNode *OrigLoopID = L->getLoopID();
|
|
|
|
|
2008-05-14 02:24:14 +02:00
|
|
|
// Unroll the loop.
|
[Unroll/UnrollAndJam/Vectorizer/Distribute] Add followup loop attributes.
When multiple loop transformation are defined in a loop's metadata, their order of execution is defined by the order of their respective passes in the pass pipeline. For instance, e.g.
#pragma clang loop unroll_and_jam(enable)
#pragma clang loop distribute(enable)
is the same as
#pragma clang loop distribute(enable)
#pragma clang loop unroll_and_jam(enable)
and will try to loop-distribute before Unroll-And-Jam because the LoopDistribute pass is scheduled after UnrollAndJam pass. UnrollAndJamPass only supports one inner loop, i.e. it will necessarily fail after loop distribution. It is not possible to specify another execution order. Also,t the order of passes in the pipeline is subject to change between versions of LLVM, optimization options and which pass manager is used.
This patch adds 'followup' attributes to various loop transformation passes. These attributes define which attributes the resulting loop of a transformation should have. For instance,
!0 = !{!0, !1, !2}
!1 = !{!"llvm.loop.unroll_and_jam.enable"}
!2 = !{!"llvm.loop.unroll_and_jam.followup_inner", !3}
!3 = !{!"llvm.loop.distribute.enable"}
defines a loop ID (!0) to be unrolled-and-jammed (!1) and then the attribute !3 to be added to the jammed inner loop, which contains the instruction to distribute the inner loop.
Currently, in both pass managers, pass execution is in a fixed order and UnrollAndJamPass will not execute again after LoopDistribute. We hope to fix this in the future by allowing pass managers to run passes until a fixpoint is reached, use Polly to perform these transformations, or add a loop transformation pass which takes the order issue into account.
For mandatory/forced transformations (e.g. by having been declared by #pragma omp simd), the user must be notified when a transformation could not be performed. It is not possible that the responsible pass emits such a warning because the transformation might be 'hidden' in a followup attribute when it is executed, or it is not present in the pipeline at all. For this reason, this patche introduces a WarnMissedTransformations pass, to warn about orphaned transformations.
Since this changes the user-visible diagnostic message when a transformation is applied, two test cases in the clang repository need to be updated.
To ensure that no other transformation is executed before the intended one, the attribute `llvm.loop.disable_nonforced` can be added which should disable transformation heuristics before the intended transformation is applied. E.g. it would be surprising if a loop is distributed before a #pragma unroll_and_jam is applied.
With more supported code transformations (loop fusion, interchange, stripmining, offloading, etc.), transformations can be used as building blocks for more complex transformations (e.g. stripmining+stripmining+interchange -> tiling).
Reviewed By: hfinkel, dmgreen
Differential Revision: https://reviews.llvm.org/D49281
Differential Revision: https://reviews.llvm.org/D55288
llvm-svn: 348944
2018-12-12 18:32:52 +01:00
|
|
|
Loop *RemainderLoop = nullptr;
|
2017-09-27 23:45:22 +02:00
|
|
|
LoopUnrollResult UnrollResult = UnrollLoop(
|
2019-04-19 01:43:49 +02:00
|
|
|
L,
|
2021-06-17 21:49:29 +02:00
|
|
|
{UP.Count, UP.Force, UP.Runtime, UP.AllowExpensiveTripCount,
|
|
|
|
UP.UnrollRemainder, ForgetAllSCEV},
|
2020-02-25 19:50:55 +01:00
|
|
|
LI, &SE, &DT, &AC, &TTI, &ORE, PreserveLCSSA, &RemainderLoop);
|
2017-09-27 23:45:22 +02:00
|
|
|
if (UnrollResult == LoopUnrollResult::Unmodified)
|
|
|
|
return LoopUnrollResult::Unmodified;
|
2004-04-18 07:20:17 +02:00
|
|
|
|
[Unroll/UnrollAndJam/Vectorizer/Distribute] Add followup loop attributes.
When multiple loop transformation are defined in a loop's metadata, their order of execution is defined by the order of their respective passes in the pass pipeline. For instance, e.g.
#pragma clang loop unroll_and_jam(enable)
#pragma clang loop distribute(enable)
is the same as
#pragma clang loop distribute(enable)
#pragma clang loop unroll_and_jam(enable)
and will try to loop-distribute before Unroll-And-Jam because the LoopDistribute pass is scheduled after UnrollAndJam pass. UnrollAndJamPass only supports one inner loop, i.e. it will necessarily fail after loop distribution. It is not possible to specify another execution order. Also,t the order of passes in the pipeline is subject to change between versions of LLVM, optimization options and which pass manager is used.
This patch adds 'followup' attributes to various loop transformation passes. These attributes define which attributes the resulting loop of a transformation should have. For instance,
!0 = !{!0, !1, !2}
!1 = !{!"llvm.loop.unroll_and_jam.enable"}
!2 = !{!"llvm.loop.unroll_and_jam.followup_inner", !3}
!3 = !{!"llvm.loop.distribute.enable"}
defines a loop ID (!0) to be unrolled-and-jammed (!1) and then the attribute !3 to be added to the jammed inner loop, which contains the instruction to distribute the inner loop.
Currently, in both pass managers, pass execution is in a fixed order and UnrollAndJamPass will not execute again after LoopDistribute. We hope to fix this in the future by allowing pass managers to run passes until a fixpoint is reached, use Polly to perform these transformations, or add a loop transformation pass which takes the order issue into account.
For mandatory/forced transformations (e.g. by having been declared by #pragma omp simd), the user must be notified when a transformation could not be performed. It is not possible that the responsible pass emits such a warning because the transformation might be 'hidden' in a followup attribute when it is executed, or it is not present in the pipeline at all. For this reason, this patche introduces a WarnMissedTransformations pass, to warn about orphaned transformations.
Since this changes the user-visible diagnostic message when a transformation is applied, two test cases in the clang repository need to be updated.
To ensure that no other transformation is executed before the intended one, the attribute `llvm.loop.disable_nonforced` can be added which should disable transformation heuristics before the intended transformation is applied. E.g. it would be surprising if a loop is distributed before a #pragma unroll_and_jam is applied.
With more supported code transformations (loop fusion, interchange, stripmining, offloading, etc.), transformations can be used as building blocks for more complex transformations (e.g. stripmining+stripmining+interchange -> tiling).
Reviewed By: hfinkel, dmgreen
Differential Revision: https://reviews.llvm.org/D49281
Differential Revision: https://reviews.llvm.org/D55288
llvm-svn: 348944
2018-12-12 18:32:52 +01:00
|
|
|
if (RemainderLoop) {
|
|
|
|
Optional<MDNode *> RemainderLoopID =
|
|
|
|
makeFollowupLoopID(OrigLoopID, {LLVMLoopUnrollFollowupAll,
|
|
|
|
LLVMLoopUnrollFollowupRemainder});
|
|
|
|
if (RemainderLoopID.hasValue())
|
|
|
|
RemainderLoop->setLoopID(RemainderLoopID.getValue());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (UnrollResult != LoopUnrollResult::FullyUnrolled) {
|
|
|
|
Optional<MDNode *> NewLoopID =
|
|
|
|
makeFollowupLoopID(OrigLoopID, {LLVMLoopUnrollFollowupAll,
|
|
|
|
LLVMLoopUnrollFollowupUnrolled});
|
|
|
|
if (NewLoopID.hasValue()) {
|
|
|
|
L->setLoopID(NewLoopID.getValue());
|
|
|
|
|
|
|
|
// Do not setLoopAlreadyUnrolled if loop attributes have been specified
|
|
|
|
// explicitly.
|
|
|
|
return UnrollResult;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-28 01:15:06 +02:00
|
|
|
// If loop has an unroll count pragma or unrolled by explicitly set count
|
|
|
|
// mark loop as unrolled to prevent unrolling beyond that requested.
|
2021-05-29 18:33:31 +02:00
|
|
|
if (UnrollResult != LoopUnrollResult::FullyUnrolled && IsCountSetExplicitly)
|
2017-10-15 09:31:02 +02:00
|
|
|
L->setLoopAlreadyUnrolled();
|
2016-11-30 22:13:57 +01:00
|
|
|
|
2017-09-27 23:45:22 +02:00
|
|
|
return UnrollResult;
|
2004-04-18 07:20:17 +02:00
|
|
|
}
|
2016-01-12 06:21:37 +01:00
|
|
|
|
|
|
|
namespace {
|
2017-10-18 23:46:47 +02:00
|
|
|
|
2016-01-12 06:21:37 +01:00
|
|
|
class LoopUnroll : public LoopPass {
|
|
|
|
public:
|
|
|
|
static char ID; // Pass ID, replacement for typeid
|
2017-10-18 23:46:47 +02:00
|
|
|
|
|
|
|
int OptLevel;
|
2018-12-18 18:16:05 +01:00
|
|
|
|
|
|
|
/// If false, use a cost model to determine whether unrolling of a loop is
|
|
|
|
/// profitable. If true, only loops that explicitly request unrolling via
|
|
|
|
/// metadata are considered. All other loops are skipped.
|
|
|
|
bool OnlyWhenForced;
|
|
|
|
|
2019-04-12 21:16:07 +02:00
|
|
|
/// If false, when SCEV is invalidated, only forget everything in the
|
|
|
|
/// top-most loop (call forgetTopMostLoop), of the loop being processed.
|
|
|
|
/// Otherwise, forgetAllLoops and rebuild when needed next.
|
|
|
|
bool ForgetAllSCEV;
|
|
|
|
|
2017-10-18 23:46:47 +02:00
|
|
|
Optional<unsigned> ProvidedCount;
|
|
|
|
Optional<unsigned> ProvidedThreshold;
|
|
|
|
Optional<bool> ProvidedAllowPartial;
|
|
|
|
Optional<bool> ProvidedRuntime;
|
|
|
|
Optional<bool> ProvidedUpperBound;
|
|
|
|
Optional<bool> ProvidedAllowPeeling;
|
2019-08-02 11:32:52 +02:00
|
|
|
Optional<bool> ProvidedAllowProfileBasedPeeling;
|
2019-09-19 08:57:29 +02:00
|
|
|
Optional<unsigned> ProvidedFullUnrollMaxCount;
|
2017-10-18 23:46:47 +02:00
|
|
|
|
2018-12-18 18:16:05 +01:00
|
|
|
LoopUnroll(int OptLevel = 2, bool OnlyWhenForced = false,
|
2019-04-12 21:16:07 +02:00
|
|
|
bool ForgetAllSCEV = false, Optional<unsigned> Threshold = None,
|
2016-01-12 06:21:37 +01:00
|
|
|
Optional<unsigned> Count = None,
|
2016-10-12 23:29:38 +02:00
|
|
|
Optional<bool> AllowPartial = None, Optional<bool> Runtime = None,
|
2017-08-03 19:52:38 +02:00
|
|
|
Optional<bool> UpperBound = None,
|
2019-08-02 11:32:52 +02:00
|
|
|
Optional<bool> AllowPeeling = None,
|
2019-09-19 08:57:29 +02:00
|
|
|
Optional<bool> AllowProfileBasedPeeling = None,
|
|
|
|
Optional<unsigned> ProvidedFullUnrollMaxCount = None)
|
2018-12-18 18:16:05 +01:00
|
|
|
: LoopPass(ID), OptLevel(OptLevel), OnlyWhenForced(OnlyWhenForced),
|
2019-04-12 21:16:07 +02:00
|
|
|
ForgetAllSCEV(ForgetAllSCEV), ProvidedCount(std::move(Count)),
|
|
|
|
ProvidedThreshold(Threshold), ProvidedAllowPartial(AllowPartial),
|
|
|
|
ProvidedRuntime(Runtime), ProvidedUpperBound(UpperBound),
|
2019-08-02 11:32:52 +02:00
|
|
|
ProvidedAllowPeeling(AllowPeeling),
|
2019-09-19 08:57:29 +02:00
|
|
|
ProvidedAllowProfileBasedPeeling(AllowProfileBasedPeeling),
|
|
|
|
ProvidedFullUnrollMaxCount(ProvidedFullUnrollMaxCount) {
|
2016-01-12 06:21:37 +01:00
|
|
|
initializeLoopUnrollPass(*PassRegistry::getPassRegistry());
|
|
|
|
}
|
|
|
|
|
2017-09-28 04:45:42 +02:00
|
|
|
bool runOnLoop(Loop *L, LPPassManager &LPM) override {
|
2016-04-23 00:06:11 +02:00
|
|
|
if (skipLoop(L))
|
2016-01-12 06:21:37 +01:00
|
|
|
return false;
|
|
|
|
|
|
|
|
Function &F = *L->getHeader()->getParent();
|
|
|
|
|
|
|
|
auto &DT = getAnalysis<DominatorTreeWrapperPass>().getDomTree();
|
|
|
|
LoopInfo *LI = &getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
|
[LoopUnroll] Pass SCEV to getUnrollingPreferences hook. NFCI.
Reviewers: sanjoy, anna, reames, apilipenko, igor-laevsky, mkuper
Subscribers: jholewinski, arsenm, mzolotukhin, nemanjai, nhaehnle, javed.absar, mcrosier, llvm-commits
Differential Revision: https://reviews.llvm.org/D34531
llvm-svn: 306554
2017-06-28 17:53:17 +02:00
|
|
|
ScalarEvolution &SE = getAnalysis<ScalarEvolutionWrapperPass>().getSE();
|
2016-01-12 06:21:37 +01:00
|
|
|
const TargetTransformInfo &TTI =
|
|
|
|
getAnalysis<TargetTransformInfoWrapperPass>().getTTI(F);
|
2016-12-19 09:22:17 +01:00
|
|
|
auto &AC = getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F);
|
2016-08-26 17:58:34 +02:00
|
|
|
// For the old PM, we can't use OptimizationRemarkEmitter as an analysis
|
|
|
|
// pass. Function analyses need to be preserved across loop transformations
|
|
|
|
// but ORE cannot be preserved (see comment before the pass definition).
|
|
|
|
OptimizationRemarkEmitter ORE(&F);
|
2016-01-12 06:21:37 +01:00
|
|
|
bool PreserveLCSSA = mustPreserveAnalysisID(LCSSAID);
|
|
|
|
|
2017-09-28 04:45:42 +02:00
|
|
|
LoopUnrollResult Result = tryToUnrollLoop(
|
2019-08-02 11:32:52 +02:00
|
|
|
L, DT, LI, SE, TTI, AC, ORE, nullptr, nullptr, PreserveLCSSA, OptLevel,
|
|
|
|
OnlyWhenForced, ForgetAllSCEV, ProvidedCount, ProvidedThreshold,
|
|
|
|
ProvidedAllowPartial, ProvidedRuntime, ProvidedUpperBound,
|
2019-09-19 08:57:29 +02:00
|
|
|
ProvidedAllowPeeling, ProvidedAllowProfileBasedPeeling,
|
|
|
|
ProvidedFullUnrollMaxCount);
|
2017-09-28 04:45:42 +02:00
|
|
|
|
|
|
|
if (Result == LoopUnrollResult::FullyUnrolled)
|
|
|
|
LPM.markLoopAsDeleted(*L);
|
|
|
|
|
|
|
|
return Result != LoopUnrollResult::Unmodified;
|
2016-01-12 06:21:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// This transformation requires natural loop information & requires that
|
|
|
|
/// loop preheaders be inserted into the CFG...
|
|
|
|
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
2016-12-19 09:22:17 +01:00
|
|
|
AU.addRequired<AssumptionCacheTracker>();
|
2016-01-12 06:21:37 +01:00
|
|
|
AU.addRequired<TargetTransformInfoWrapperPass>();
|
[LPM] Factor all of the loop analysis usage updates into a common helper
routine.
We were getting this wrong in small ways and generally being very
inconsistent about it across loop passes. Instead, let's have a common
place where we do this. One minor downside is that this will require
some analyses like SCEV in more places than they are strictly needed.
However, this seems benign as these analyses are complete no-ops, and
without this consistency we can in many cases end up with the legacy
pass manager scheduling deciding to split up a loop pass pipeline in
order to run the function analysis half-way through. It is very, very
annoying to fix these without just being very pedantic across the board.
The only loop passes I've not updated here are ones that use
AU.setPreservesAll() such as IVUsers (an analysis) and the pass printer.
They seemed less relevant.
With this patch, almost all of the problems in PR24804 around loop pass
pipelines are fixed. The one remaining issue is that we run simplify-cfg
and instcombine in the middle of the loop pass pipeline. We've recently
added some loop variants of these passes that would seem substantially
cleaner to use, but this at least gets us much closer to the previous
state. Notably, the seven loop pass managers is down to three.
I've not updated the loop passes using LoopAccessAnalysis because that
analysis hasn't been fully wired into LoopSimplify/LCSSA, and it isn't
clear that those transforms want to support those forms anyways. They
all run late anyways, so this is harmless. Similarly, LSR is left alone
because it already carefully manages its forms and doesn't need to get
fused into a single loop pass manager with a bunch of other loop passes.
LoopReroll didn't use loop simplified form previously, and I've updated
the test case to match the trivially different output.
Finally, I've also factored all the pass initialization for the passes
that use this technique as well, so that should be done regularly and
reliably.
Thanks to James for the help reviewing and thinking about this stuff,
and Ben for help thinking about it as well!
Differential Revision: http://reviews.llvm.org/D17435
llvm-svn: 261316
2016-02-19 11:45:18 +01:00
|
|
|
// FIXME: Loop passes are required to preserve domtree, and for now we just
|
|
|
|
// recreate dom info if anything gets unrolled.
|
|
|
|
getLoopAnalysisUsage(AU);
|
2016-01-12 06:21:37 +01:00
|
|
|
}
|
|
|
|
};
|
2017-10-18 23:46:47 +02:00
|
|
|
|
|
|
|
} // end anonymous namespace
|
2016-01-12 06:21:37 +01:00
|
|
|
|
|
|
|
char LoopUnroll::ID = 0;
|
2017-10-18 23:46:47 +02:00
|
|
|
|
2016-01-12 06:21:37 +01:00
|
|
|
INITIALIZE_PASS_BEGIN(LoopUnroll, "loop-unroll", "Unroll loops", false, false)
|
2016-12-19 09:22:17 +01:00
|
|
|
INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker)
|
[LPM] Factor all of the loop analysis usage updates into a common helper
routine.
We were getting this wrong in small ways and generally being very
inconsistent about it across loop passes. Instead, let's have a common
place where we do this. One minor downside is that this will require
some analyses like SCEV in more places than they are strictly needed.
However, this seems benign as these analyses are complete no-ops, and
without this consistency we can in many cases end up with the legacy
pass manager scheduling deciding to split up a loop pass pipeline in
order to run the function analysis half-way through. It is very, very
annoying to fix these without just being very pedantic across the board.
The only loop passes I've not updated here are ones that use
AU.setPreservesAll() such as IVUsers (an analysis) and the pass printer.
They seemed less relevant.
With this patch, almost all of the problems in PR24804 around loop pass
pipelines are fixed. The one remaining issue is that we run simplify-cfg
and instcombine in the middle of the loop pass pipeline. We've recently
added some loop variants of these passes that would seem substantially
cleaner to use, but this at least gets us much closer to the previous
state. Notably, the seven loop pass managers is down to three.
I've not updated the loop passes using LoopAccessAnalysis because that
analysis hasn't been fully wired into LoopSimplify/LCSSA, and it isn't
clear that those transforms want to support those forms anyways. They
all run late anyways, so this is harmless. Similarly, LSR is left alone
because it already carefully manages its forms and doesn't need to get
fused into a single loop pass manager with a bunch of other loop passes.
LoopReroll didn't use loop simplified form previously, and I've updated
the test case to match the trivially different output.
Finally, I've also factored all the pass initialization for the passes
that use this technique as well, so that should be done regularly and
reliably.
Thanks to James for the help reviewing and thinking about this stuff,
and Ben for help thinking about it as well!
Differential Revision: http://reviews.llvm.org/D17435
llvm-svn: 261316
2016-02-19 11:45:18 +01:00
|
|
|
INITIALIZE_PASS_DEPENDENCY(LoopPass)
|
|
|
|
INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
|
2016-01-12 06:21:37 +01:00
|
|
|
INITIALIZE_PASS_END(LoopUnroll, "loop-unroll", "Unroll loops", false, false)
|
|
|
|
|
2018-12-18 18:16:05 +01:00
|
|
|
Pass *llvm::createLoopUnrollPass(int OptLevel, bool OnlyWhenForced,
|
2019-04-12 21:16:07 +02:00
|
|
|
bool ForgetAllSCEV, int Threshold, int Count,
|
|
|
|
int AllowPartial, int Runtime, int UpperBound,
|
2017-08-03 19:52:38 +02:00
|
|
|
int AllowPeeling) {
|
2016-01-12 06:21:37 +01:00
|
|
|
// TODO: It would make more sense for this function to take the optionals
|
|
|
|
// directly, but that's dangerous since it would silently break out of tree
|
|
|
|
// callers.
|
Increases full-unroll threshold.
Summary:
The default threshold for fully unroll is too conservative. This patch doubles the full-unroll threshold
This change will affect the following speccpu2006 benchmarks (performance numbers were collected from Intel Sandybridge):
Performance:
403 0.11%
433 0.51%
445 0.48%
447 3.50%
453 1.49%
464 0.75%
Code size:
403 0.56%
433 0.96%
445 2.16%
447 2.96%
453 0.94%
464 8.02%
The compiler time overhead is similar with code size.
Reviewers: davidxl, mkuper, mzolotukhin, hfinkel, chandlerc
Reviewed By: hfinkel, chandlerc
Subscribers: mehdi_amini, zzheng, efriedma, haicheng, hfinkel, llvm-commits
Differential Revision: https://reviews.llvm.org/D28368
llvm-svn: 295538
2017-02-18 04:46:51 +01:00
|
|
|
return new LoopUnroll(
|
2019-04-12 21:16:07 +02:00
|
|
|
OptLevel, OnlyWhenForced, ForgetAllSCEV,
|
2018-12-18 18:16:05 +01:00
|
|
|
Threshold == -1 ? None : Optional<unsigned>(Threshold),
|
Increases full-unroll threshold.
Summary:
The default threshold for fully unroll is too conservative. This patch doubles the full-unroll threshold
This change will affect the following speccpu2006 benchmarks (performance numbers were collected from Intel Sandybridge):
Performance:
403 0.11%
433 0.51%
445 0.48%
447 3.50%
453 1.49%
464 0.75%
Code size:
403 0.56%
433 0.96%
445 2.16%
447 2.96%
453 0.94%
464 8.02%
The compiler time overhead is similar with code size.
Reviewers: davidxl, mkuper, mzolotukhin, hfinkel, chandlerc
Reviewed By: hfinkel, chandlerc
Subscribers: mehdi_amini, zzheng, efriedma, haicheng, hfinkel, llvm-commits
Differential Revision: https://reviews.llvm.org/D28368
llvm-svn: 295538
2017-02-18 04:46:51 +01:00
|
|
|
Count == -1 ? None : Optional<unsigned>(Count),
|
|
|
|
AllowPartial == -1 ? None : Optional<bool>(AllowPartial),
|
|
|
|
Runtime == -1 ? None : Optional<bool>(Runtime),
|
2017-08-03 19:52:38 +02:00
|
|
|
UpperBound == -1 ? None : Optional<bool>(UpperBound),
|
|
|
|
AllowPeeling == -1 ? None : Optional<bool>(AllowPeeling));
|
2016-01-12 06:21:37 +01:00
|
|
|
}
|
|
|
|
|
2019-04-12 21:16:07 +02:00
|
|
|
Pass *llvm::createSimpleLoopUnrollPass(int OptLevel, bool OnlyWhenForced,
|
|
|
|
bool ForgetAllSCEV) {
|
|
|
|
return createLoopUnrollPass(OptLevel, OnlyWhenForced, ForgetAllSCEV, -1, -1,
|
2021-01-26 14:43:39 +01:00
|
|
|
0, 0, 0, 1);
|
2016-01-12 06:21:37 +01:00
|
|
|
}
|
2016-07-20 01:54:23 +02:00
|
|
|
|
2017-08-02 22:35:29 +02:00
|
|
|
PreservedAnalyses LoopFullUnrollPass::run(Loop &L, LoopAnalysisManager &AM,
|
|
|
|
LoopStandardAnalysisResults &AR,
|
|
|
|
LPMUpdater &Updater) {
|
Compute ORE, BPI, BFI in Loop passes.
Summary:
Passes ORE, BPI, BFI are not being preserved by Loop passes, hence it
is incorrect to retrieve these passes as cached.
This patch makes the loop passes in question compute a new instance.
In some of these cases, however, it may be beneficial to change the Loop pass to
a Function pass instead, similar to the change for LoopUnrollAndJam.
Reviewers: chandlerc, dmgreen, jdoerfert, reames
Subscribers: mehdi_amini, hiraditya, zzheng, steven_wu, dexonsmith, Whitney, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D72891
2020-01-17 00:32:30 +01:00
|
|
|
// For the new PM, we can't use OptimizationRemarkEmitter as an analysis
|
|
|
|
// pass. Function analyses need to be preserved across loop transformations
|
|
|
|
// but ORE cannot be preserved (see comment before the pass definition).
|
|
|
|
OptimizationRemarkEmitter ORE(L.getHeader()->getParent());
|
2016-07-20 01:54:23 +02:00
|
|
|
|
2017-01-25 03:49:01 +01:00
|
|
|
// Keep track of the previous loop structure so we can identify new loops
|
|
|
|
// created by unrolling.
|
|
|
|
Loop *ParentL = L.getParentLoop();
|
|
|
|
SmallPtrSet<Loop *, 4> OldLoops;
|
|
|
|
if (ParentL)
|
|
|
|
OldLoops.insert(ParentL->begin(), ParentL->end());
|
|
|
|
else
|
|
|
|
OldLoops.insert(AR.LI.begin(), AR.LI.end());
|
|
|
|
|
2020-01-28 20:23:46 +01:00
|
|
|
std::string LoopName = std::string(L.getName());
|
2017-09-28 04:45:42 +02:00
|
|
|
|
Compute ORE, BPI, BFI in Loop passes.
Summary:
Passes ORE, BPI, BFI are not being preserved by Loop passes, hence it
is incorrect to retrieve these passes as cached.
This patch makes the loop passes in question compute a new instance.
In some of these cases, however, it may be beneficial to change the Loop pass to
a Function pass instead, similar to the change for LoopUnrollAndJam.
Reviewers: chandlerc, dmgreen, jdoerfert, reames
Subscribers: mehdi_amini, hiraditya, zzheng, steven_wu, dexonsmith, Whitney, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D72891
2020-01-17 00:32:30 +01:00
|
|
|
bool Changed = tryToUnrollLoop(&L, AR.DT, &AR.LI, AR.SE, AR.TTI, AR.AC, ORE,
|
2019-08-02 11:32:52 +02:00
|
|
|
/*BFI*/ nullptr, /*PSI*/ nullptr,
|
|
|
|
/*PreserveLCSSA*/ true, OptLevel,
|
|
|
|
OnlyWhenForced, ForgetSCEV, /*Count*/ None,
|
|
|
|
/*Threshold*/ None, /*AllowPartial*/ false,
|
|
|
|
/*Runtime*/ false, /*UpperBound*/ false,
|
2021-01-26 14:43:39 +01:00
|
|
|
/*AllowPeeling*/ true,
|
2019-09-19 08:57:29 +02:00
|
|
|
/*AllowProfileBasedPeeling*/ false,
|
|
|
|
/*FullUnrollMaxCount*/ None) !=
|
2019-08-02 11:32:52 +02:00
|
|
|
LoopUnrollResult::Unmodified;
|
2016-07-20 01:54:23 +02:00
|
|
|
if (!Changed)
|
|
|
|
return PreservedAnalyses::all();
|
2017-01-15 07:32:49 +01:00
|
|
|
|
2017-01-25 03:49:01 +01:00
|
|
|
// The parent must not be damaged by unrolling!
|
|
|
|
#ifndef NDEBUG
|
|
|
|
if (ParentL)
|
|
|
|
ParentL->verifyLoop();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Unrolling can do several things to introduce new loops into a loop nest:
|
|
|
|
// - Full unrolling clones child loops within the current loop but then
|
|
|
|
// removes the current loop making all of the children appear to be new
|
|
|
|
// sibling loops.
|
|
|
|
//
|
2017-08-02 22:35:29 +02:00
|
|
|
// When a new loop appears as a sibling loop after fully unrolling,
|
|
|
|
// its nesting structure has fundamentally changed and we want to revisit
|
|
|
|
// it to reflect that.
|
2017-01-25 03:49:01 +01:00
|
|
|
//
|
|
|
|
// When unrolling has removed the current loop, we need to tell the
|
|
|
|
// infrastructure that it is gone.
|
|
|
|
//
|
|
|
|
// Finally, we support a debugging/testing mode where we revisit child loops
|
|
|
|
// as well. These are not expected to require further optimizations as either
|
|
|
|
// they or the loop they were cloned from have been directly visited already.
|
|
|
|
// But the debugging mode allows us to check this assumption.
|
|
|
|
bool IsCurrentLoopValid = false;
|
|
|
|
SmallVector<Loop *, 4> SibLoops;
|
|
|
|
if (ParentL)
|
|
|
|
SibLoops.append(ParentL->begin(), ParentL->end());
|
|
|
|
else
|
|
|
|
SibLoops.append(AR.LI.begin(), AR.LI.end());
|
|
|
|
erase_if(SibLoops, [&](Loop *SibLoop) {
|
|
|
|
if (SibLoop == &L) {
|
|
|
|
IsCurrentLoopValid = true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise erase the loop from the list if it was in the old loops.
|
2021-01-08 05:29:34 +01:00
|
|
|
return OldLoops.contains(SibLoop);
|
2017-01-25 03:49:01 +01:00
|
|
|
});
|
|
|
|
Updater.addSiblingLoops(SibLoops);
|
|
|
|
|
|
|
|
if (!IsCurrentLoopValid) {
|
2017-09-28 04:45:42 +02:00
|
|
|
Updater.markLoopAsDeleted(L, LoopName);
|
2017-01-25 03:49:01 +01:00
|
|
|
} else {
|
|
|
|
// We can only walk child loops if the current loop remained valid.
|
|
|
|
if (UnrollRevisitChildLoops) {
|
2017-08-02 22:35:29 +02:00
|
|
|
// Walk *all* of the child loops.
|
2017-01-25 03:49:01 +01:00
|
|
|
SmallVector<Loop *, 4> ChildLoops(L.begin(), L.end());
|
|
|
|
Updater.addChildLoops(ChildLoops);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-20 01:54:23 +02:00
|
|
|
return getLoopPassPreservedAnalyses();
|
|
|
|
}
|
2017-08-02 22:35:29 +02:00
|
|
|
|
|
|
|
PreservedAnalyses LoopUnrollPass::run(Function &F,
|
|
|
|
FunctionAnalysisManager &AM) {
|
|
|
|
auto &SE = AM.getResult<ScalarEvolutionAnalysis>(F);
|
|
|
|
auto &LI = AM.getResult<LoopAnalysis>(F);
|
|
|
|
auto &TTI = AM.getResult<TargetIRAnalysis>(F);
|
|
|
|
auto &DT = AM.getResult<DominatorTreeAnalysis>(F);
|
|
|
|
auto &AC = AM.getResult<AssumptionAnalysis>(F);
|
|
|
|
auto &ORE = AM.getResult<OptimizationRemarkEmitterAnalysis>(F);
|
|
|
|
|
2017-08-08 04:24:20 +02:00
|
|
|
LoopAnalysisManager *LAM = nullptr;
|
|
|
|
if (auto *LAMProxy = AM.getCachedResult<LoopAnalysisManagerFunctionProxy>(F))
|
|
|
|
LAM = &LAMProxy->getManager();
|
|
|
|
|
2020-01-14 19:27:20 +01:00
|
|
|
auto &MAMProxy = AM.getResult<ModuleAnalysisManagerFunctionProxy>(F);
|
2017-08-04 01:42:58 +02:00
|
|
|
ProfileSummaryInfo *PSI =
|
2020-01-14 19:27:20 +01:00
|
|
|
MAMProxy.getCachedResult<ProfileSummaryAnalysis>(*F.getParent());
|
2019-04-15 18:49:00 +02:00
|
|
|
auto *BFI = (PSI && PSI->hasProfileSummary()) ?
|
|
|
|
&AM.getResult<BlockFrequencyAnalysis>(F) : nullptr;
|
2017-08-04 01:42:58 +02:00
|
|
|
|
2017-08-02 22:35:29 +02:00
|
|
|
bool Changed = false;
|
|
|
|
|
|
|
|
// The unroller requires loops to be in simplified form, and also needs LCSSA.
|
|
|
|
// Since simplification may add new inner loops, it has to run before the
|
|
|
|
// legality and profitability checks. This means running the loop unroller
|
|
|
|
// will simplify all loops, regardless of whether anything end up being
|
|
|
|
// unrolled.
|
|
|
|
for (auto &L : LI) {
|
[MemorySSA] Teach LoopSimplify to preserve MemorySSA.
Summary:
Preserve MemorySSA in LoopSimplify, in the old pass manager, if the analysis is available.
Do not preserve it in the new pass manager.
Update tests.
Subscribers: nemanjai, jlebar, javed.absar, Prazek, kbarton, zzheng, jsji, llvm-commits, george.burgess.iv, chandlerc
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D60833
llvm-svn: 360270
2019-05-08 19:05:36 +02:00
|
|
|
Changed |=
|
|
|
|
simplifyLoop(L, &DT, &LI, &SE, &AC, nullptr, false /* PreserveLCSSA */);
|
2017-08-02 22:35:29 +02:00
|
|
|
Changed |= formLCSSARecursively(*L, DT, &LI, &SE);
|
|
|
|
}
|
|
|
|
|
2020-01-27 22:36:41 +01:00
|
|
|
// Add the loop nests in the reverse order of LoopInfo. See method
|
|
|
|
// declaration.
|
|
|
|
SmallPriorityWorklist<Loop *, 4> Worklist;
|
|
|
|
appendLoopsToWorklist(LI, Worklist);
|
2017-08-02 22:35:29 +02:00
|
|
|
|
|
|
|
while (!Worklist.empty()) {
|
|
|
|
// Because the LoopInfo stores the loops in RPO, we walk the worklist
|
|
|
|
// from back to front so that we work forward across the CFG, which
|
|
|
|
// for unrolling is only needed to get optimization remarks emitted in
|
|
|
|
// a forward order.
|
|
|
|
Loop &L = *Worklist.pop_back_val();
|
2017-09-28 16:47:39 +02:00
|
|
|
#ifndef NDEBUG
|
|
|
|
Loop *ParentL = L.getParentLoop();
|
|
|
|
#endif
|
2017-08-02 22:35:29 +02:00
|
|
|
|
2017-08-04 01:42:58 +02:00
|
|
|
// Check if the profile summary indicates that the profiled application
|
|
|
|
// has a huge working set size, in which case we disable peeling to avoid
|
|
|
|
// bloating it further.
|
2018-10-31 15:33:14 +01:00
|
|
|
Optional<bool> LocalAllowPeeling = UnrollOpts.AllowPeeling;
|
2017-08-04 01:42:58 +02:00
|
|
|
if (PSI && PSI->hasHugeWorkingSetSize())
|
2018-10-31 15:33:14 +01:00
|
|
|
LocalAllowPeeling = false;
|
2020-01-28 20:23:46 +01:00
|
|
|
std::string LoopName = std::string(L.getName());
|
2018-10-31 15:33:14 +01:00
|
|
|
// The API here is quite complex to call and we allow to select some
|
|
|
|
// flavors of unrolling during construction time (by setting UnrollOpts).
|
|
|
|
LoopUnrollResult Result = tryToUnrollLoop(
|
2019-04-15 18:49:00 +02:00
|
|
|
&L, DT, &LI, SE, TTI, AC, ORE, BFI, PSI,
|
2018-12-18 18:16:05 +01:00
|
|
|
/*PreserveLCSSA*/ true, UnrollOpts.OptLevel, UnrollOpts.OnlyWhenForced,
|
2019-05-23 23:52:59 +02:00
|
|
|
UnrollOpts.ForgetSCEV, /*Count*/ None,
|
2018-10-31 15:33:14 +01:00
|
|
|
/*Threshold*/ None, UnrollOpts.AllowPartial, UnrollOpts.AllowRuntime,
|
2019-08-02 11:32:52 +02:00
|
|
|
UnrollOpts.AllowUpperBound, LocalAllowPeeling,
|
2019-09-19 08:57:29 +02:00
|
|
|
UnrollOpts.AllowProfileBasedPeeling, UnrollOpts.FullUnrollMaxCount);
|
2017-09-27 23:45:22 +02:00
|
|
|
Changed |= Result != LoopUnrollResult::Unmodified;
|
2017-08-02 22:35:29 +02:00
|
|
|
|
|
|
|
// The parent must not be damaged by unrolling!
|
|
|
|
#ifndef NDEBUG
|
2017-09-27 23:45:22 +02:00
|
|
|
if (Result != LoopUnrollResult::Unmodified && ParentL)
|
2017-08-02 22:35:29 +02:00
|
|
|
ParentL->verifyLoop();
|
|
|
|
#endif
|
2017-08-08 04:24:20 +02:00
|
|
|
|
2017-09-27 23:45:22 +02:00
|
|
|
// Clear any cached analysis results for L if we removed it completely.
|
|
|
|
if (LAM && Result == LoopUnrollResult::FullyUnrolled)
|
2017-09-28 04:45:42 +02:00
|
|
|
LAM->clear(L, LoopName);
|
2017-08-02 22:35:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!Changed)
|
|
|
|
return PreservedAnalyses::all();
|
|
|
|
|
|
|
|
return getLoopPassPreservedAnalyses();
|
|
|
|
}
|