/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/tools/polly/lib/CodeGen/IslAst.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===- IslAst.cpp - isl code generator interface --------------------------===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // The isl code generator interface takes a Scop and generates an isl_ast. This |
10 | | // ist_ast can either be returned directly or it can be pretty printed to |
11 | | // stdout. |
12 | | // |
13 | | // A typical isl_ast output looks like this: |
14 | | // |
15 | | // for (c2 = max(0, ceild(n + m, 2); c2 <= min(511, floord(5 * n, 3)); c2++) { |
16 | | // bb2(c2); |
17 | | // } |
18 | | // |
19 | | // An in-depth discussion of our AST generation approach can be found in: |
20 | | // |
21 | | // Polyhedral AST generation is more than scanning polyhedra |
22 | | // Tobias Grosser, Sven Verdoolaege, Albert Cohen |
23 | | // ACM Transactions on Programming Languages and Systems (TOPLAS), |
24 | | // 37(4), July 2015 |
25 | | // http://www.grosser.es/#pub-polyhedral-AST-generation |
26 | | // |
27 | | //===----------------------------------------------------------------------===// |
28 | | |
29 | | #include "polly/CodeGen/IslAst.h" |
30 | | #include "polly/CodeGen/CodeGeneration.h" |
31 | | #include "polly/DependenceInfo.h" |
32 | | #include "polly/LinkAllPasses.h" |
33 | | #include "polly/Options.h" |
34 | | #include "polly/ScopDetection.h" |
35 | | #include "polly/ScopInfo.h" |
36 | | #include "polly/ScopPass.h" |
37 | | #include "polly/Support/GICHelper.h" |
38 | | #include "llvm/ADT/Statistic.h" |
39 | | #include "llvm/IR/Function.h" |
40 | | #include "llvm/Support/Debug.h" |
41 | | #include "llvm/Support/raw_ostream.h" |
42 | | #include "isl/aff.h" |
43 | | #include "isl/ast.h" |
44 | | #include "isl/ast_build.h" |
45 | | #include "isl/id.h" |
46 | | #include "isl/isl-noexceptions.h" |
47 | | #include "isl/printer.h" |
48 | | #include "isl/schedule.h" |
49 | | #include "isl/set.h" |
50 | | #include "isl/union_map.h" |
51 | | #include "isl/val.h" |
52 | | #include <cassert> |
53 | | #include <cstdlib> |
54 | | |
55 | | #define DEBUG_TYPE "polly-ast" |
56 | | |
57 | | using namespace llvm; |
58 | | using namespace polly; |
59 | | |
60 | | using IslAstUserPayload = IslAstInfo::IslAstUserPayload; |
61 | | |
62 | | static cl::opt<bool> |
63 | | PollyParallel("polly-parallel", |
64 | | cl::desc("Generate thread parallel code (isl codegen only)"), |
65 | | cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory)); |
66 | | |
67 | | static cl::opt<bool> PrintAccesses("polly-ast-print-accesses", |
68 | | cl::desc("Print memory access functions"), |
69 | | cl::init(false), cl::ZeroOrMore, |
70 | | cl::cat(PollyCategory)); |
71 | | |
72 | | static cl::opt<bool> PollyParallelForce( |
73 | | "polly-parallel-force", |
74 | | cl::desc( |
75 | | "Force generation of thread parallel code ignoring any cost model"), |
76 | | cl::init(false), cl::ZeroOrMore, cl::cat(PollyCategory)); |
77 | | |
78 | | static cl::opt<bool> UseContext("polly-ast-use-context", |
79 | | cl::desc("Use context"), cl::Hidden, |
80 | | cl::init(true), cl::ZeroOrMore, |
81 | | cl::cat(PollyCategory)); |
82 | | |
83 | | static cl::opt<bool> DetectParallel("polly-ast-detect-parallel", |
84 | | cl::desc("Detect parallelism"), cl::Hidden, |
85 | | cl::init(false), cl::ZeroOrMore, |
86 | | cl::cat(PollyCategory)); |
87 | | |
88 | | STATISTIC(ScopsProcessed, "Number of SCoPs processed"); |
89 | | STATISTIC(ScopsBeneficial, "Number of beneficial SCoPs"); |
90 | | STATISTIC(BeneficialAffineLoops, "Number of beneficial affine loops"); |
91 | | STATISTIC(BeneficialBoxedLoops, "Number of beneficial boxed loops"); |
92 | | |
93 | | STATISTIC(NumForLoops, "Number of for-loops"); |
94 | | STATISTIC(NumParallel, "Number of parallel for-loops"); |
95 | | STATISTIC(NumInnermostParallel, "Number of innermost parallel for-loops"); |
96 | | STATISTIC(NumOutermostParallel, "Number of outermost parallel for-loops"); |
97 | | STATISTIC(NumReductionParallel, "Number of reduction-parallel for-loops"); |
98 | | STATISTIC(NumExecutedInParallel, "Number of for-loops executed in parallel"); |
99 | | STATISTIC(NumIfConditions, "Number of if-conditions"); |
100 | | |
101 | | namespace polly { |
102 | | |
103 | | /// Temporary information used when building the ast. |
104 | | struct AstBuildUserInfo { |
105 | | /// Construct and initialize the helper struct for AST creation. |
106 | 458 | AstBuildUserInfo() = default; |
107 | | |
108 | | /// The dependence information used for the parallelism check. |
109 | | const Dependences *Deps = nullptr; |
110 | | |
111 | | /// Flag to indicate that we are inside a parallel for node. |
112 | | bool InParallelFor = false; |
113 | | |
114 | | /// Flag to indicate that we are inside an SIMD node. |
115 | | bool InSIMD = false; |
116 | | |
117 | | /// The last iterator id created for the current SCoP. |
118 | | isl_id *LastForNodeId = nullptr; |
119 | | }; |
120 | | } // namespace polly |
121 | | |
122 | | /// Free an IslAstUserPayload object pointed to by @p Ptr. |
123 | 2.49k | static void freeIslAstUserPayload(void *Ptr) { |
124 | 2.49k | delete ((IslAstInfo::IslAstUserPayload *)Ptr); |
125 | 2.49k | } |
126 | | |
127 | 2.49k | IslAstInfo::IslAstUserPayload::~IslAstUserPayload() { |
128 | 2.49k | isl_ast_build_free(Build); |
129 | 2.49k | } |
130 | | |
131 | | /// Print a string @p str in a single line using @p Printer. |
132 | | static isl_printer *printLine(__isl_take isl_printer *Printer, |
133 | | const std::string &str, |
134 | 160 | __isl_keep isl_pw_aff *PWA = nullptr) { |
135 | 160 | Printer = isl_printer_start_line(Printer); |
136 | 160 | Printer = isl_printer_print_str(Printer, str.c_str()); |
137 | 160 | if (PWA) |
138 | 42 | Printer = isl_printer_print_pw_aff(Printer, PWA); |
139 | 160 | return isl_printer_end_line(Printer); |
140 | 160 | } |
141 | | |
142 | | /// Return all broken reductions as a string of clauses (OpenMP style). |
143 | 424 | static const std::string getBrokenReductionsStr(__isl_keep isl_ast_node *Node) { |
144 | 424 | IslAstInfo::MemoryAccessSet *BrokenReductions; |
145 | 424 | std::string str; |
146 | 424 | |
147 | 424 | BrokenReductions = IslAstInfo::getBrokenReductions(Node); |
148 | 424 | if (!BrokenReductions || BrokenReductions->empty()183 ) |
149 | 398 | return ""; |
150 | 26 | |
151 | 26 | // Map each type of reduction to a comma separated list of the base addresses. |
152 | 26 | std::map<MemoryAccess::ReductionType, std::string> Clauses; |
153 | 26 | for (MemoryAccess *MA : *BrokenReductions) |
154 | 60 | if (MA->isWrite()) |
155 | 30 | Clauses[MA->getReductionType()] += |
156 | 30 | ", " + MA->getScopArrayInfo()->getName(); |
157 | 26 | |
158 | 26 | // Now print the reductions sorted by type. Each type will cause a clause |
159 | 26 | // like: reduction (+ : sum0, sum1, sum2) |
160 | 29 | for (const auto &ReductionClause : Clauses) { |
161 | 29 | str += " reduction ("; |
162 | 29 | str += MemoryAccess::getReductionOperatorStr(ReductionClause.first); |
163 | 29 | // Remove the first two symbols (", ") to make the output look pretty. |
164 | 29 | str += " : " + ReductionClause.second.substr(2) + ")"; |
165 | 29 | } |
166 | 26 | |
167 | 26 | return str; |
168 | 26 | } |
169 | | |
170 | | /// Callback executed for each for node in the ast in order to print it. |
171 | | static isl_printer *cbPrintFor(__isl_take isl_printer *Printer, |
172 | | __isl_take isl_ast_print_options *Options, |
173 | 424 | __isl_keep isl_ast_node *Node, void *) { |
174 | 424 | isl_pw_aff *DD = IslAstInfo::getMinimalDependenceDistance(Node); |
175 | 424 | const std::string BrokenReductionsStr = getBrokenReductionsStr(Node); |
176 | 424 | const std::string KnownParallelStr = "#pragma known-parallel"; |
177 | 424 | const std::string DepDisPragmaStr = "#pragma minimal dependence distance: "; |
178 | 424 | const std::string SimdPragmaStr = "#pragma simd"; |
179 | 424 | const std::string OmpPragmaStr = "#pragma omp parallel for"; |
180 | 424 | |
181 | 424 | if (DD) |
182 | 42 | Printer = printLine(Printer, DepDisPragmaStr, DD); |
183 | 424 | |
184 | 424 | if (IslAstInfo::isInnermostParallel(Node)) |
185 | 65 | Printer = printLine(Printer, SimdPragmaStr + BrokenReductionsStr); |
186 | 424 | |
187 | 424 | if (IslAstInfo::isExecutedInParallel(Node)) |
188 | 20 | Printer = printLine(Printer, OmpPragmaStr); |
189 | 404 | else if (IslAstInfo::isOutermostParallel(Node)) |
190 | 33 | Printer = printLine(Printer, KnownParallelStr + BrokenReductionsStr); |
191 | 424 | |
192 | 424 | isl_pw_aff_free(DD); |
193 | 424 | return isl_ast_node_for_print(Node, Printer, Options); |
194 | 424 | } |
195 | | |
196 | | /// Check if the current scheduling dimension is parallel. |
197 | | /// |
198 | | /// In case the dimension is parallel we also check if any reduction |
199 | | /// dependences is broken when we exploit this parallelism. If so, |
200 | | /// @p IsReductionParallel will be set to true. The reduction dependences we use |
201 | | /// to check are actually the union of the transitive closure of the initial |
202 | | /// reduction dependences together with their reversal. Even though these |
203 | | /// dependences connect all iterations with each other (thus they are cyclic) |
204 | | /// we can perform the parallelism check as we are only interested in a zero |
205 | | /// (or non-zero) dependence distance on the dimension in question. |
206 | | static bool astScheduleDimIsParallel(__isl_keep isl_ast_build *Build, |
207 | | const Dependences *D, |
208 | 274 | IslAstUserPayload *NodeInfo) { |
209 | 274 | if (!D->hasValidDependences()) |
210 | 1 | return false; |
211 | 273 | |
212 | 273 | isl_union_map *Schedule = isl_ast_build_get_schedule(Build); |
213 | 273 | isl_union_map *Deps = |
214 | 273 | D->getDependences(Dependences::TYPE_RAW | Dependences::TYPE_WAW | |
215 | 273 | Dependences::TYPE_WAR) |
216 | 273 | .release(); |
217 | 273 | |
218 | 273 | if (!D->isParallel(Schedule, Deps)) { |
219 | 53 | isl_union_map *DepsAll = |
220 | 53 | D->getDependences(Dependences::TYPE_RAW | Dependences::TYPE_WAW | |
221 | 53 | Dependences::TYPE_WAR | Dependences::TYPE_TC_RED) |
222 | 53 | .release(); |
223 | 53 | isl_pw_aff *MinimalDependenceDistance = nullptr; |
224 | 53 | D->isParallel(Schedule, DepsAll, &MinimalDependenceDistance); |
225 | 53 | NodeInfo->MinimalDependenceDistance = |
226 | 53 | isl::manage(MinimalDependenceDistance); |
227 | 53 | isl_union_map_free(Schedule); |
228 | 53 | return false; |
229 | 53 | } |
230 | 220 | |
231 | 220 | isl_union_map *RedDeps = |
232 | 220 | D->getDependences(Dependences::TYPE_TC_RED).release(); |
233 | 220 | if (!D->isParallel(Schedule, RedDeps)) |
234 | 26 | NodeInfo->IsReductionParallel = true; |
235 | 220 | |
236 | 220 | if (!NodeInfo->IsReductionParallel && !isl_union_map_free(Schedule)194 ) |
237 | 194 | return true; |
238 | 26 | |
239 | 26 | // Annotate reduction parallel nodes with the memory accesses which caused the |
240 | 26 | // reduction dependences parallel execution of the node conflicts with. |
241 | 60 | for (const auto &MaRedPair : D->getReductionDependences())26 { |
242 | 60 | if (!MaRedPair.second) |
243 | 0 | continue; |
244 | 60 | RedDeps = isl_union_map_from_map(isl_map_copy(MaRedPair.second)); |
245 | 60 | if (!D->isParallel(Schedule, RedDeps)) |
246 | 60 | NodeInfo->BrokenReductions.insert(MaRedPair.first); |
247 | 60 | } |
248 | 26 | |
249 | 26 | isl_union_map_free(Schedule); |
250 | 26 | return true; |
251 | 26 | } |
252 | | |
253 | | // This method is executed before the construction of a for node. It creates |
254 | | // an isl_id that is used to annotate the subsequently generated ast for nodes. |
255 | | // |
256 | | // In this function we also run the following analyses: |
257 | | // |
258 | | // - Detection of openmp parallel loops |
259 | | // |
260 | | static __isl_give isl_id *astBuildBeforeFor(__isl_keep isl_ast_build *Build, |
261 | 274 | void *User) { |
262 | 274 | AstBuildUserInfo *BuildInfo = (AstBuildUserInfo *)User; |
263 | 274 | IslAstUserPayload *Payload = new IslAstUserPayload(); |
264 | 274 | isl_id *Id = isl_id_alloc(isl_ast_build_get_ctx(Build), "", Payload); |
265 | 274 | Id = isl_id_set_free_user(Id, freeIslAstUserPayload); |
266 | 274 | BuildInfo->LastForNodeId = Id; |
267 | 274 | |
268 | 274 | Payload->IsParallel = |
269 | 274 | astScheduleDimIsParallel(Build, BuildInfo->Deps, Payload); |
270 | 274 | |
271 | 274 | // Test for parallelism only if we are not already inside a parallel loop |
272 | 274 | if (!BuildInfo->InParallelFor && !BuildInfo->InSIMD147 ) |
273 | 147 | BuildInfo->InParallelFor = Payload->IsOutermostParallel = |
274 | 147 | Payload->IsParallel; |
275 | 274 | |
276 | 274 | return Id; |
277 | 274 | } |
278 | | |
279 | | // This method is executed after the construction of a for node. |
280 | | // |
281 | | // It performs the following actions: |
282 | | // |
283 | | // - Reset the 'InParallelFor' flag, as soon as we leave a for node, |
284 | | // that is marked as openmp parallel. |
285 | | // |
286 | | static __isl_give isl_ast_node * |
287 | | astBuildAfterFor(__isl_take isl_ast_node *Node, __isl_keep isl_ast_build *Build, |
288 | 274 | void *User) { |
289 | 274 | isl_id *Id = isl_ast_node_get_annotation(Node); |
290 | 274 | assert(Id && "Post order visit assumes annotated for nodes"); |
291 | 274 | IslAstUserPayload *Payload = (IslAstUserPayload *)isl_id_get_user(Id); |
292 | 274 | assert(Payload && "Post order visit assumes annotated for nodes"); |
293 | 274 | |
294 | 274 | AstBuildUserInfo *BuildInfo = (AstBuildUserInfo *)User; |
295 | 274 | assert(!Payload->Build && "Build environment already set"); |
296 | 274 | Payload->Build = isl_ast_build_copy(Build); |
297 | 274 | Payload->IsInnermost = (Id == BuildInfo->LastForNodeId); |
298 | 274 | |
299 | 274 | Payload->IsInnermostParallel = |
300 | 274 | Payload->IsInnermost && (149 BuildInfo->InSIMD149 || Payload->IsParallel127 ); |
301 | 274 | if (Payload->IsOutermostParallel) |
302 | 119 | BuildInfo->InParallelFor = false; |
303 | 274 | |
304 | 274 | isl_id_free(Id); |
305 | 274 | return Node; |
306 | 274 | } |
307 | | |
308 | | static isl_stat astBuildBeforeMark(__isl_keep isl_id *MarkId, |
309 | | __isl_keep isl_ast_build *Build, |
310 | 61 | void *User) { |
311 | 61 | if (!MarkId) |
312 | 0 | return isl_stat_error; |
313 | 61 | |
314 | 61 | AstBuildUserInfo *BuildInfo = (AstBuildUserInfo *)User; |
315 | 61 | if (strcmp(isl_id_get_name(MarkId), "SIMD") == 0) |
316 | 26 | BuildInfo->InSIMD = true; |
317 | 61 | |
318 | 61 | return isl_stat_ok; |
319 | 61 | } |
320 | | |
321 | | static __isl_give isl_ast_node * |
322 | | astBuildAfterMark(__isl_take isl_ast_node *Node, |
323 | 61 | __isl_keep isl_ast_build *Build, void *User) { |
324 | 61 | assert(isl_ast_node_get_type(Node) == isl_ast_node_mark); |
325 | 61 | AstBuildUserInfo *BuildInfo = (AstBuildUserInfo *)User; |
326 | 61 | auto *Id = isl_ast_node_mark_get_id(Node); |
327 | 61 | if (strcmp(isl_id_get_name(Id), "SIMD") == 0) |
328 | 26 | BuildInfo->InSIMD = false; |
329 | 61 | isl_id_free(Id); |
330 | 61 | return Node; |
331 | 61 | } |
332 | | |
333 | | static __isl_give isl_ast_node *AtEachDomain(__isl_take isl_ast_node *Node, |
334 | | __isl_keep isl_ast_build *Build, |
335 | 2.22k | void *User) { |
336 | 2.22k | assert(!isl_ast_node_get_annotation(Node) && "Node already annotated"); |
337 | 2.22k | |
338 | 2.22k | IslAstUserPayload *Payload = new IslAstUserPayload(); |
339 | 2.22k | isl_id *Id = isl_id_alloc(isl_ast_build_get_ctx(Build), "", Payload); |
340 | 2.22k | Id = isl_id_set_free_user(Id, freeIslAstUserPayload); |
341 | 2.22k | |
342 | 2.22k | Payload->Build = isl_ast_build_copy(Build); |
343 | 2.22k | |
344 | 2.22k | return isl_ast_node_set_annotation(Node, Id); |
345 | 2.22k | } |
346 | | |
347 | | // Build alias check condition given a pair of minimal/maximal access. |
348 | | static isl::ast_expr buildCondition(Scop &S, isl::ast_build Build, |
349 | | const Scop::MinMaxAccessTy *It0, |
350 | 174 | const Scop::MinMaxAccessTy *It1) { |
351 | 174 | |
352 | 174 | isl::pw_multi_aff AFirst = It0->first; |
353 | 174 | isl::pw_multi_aff ASecond = It0->second; |
354 | 174 | isl::pw_multi_aff BFirst = It1->first; |
355 | 174 | isl::pw_multi_aff BSecond = It1->second; |
356 | 174 | |
357 | 174 | isl::id Left = AFirst.get_tuple_id(isl::dim::set); |
358 | 174 | isl::id Right = BFirst.get_tuple_id(isl::dim::set); |
359 | 174 | |
360 | 174 | isl::ast_expr True = |
361 | 174 | isl::ast_expr::from_val(isl::val::int_from_ui(Build.get_ctx(), 1)); |
362 | 174 | isl::ast_expr False = |
363 | 174 | isl::ast_expr::from_val(isl::val::int_from_ui(Build.get_ctx(), 0)); |
364 | 174 | |
365 | 174 | const ScopArrayInfo *BaseLeft = |
366 | 174 | ScopArrayInfo::getFromId(Left)->getBasePtrOriginSAI(); |
367 | 174 | const ScopArrayInfo *BaseRight = |
368 | 174 | ScopArrayInfo::getFromId(Right)->getBasePtrOriginSAI(); |
369 | 174 | if (BaseLeft && BaseLeft == BaseRight24 ) |
370 | 2 | return True; |
371 | 172 | |
372 | 172 | isl::set Params = S.getContext(); |
373 | 172 | |
374 | 172 | isl::ast_expr NonAliasGroup, MinExpr, MaxExpr; |
375 | 172 | |
376 | 172 | // In the following, we first check if any accesses will be empty under |
377 | 172 | // the execution context of the scop and do not code generate them if this |
378 | 172 | // is the case as isl will fail to derive valid AST expressions for such |
379 | 172 | // accesses. |
380 | 172 | |
381 | 172 | if (!AFirst.intersect_params(Params).domain().is_empty() && |
382 | 172 | !BSecond.intersect_params(Params).domain().is_empty()) { |
383 | 170 | MinExpr = Build.access_from(AFirst).address_of(); |
384 | 170 | MaxExpr = Build.access_from(BSecond).address_of(); |
385 | 170 | NonAliasGroup = MaxExpr.le(MinExpr); |
386 | 170 | } |
387 | 172 | |
388 | 172 | if (!BFirst.intersect_params(Params).domain().is_empty() && |
389 | 172 | !ASecond.intersect_params(Params).domain().is_empty()170 ) { |
390 | 170 | MinExpr = Build.access_from(BFirst).address_of(); |
391 | 170 | MaxExpr = Build.access_from(ASecond).address_of(); |
392 | 170 | |
393 | 170 | isl::ast_expr Result = MaxExpr.le(MinExpr); |
394 | 170 | if (!NonAliasGroup.is_null()) |
395 | 170 | NonAliasGroup = isl::manage( |
396 | 170 | isl_ast_expr_or(NonAliasGroup.release(), Result.release())); |
397 | 0 | else |
398 | 0 | NonAliasGroup = Result; |
399 | 170 | } |
400 | 172 | |
401 | 172 | if (NonAliasGroup.is_null()) |
402 | 2 | NonAliasGroup = True; |
403 | 172 | |
404 | 172 | return NonAliasGroup; |
405 | 172 | } |
406 | | |
407 | | __isl_give isl_ast_expr * |
408 | 458 | IslAst::buildRunCondition(Scop &S, __isl_keep isl_ast_build *Build) { |
409 | 458 | isl_ast_expr *RunCondition; |
410 | 458 | |
411 | 458 | // The conditions that need to be checked at run-time for this scop are |
412 | 458 | // available as an isl_set in the runtime check context from which we can |
413 | 458 | // directly derive a run-time condition. |
414 | 458 | auto *PosCond = |
415 | 458 | isl_ast_build_expr_from_set(Build, S.getAssumedContext().release()); |
416 | 458 | if (S.hasTrivialInvalidContext()) { |
417 | 344 | RunCondition = PosCond; |
418 | 344 | } else { |
419 | 114 | auto *ZeroV = isl_val_zero(isl_ast_build_get_ctx(Build)); |
420 | 114 | auto *NegCond = |
421 | 114 | isl_ast_build_expr_from_set(Build, S.getInvalidContext().release()); |
422 | 114 | auto *NotNegCond = isl_ast_expr_eq(isl_ast_expr_from_val(ZeroV), NegCond); |
423 | 114 | RunCondition = isl_ast_expr_and(PosCond, NotNegCond); |
424 | 114 | } |
425 | 458 | |
426 | 458 | // Create the alias checks from the minimal/maximal accesses in each alias |
427 | 458 | // group which consists of read only and non read only (read write) accesses. |
428 | 458 | // This operation is by construction quadratic in the read-write pointers and |
429 | 458 | // linear in the read only pointers in each alias group. |
430 | 458 | for (const Scop::MinMaxVectorPairTy &MinMaxAccessPair : S.getAliasGroups()) { |
431 | 107 | auto &MinMaxReadWrite = MinMaxAccessPair.first; |
432 | 107 | auto &MinMaxReadOnly = MinMaxAccessPair.second; |
433 | 107 | auto RWAccEnd = MinMaxReadWrite.end(); |
434 | 107 | |
435 | 233 | for (auto RWAccIt0 = MinMaxReadWrite.begin(); RWAccIt0 != RWAccEnd; |
436 | 126 | ++RWAccIt0) { |
437 | 147 | for (auto RWAccIt1 = RWAccIt0 + 1; RWAccIt1 != RWAccEnd; ++RWAccIt121 ) |
438 | 21 | RunCondition = isl_ast_expr_and( |
439 | 21 | RunCondition, |
440 | 21 | buildCondition(S, isl::manage_copy(Build), RWAccIt0, RWAccIt1) |
441 | 21 | .release()); |
442 | 126 | for (const Scop::MinMaxAccessTy &ROAccIt : MinMaxReadOnly) |
443 | 153 | RunCondition = isl_ast_expr_and( |
444 | 153 | RunCondition, |
445 | 153 | buildCondition(S, isl::manage_copy(Build), RWAccIt0, &ROAccIt) |
446 | 153 | .release()); |
447 | 126 | } |
448 | 107 | } |
449 | 458 | |
450 | 458 | return RunCondition; |
451 | 458 | } |
452 | | |
453 | | /// Simple cost analysis for a given SCoP. |
454 | | /// |
455 | | /// TODO: Improve this analysis and extract it to make it usable in other |
456 | | /// places too. |
457 | | /// In order to improve the cost model we could either keep track of |
458 | | /// performed optimizations (e.g., tiling) or compute properties on the |
459 | | /// original as well as optimized SCoP (e.g., #stride-one-accesses). |
460 | 458 | static bool benefitsFromPolly(Scop &Scop, bool PerformParallelTest) { |
461 | 458 | if (PollyProcessUnprofitable) |
462 | 458 | return true; |
463 | 0 | |
464 | 0 | // Check if nothing interesting happened. |
465 | 0 | if (!PerformParallelTest && !Scop.isOptimized() && |
466 | 0 | Scop.getAliasGroups().empty()) |
467 | 0 | return false; |
468 | 0 | |
469 | 0 | // The default assumption is that Polly improves the code. |
470 | 0 | return true; |
471 | 0 | } |
472 | | |
473 | | /// Collect statistics for the syntax tree rooted at @p Ast. |
474 | 458 | static void walkAstForStatistics(__isl_keep isl_ast_node *Ast) { |
475 | 458 | assert(Ast); |
476 | 458 | isl_ast_node_foreach_descendant_top_down( |
477 | 458 | Ast, |
478 | 3.78k | [](__isl_keep isl_ast_node *Node, void *User) -> isl_bool { |
479 | 3.78k | switch (isl_ast_node_get_type(Node)) { |
480 | 3.78k | case isl_ast_node_for: |
481 | 741 | NumForLoops++; |
482 | 741 | if (IslAstInfo::isParallel(Node)) |
483 | 177 | NumParallel++; |
484 | 741 | if (IslAstInfo::isInnermostParallel(Node)) |
485 | 127 | NumInnermostParallel++; |
486 | 741 | if (IslAstInfo::isOutermostParallel(Node)) |
487 | 119 | NumOutermostParallel++; |
488 | 741 | if (IslAstInfo::isReductionParallel(Node)) |
489 | 26 | NumReductionParallel++; |
490 | 741 | if (IslAstInfo::isExecutedInParallel(Node)) |
491 | 59 | NumExecutedInParallel++; |
492 | 741 | break; |
493 | 3.78k | |
494 | 3.78k | case isl_ast_node_if: |
495 | 235 | NumIfConditions++; |
496 | 235 | break; |
497 | 3.78k | |
498 | 3.78k | default: |
499 | 2.81k | break; |
500 | 3.78k | } |
501 | 3.78k | |
502 | 3.78k | // Continue traversing subtrees. |
503 | 3.78k | return isl_bool_true; |
504 | 3.78k | }, |
505 | 458 | nullptr); |
506 | 458 | } |
507 | | |
508 | 458 | IslAst::IslAst(Scop &Scop) : S(Scop), Ctx(Scop.getSharedIslCtx()) {} |
509 | | |
510 | | IslAst::IslAst(IslAst &&O) |
511 | 0 | : S(O.S), Root(O.Root), RunCondition(O.RunCondition), Ctx(O.Ctx) { |
512 | 0 | O.Root = nullptr; |
513 | 0 | O.RunCondition = nullptr; |
514 | 0 | } |
515 | | |
516 | 458 | IslAst::~IslAst() { |
517 | 458 | isl_ast_node_free(Root); |
518 | 458 | isl_ast_expr_free(RunCondition); |
519 | 458 | } |
520 | | |
521 | 458 | void IslAst::init(const Dependences &D) { |
522 | 458 | bool PerformParallelTest = PollyParallel || DetectParallel398 || |
523 | 458 | PollyVectorizerChoice != VECTORIZER_NONE365 ; |
524 | 458 | auto ScheduleTree = S.getScheduleTree(); |
525 | 458 | |
526 | 458 | // Skip AST and code generation if there was no benefit achieved. |
527 | 458 | if (!benefitsFromPolly(S, PerformParallelTest)) |
528 | 0 | return; |
529 | 458 | |
530 | 458 | auto ScopStats = S.getStatistics(); |
531 | 458 | ScopsBeneficial++; |
532 | 458 | BeneficialAffineLoops += ScopStats.NumAffineLoops; |
533 | 458 | BeneficialBoxedLoops += ScopStats.NumBoxedLoops; |
534 | 458 | |
535 | 458 | auto Ctx = S.getIslCtx(); |
536 | 458 | isl_options_set_ast_build_atomic_upper_bound(Ctx.get(), true); |
537 | 458 | isl_options_set_ast_build_detect_min_max(Ctx.get(), true); |
538 | 458 | isl_ast_build *Build; |
539 | 458 | AstBuildUserInfo BuildInfo; |
540 | 458 | |
541 | 458 | if (UseContext) |
542 | 458 | Build = isl_ast_build_from_context(S.getContext().release()); |
543 | 0 | else |
544 | 0 | Build = isl_ast_build_from_context( |
545 | 0 | isl_set_universe(S.getParamSpace().release())); |
546 | 458 | |
547 | 458 | Build = isl_ast_build_set_at_each_domain(Build, AtEachDomain, nullptr); |
548 | 458 | |
549 | 458 | if (PerformParallelTest) { |
550 | 123 | BuildInfo.Deps = &D; |
551 | 123 | BuildInfo.InParallelFor = false; |
552 | 123 | BuildInfo.InSIMD = false; |
553 | 123 | |
554 | 123 | Build = isl_ast_build_set_before_each_for(Build, &astBuildBeforeFor, |
555 | 123 | &BuildInfo); |
556 | 123 | Build = |
557 | 123 | isl_ast_build_set_after_each_for(Build, &astBuildAfterFor, &BuildInfo); |
558 | 123 | |
559 | 123 | Build = isl_ast_build_set_before_each_mark(Build, &astBuildBeforeMark, |
560 | 123 | &BuildInfo); |
561 | 123 | |
562 | 123 | Build = isl_ast_build_set_after_each_mark(Build, &astBuildAfterMark, |
563 | 123 | &BuildInfo); |
564 | 123 | } |
565 | 458 | |
566 | 458 | RunCondition = buildRunCondition(S, Build); |
567 | 458 | |
568 | 458 | Root = isl_ast_build_node_from_schedule(Build, S.getScheduleTree().release()); |
569 | 458 | walkAstForStatistics(Root); |
570 | 458 | |
571 | 458 | isl_ast_build_free(Build); |
572 | 458 | } |
573 | | |
574 | 458 | IslAst IslAst::create(Scop &Scop, const Dependences &D) { |
575 | 458 | IslAst Ast{Scop}; |
576 | 458 | Ast.init(D); |
577 | 458 | return Ast; |
578 | 458 | } |
579 | | |
580 | 459 | __isl_give isl_ast_node *IslAst::getAst() { return isl_ast_node_copy(Root); } |
581 | 455 | __isl_give isl_ast_expr *IslAst::getRunCondition() { |
582 | 455 | return isl_ast_expr_copy(RunCondition); |
583 | 455 | } |
584 | | |
585 | 0 | __isl_give isl_ast_node *IslAstInfo::getAst() { return Ast.getAst(); } |
586 | 301 | __isl_give isl_ast_expr *IslAstInfo::getRunCondition() { |
587 | 301 | return Ast.getRunCondition(); |
588 | 301 | } |
589 | | |
590 | 6.93k | IslAstUserPayload *IslAstInfo::getNodePayload(__isl_keep isl_ast_node *Node) { |
591 | 6.93k | isl_id *Id = isl_ast_node_get_annotation(Node); |
592 | 6.93k | if (!Id) |
593 | 3.74k | return nullptr; |
594 | 3.19k | IslAstUserPayload *Payload = (IslAstUserPayload *)isl_id_get_user(Id); |
595 | 3.19k | isl_id_free(Id); |
596 | 3.19k | return Payload; |
597 | 3.19k | } |
598 | | |
599 | 163 | bool IslAstInfo::isInnermost(__isl_keep isl_ast_node *Node) { |
600 | 163 | IslAstUserPayload *Payload = getNodePayload(Node); |
601 | 163 | return Payload && Payload->IsInnermost; |
602 | 163 | } |
603 | | |
604 | 1.02k | bool IslAstInfo::isParallel(__isl_keep isl_ast_node *Node) { |
605 | 1.02k | return IslAstInfo::isInnermostParallel(Node) || |
606 | 1.02k | IslAstInfo::isOutermostParallel(Node)870 ; |
607 | 1.02k | } |
608 | | |
609 | 2.21k | bool IslAstInfo::isInnermostParallel(__isl_keep isl_ast_node *Node) { |
610 | 2.21k | IslAstUserPayload *Payload = getNodePayload(Node); |
611 | 2.21k | return Payload && Payload->IsInnermostParallel818 ; |
612 | 2.21k | } |
613 | | |
614 | 2.20k | bool IslAstInfo::isOutermostParallel(__isl_keep isl_ast_node *Node) { |
615 | 2.20k | IslAstUserPayload *Payload = getNodePayload(Node); |
616 | 2.20k | return Payload && Payload->IsOutermostParallel807 ; |
617 | 2.20k | } |
618 | | |
619 | 911 | bool IslAstInfo::isReductionParallel(__isl_keep isl_ast_node *Node) { |
620 | 911 | IslAstUserPayload *Payload = getNodePayload(Node); |
621 | 911 | return Payload && Payload->IsReductionParallel444 ; |
622 | 911 | } |
623 | | |
624 | 1.48k | bool IslAstInfo::isExecutedInParallel(__isl_keep isl_ast_node *Node) { |
625 | 1.48k | if (!PollyParallel) |
626 | 1.22k | return false; |
627 | 261 | |
628 | 261 | // Do not parallelize innermost loops. |
629 | 261 | // |
630 | 261 | // Parallelizing innermost loops is often not profitable, especially if |
631 | 261 | // they have a low number of iterations. |
632 | 261 | // |
633 | 261 | // TODO: Decide this based on the number of loop iterations that will be |
634 | 261 | // executed. This can possibly require run-time checks, which again |
635 | 261 | // raises the question of both run-time check overhead and code size |
636 | 261 | // costs. |
637 | 261 | if (!PollyParallelForce && isInnermost(Node)163 ) |
638 | 71 | return false; |
639 | 190 | |
640 | 190 | return isOutermostParallel(Node) && !isReductionParallel(Node)120 ; |
641 | 190 | } |
642 | | |
643 | | __isl_give isl_union_map * |
644 | 60 | IslAstInfo::getSchedule(__isl_keep isl_ast_node *Node) { |
645 | 60 | IslAstUserPayload *Payload = getNodePayload(Node); |
646 | 60 | return Payload ? isl_ast_build_get_schedule(Payload->Build) : nullptr0 ; |
647 | 60 | } |
648 | | |
649 | | __isl_give isl_pw_aff * |
650 | 424 | IslAstInfo::getMinimalDependenceDistance(__isl_keep isl_ast_node *Node) { |
651 | 424 | IslAstUserPayload *Payload = getNodePayload(Node); |
652 | 424 | return Payload ? Payload->MinimalDependenceDistance.copy()183 : nullptr241 ; |
653 | 424 | } |
654 | | |
655 | | IslAstInfo::MemoryAccessSet * |
656 | 424 | IslAstInfo::getBrokenReductions(__isl_keep isl_ast_node *Node) { |
657 | 424 | IslAstUserPayload *Payload = getNodePayload(Node); |
658 | 424 | return Payload ? &Payload->BrokenReductions183 : nullptr241 ; |
659 | 424 | } |
660 | | |
661 | 536 | isl_ast_build *IslAstInfo::getBuild(__isl_keep isl_ast_node *Node) { |
662 | 536 | IslAstUserPayload *Payload = getNodePayload(Node); |
663 | 536 | return Payload ? Payload->Build : nullptr0 ; |
664 | 536 | } |
665 | | |
666 | | IslAstInfo IslAstAnalysis::run(Scop &S, ScopAnalysisManager &SAM, |
667 | 0 | ScopStandardAnalysisResults &SAR) { |
668 | 0 | return {S, SAM.getResult<DependenceAnalysis>(S, SAR).getDependences( |
669 | 0 | Dependences::AL_Statement)}; |
670 | 0 | } |
671 | | |
672 | | static __isl_give isl_printer *cbPrintUser(__isl_take isl_printer *P, |
673 | | __isl_take isl_ast_print_options *O, |
674 | | __isl_keep isl_ast_node *Node, |
675 | 2 | void *User) { |
676 | 2 | isl::ast_node AstNode = isl::manage_copy(Node); |
677 | 2 | isl::ast_expr NodeExpr = AstNode.user_get_expr(); |
678 | 2 | isl::ast_expr CallExpr = NodeExpr.get_op_arg(0); |
679 | 2 | isl::id CallExprId = CallExpr.get_id(); |
680 | 2 | ScopStmt *AccessStmt = (ScopStmt *)CallExprId.get_user(); |
681 | 2 | |
682 | 2 | P = isl_printer_start_line(P); |
683 | 2 | P = isl_printer_print_str(P, AccessStmt->getBaseName()); |
684 | 2 | P = isl_printer_print_str(P, "("); |
685 | 2 | P = isl_printer_end_line(P); |
686 | 2 | P = isl_printer_indent(P, 2); |
687 | 2 | |
688 | 3 | for (MemoryAccess *MemAcc : *AccessStmt) { |
689 | 3 | P = isl_printer_start_line(P); |
690 | 3 | |
691 | 3 | if (MemAcc->isRead()) |
692 | 1 | P = isl_printer_print_str(P, "/* read */ &"); |
693 | 2 | else |
694 | 2 | P = isl_printer_print_str(P, "/* write */ "); |
695 | 3 | |
696 | 3 | isl::ast_build Build = isl::manage_copy(IslAstInfo::getBuild(Node)); |
697 | 3 | if (MemAcc->isAffine()) { |
698 | 2 | isl_pw_multi_aff *PwmaPtr = |
699 | 2 | MemAcc->applyScheduleToAccessRelation(Build.get_schedule()).release(); |
700 | 2 | isl::pw_multi_aff Pwma = isl::manage(PwmaPtr); |
701 | 2 | isl::ast_expr AccessExpr = Build.access_from(Pwma); |
702 | 2 | P = isl_printer_print_ast_expr(P, AccessExpr.get()); |
703 | 2 | } else { |
704 | 1 | P = isl_printer_print_str( |
705 | 1 | P, MemAcc->getLatestScopArrayInfo()->getName().c_str()); |
706 | 1 | P = isl_printer_print_str(P, "[*]"); |
707 | 1 | } |
708 | 3 | P = isl_printer_end_line(P); |
709 | 3 | } |
710 | 2 | |
711 | 2 | P = isl_printer_indent(P, -2); |
712 | 2 | P = isl_printer_start_line(P); |
713 | 2 | P = isl_printer_print_str(P, ");"); |
714 | 2 | P = isl_printer_end_line(P); |
715 | 2 | |
716 | 2 | isl_ast_print_options_free(O); |
717 | 2 | return P; |
718 | 2 | } |
719 | | |
720 | 154 | void IslAstInfo::print(raw_ostream &OS) { |
721 | 154 | isl_ast_print_options *Options; |
722 | 154 | isl_ast_node *RootNode = Ast.getAst(); |
723 | 154 | Function &F = S.getFunction(); |
724 | 154 | |
725 | 154 | OS << ":: isl ast :: " << F.getName() << " :: " << S.getNameStr() << "\n"; |
726 | 154 | |
727 | 154 | if (!RootNode) { |
728 | 0 | OS << ":: isl ast generation and code generation was skipped!\n\n"; |
729 | 0 | OS << ":: This is either because no useful optimizations could be applied " |
730 | 0 | "(use -polly-process-unprofitable to enforce code generation) or " |
731 | 0 | "because earlier passes such as dependence analysis timed out (use " |
732 | 0 | "-polly-dependences-computeout=0 to set dependence analysis timeout " |
733 | 0 | "to infinity)\n\n"; |
734 | 0 | return; |
735 | 0 | } |
736 | 154 | |
737 | 154 | isl_ast_expr *RunCondition = Ast.getRunCondition(); |
738 | 154 | char *RtCStr, *AstStr; |
739 | 154 | |
740 | 154 | Options = isl_ast_print_options_alloc(S.getIslCtx().get()); |
741 | 154 | |
742 | 154 | if (PrintAccesses) |
743 | 2 | Options = |
744 | 2 | isl_ast_print_options_set_print_user(Options, cbPrintUser, nullptr); |
745 | 154 | Options = isl_ast_print_options_set_print_for(Options, cbPrintFor, nullptr); |
746 | 154 | |
747 | 154 | isl_printer *P = isl_printer_to_str(S.getIslCtx().get()); |
748 | 154 | P = isl_printer_set_output_format(P, ISL_FORMAT_C); |
749 | 154 | P = isl_printer_print_ast_expr(P, RunCondition); |
750 | 154 | RtCStr = isl_printer_get_str(P); |
751 | 154 | P = isl_printer_flush(P); |
752 | 154 | P = isl_printer_indent(P, 4); |
753 | 154 | P = isl_ast_node_print(RootNode, P, Options); |
754 | 154 | AstStr = isl_printer_get_str(P); |
755 | 154 | |
756 | 154 | auto *Schedule = S.getScheduleTree().release(); |
757 | 154 | |
758 | 154 | LLVM_DEBUG({ |
759 | 154 | dbgs() << S.getContextStr() << "\n"; |
760 | 154 | dbgs() << stringFromIslObj(Schedule); |
761 | 154 | }); |
762 | 154 | OS << "\nif (" << RtCStr << ")\n\n"; |
763 | 154 | OS << AstStr << "\n"; |
764 | 154 | OS << "else\n"; |
765 | 154 | OS << " { /* original code */ }\n\n"; |
766 | 154 | |
767 | 154 | free(RtCStr); |
768 | 154 | free(AstStr); |
769 | 154 | |
770 | 154 | isl_ast_expr_free(RunCondition); |
771 | 154 | isl_schedule_free(Schedule); |
772 | 154 | isl_ast_node_free(RootNode); |
773 | 154 | isl_printer_free(P); |
774 | 154 | } |
775 | | |
776 | | AnalysisKey IslAstAnalysis::Key; |
777 | | PreservedAnalyses IslAstPrinterPass::run(Scop &S, ScopAnalysisManager &SAM, |
778 | | ScopStandardAnalysisResults &SAR, |
779 | 0 | SPMUpdater &U) { |
780 | 0 | auto &Ast = SAM.getResult<IslAstAnalysis>(S, SAR); |
781 | 0 | Ast.print(OS); |
782 | 0 | return PreservedAnalyses::all(); |
783 | 0 | } |
784 | | |
785 | 1.90k | void IslAstInfoWrapperPass::releaseMemory() { Ast.reset(); } |
786 | | |
787 | 458 | bool IslAstInfoWrapperPass::runOnScop(Scop &Scop) { |
788 | 458 | // Skip SCoPs in case they're already handled by PPCGCodeGeneration. |
789 | 458 | if (Scop.isToBeSkipped()) |
790 | 0 | return false; |
791 | 458 | |
792 | 458 | ScopsProcessed++; |
793 | 458 | |
794 | 458 | const Dependences &D = |
795 | 458 | getAnalysis<DependenceInfo>().getDependences(Dependences::AL_Statement); |
796 | 458 | |
797 | 458 | if (D.getSharedIslCtx() != Scop.getSharedIslCtx()) { |
798 | 0 | LLVM_DEBUG( |
799 | 0 | dbgs() << "Got dependence analysis for different SCoP/isl_ctx\n"); |
800 | 0 | Ast.reset(); |
801 | 0 | return false; |
802 | 0 | } |
803 | 458 | |
804 | 458 | Ast.reset(new IslAstInfo(Scop, D)); |
805 | 458 | |
806 | 458 | LLVM_DEBUG(printScop(dbgs(), Scop)); |
807 | 458 | return false; |
808 | 458 | } |
809 | | |
810 | 496 | void IslAstInfoWrapperPass::getAnalysisUsage(AnalysisUsage &AU) const { |
811 | 496 | // Get the Common analysis usage of ScopPasses. |
812 | 496 | ScopPass::getAnalysisUsage(AU); |
813 | 496 | AU.addRequiredTransitive<ScopInfoRegionPass>(); |
814 | 496 | AU.addRequired<DependenceInfo>(); |
815 | 496 | |
816 | 496 | AU.addPreserved<DependenceInfo>(); |
817 | 496 | } |
818 | | |
819 | 154 | void IslAstInfoWrapperPass::printScop(raw_ostream &OS, Scop &S) const { |
820 | 154 | if (Ast) |
821 | 154 | Ast->print(OS); |
822 | 154 | } |
823 | | |
824 | | char IslAstInfoWrapperPass::ID = 0; |
825 | | |
826 | 0 | Pass *polly::createIslAstInfoWrapperPassPass() { |
827 | 0 | return new IslAstInfoWrapperPass(); |
828 | 0 | } |
829 | | |
830 | 48.2k | INITIALIZE_PASS_BEGIN(IslAstInfoWrapperPass, "polly-ast", |
831 | 48.2k | "Polly - Generate an AST of the SCoP (isl)", false, |
832 | 48.2k | false); |
833 | 48.2k | INITIALIZE_PASS_DEPENDENCY(ScopInfoRegionPass); |
834 | 48.2k | INITIALIZE_PASS_DEPENDENCY(DependenceInfo); |
835 | 48.2k | INITIALIZE_PASS_END(IslAstInfoWrapperPass, "polly-ast", |
836 | | "Polly - Generate an AST from the SCoP (isl)", false, false) |