1 | //===- Support/GICHelper.h -- Helper functions for ISL --------------------===// |
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 | // Helper functions for isl objects. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | // |
13 | #ifndef POLLY_SUPPORT_GIC_HELPER_H |
14 | #define POLLY_SUPPORT_GIC_HELPER_H |
15 | |
16 | #include "llvm/ADT/APInt.h" |
17 | #include "llvm/IR/DiagnosticInfo.h" |
18 | #include "llvm/Support/raw_ostream.h" |
19 | #include "isl/ctx.h" |
20 | #include "isl/isl-noexceptions.h" |
21 | #include "isl/options.h" |
22 | |
23 | namespace polly { |
24 | |
25 | /// Translate an llvm::APInt to an isl_val. |
26 | /// |
27 | /// Translate the bitsequence without sign information as provided by APInt into |
28 | /// a signed isl_val type. Depending on the value of @p IsSigned @p Int is |
29 | /// interpreted as unsigned value or as signed value in two's complement |
30 | /// representation. |
31 | /// |
32 | /// Input IsSigned Output |
33 | /// |
34 | /// 0 0 -> 0 |
35 | /// 1 0 -> 1 |
36 | /// 00 0 -> 0 |
37 | /// 01 0 -> 1 |
38 | /// 10 0 -> 2 |
39 | /// 11 0 -> 3 |
40 | /// |
41 | /// 0 1 -> 0 |
42 | /// 1 1 -> -1 |
43 | /// 00 1 -> 0 |
44 | /// 01 1 -> 1 |
45 | /// 10 1 -> -2 |
46 | /// 11 1 -> -1 |
47 | /// |
48 | /// @param Ctx The isl_ctx to create the isl_val in. |
49 | /// @param Int The integer value to translate. |
50 | /// @param IsSigned If the APInt should be interpreted as signed or unsigned |
51 | /// value. |
52 | /// |
53 | /// @return The isl_val corresponding to @p Int. |
54 | __isl_give isl_val *isl_valFromAPInt(isl_ctx *Ctx, const llvm::APInt Int, |
55 | bool IsSigned); |
56 | |
57 | /// Translate an llvm::APInt to an isl::val. |
58 | /// |
59 | /// Translate the bitsequence without sign information as provided by APInt into |
60 | /// a signed isl::val type. Depending on the value of @p IsSigned @p Int is |
61 | /// interpreted as unsigned value or as signed value in two's complement |
62 | /// representation. |
63 | /// |
64 | /// Input IsSigned Output |
65 | /// |
66 | /// 0 0 -> 0 |
67 | /// 1 0 -> 1 |
68 | /// 00 0 -> 0 |
69 | /// 01 0 -> 1 |
70 | /// 10 0 -> 2 |
71 | /// 11 0 -> 3 |
72 | /// |
73 | /// 0 1 -> 0 |
74 | /// 1 1 -> -1 |
75 | /// 00 1 -> 0 |
76 | /// 01 1 -> 1 |
77 | /// 10 1 -> -2 |
78 | /// 11 1 -> -1 |
79 | /// |
80 | /// @param Ctx The isl_ctx to create the isl::val in. |
81 | /// @param Int The integer value to translate. |
82 | /// @param IsSigned If the APInt should be interpreted as signed or unsigned |
83 | /// value. |
84 | /// |
85 | /// @return The isl::val corresponding to @p Int. |
86 | inline isl::val valFromAPInt(isl_ctx *Ctx, const llvm::APInt Int, |
87 | bool IsSigned) { |
88 | return isl::manage(ptr: isl_valFromAPInt(Ctx, Int, IsSigned)); |
89 | } |
90 | |
91 | /// Translate isl_val to llvm::APInt. |
92 | /// |
93 | /// This function can only be called on isl_val values which are integers. |
94 | /// Calling this function with a non-integral rational, NaN or infinity value |
95 | /// is not allowed. |
96 | /// |
97 | /// As the input isl_val may be negative, the APInt that this function returns |
98 | /// must always be interpreted as signed two's complement value. The bitwidth of |
99 | /// the generated APInt is always the minimal bitwidth necessary to model the |
100 | /// provided integer when interpreting the bit pattern as signed value. |
101 | /// |
102 | /// Some example conversions are: |
103 | /// |
104 | /// Input Bits Signed Bitwidth |
105 | /// 0 -> 0 0 1 |
106 | /// -1 -> 1 -1 1 |
107 | /// 1 -> 01 1 2 |
108 | /// -2 -> 10 -2 2 |
109 | /// 2 -> 010 2 3 |
110 | /// -3 -> 101 -3 3 |
111 | /// 3 -> 011 3 3 |
112 | /// -4 -> 100 -4 3 |
113 | /// 4 -> 0100 4 4 |
114 | /// |
115 | /// @param Val The isl val to translate. |
116 | /// |
117 | /// @return The APInt value corresponding to @p Val. |
118 | llvm::APInt APIntFromVal(__isl_take isl_val *Val); |
119 | |
120 | /// Translate isl::val to llvm::APInt. |
121 | /// |
122 | /// This function can only be called on isl::val values which are integers. |
123 | /// Calling this function with a non-integral rational, NaN or infinity value |
124 | /// is not allowed. |
125 | /// |
126 | /// As the input isl::val may be negative, the APInt that this function returns |
127 | /// must always be interpreted as signed two's complement value. The bitwidth of |
128 | /// the generated APInt is always the minimal bitwidth necessary to model the |
129 | /// provided integer when interpreting the bit pattern as signed value. |
130 | /// |
131 | /// Some example conversions are: |
132 | /// |
133 | /// Input Bits Signed Bitwidth |
134 | /// 0 -> 0 0 1 |
135 | /// -1 -> 1 -1 1 |
136 | /// 1 -> 01 1 2 |
137 | /// -2 -> 10 -2 2 |
138 | /// 2 -> 010 2 3 |
139 | /// -3 -> 101 -3 3 |
140 | /// 3 -> 011 3 3 |
141 | /// -4 -> 100 -4 3 |
142 | /// 4 -> 0100 4 4 |
143 | /// |
144 | /// @param Val The isl val to translate. |
145 | /// |
146 | /// @return The APInt value corresponding to @p Val. |
147 | inline llvm::APInt APIntFromVal(isl::val V) { |
148 | return APIntFromVal(Val: V.release()); |
149 | } |
150 | |
151 | /// Get c++ string from Isl objects. |
152 | //@{ |
153 | #define ISL_CPP_OBJECT_TO_STRING(name) \ |
154 | inline std::string stringFromIslObj(const name &Obj, \ |
155 | std::string DefaultValue = "") { \ |
156 | return stringFromIslObj(Obj.get(), DefaultValue); \ |
157 | } |
158 | |
159 | #define ISL_OBJECT_TO_STRING(name) \ |
160 | std::string stringFromIslObj(__isl_keep isl_##name *Obj, \ |
161 | std::string DefaultValue = ""); \ |
162 | ISL_CPP_OBJECT_TO_STRING(isl::name) |
163 | |
164 | ISL_OBJECT_TO_STRING(aff) |
165 | ISL_OBJECT_TO_STRING(ast_expr) |
166 | ISL_OBJECT_TO_STRING(ast_node) |
167 | ISL_OBJECT_TO_STRING(basic_map) |
168 | ISL_OBJECT_TO_STRING(basic_set) |
169 | ISL_OBJECT_TO_STRING(map) |
170 | ISL_OBJECT_TO_STRING(set) |
171 | ISL_OBJECT_TO_STRING(id) |
172 | ISL_OBJECT_TO_STRING(multi_aff) |
173 | ISL_OBJECT_TO_STRING(multi_pw_aff) |
174 | ISL_OBJECT_TO_STRING(multi_union_pw_aff) |
175 | ISL_OBJECT_TO_STRING(point) |
176 | ISL_OBJECT_TO_STRING(pw_aff) |
177 | ISL_OBJECT_TO_STRING(pw_multi_aff) |
178 | ISL_OBJECT_TO_STRING(schedule) |
179 | ISL_OBJECT_TO_STRING(schedule_node) |
180 | ISL_OBJECT_TO_STRING(space) |
181 | ISL_OBJECT_TO_STRING(union_access_info) |
182 | ISL_OBJECT_TO_STRING(union_flow) |
183 | ISL_OBJECT_TO_STRING(union_set) |
184 | ISL_OBJECT_TO_STRING(union_map) |
185 | ISL_OBJECT_TO_STRING(union_pw_aff) |
186 | ISL_OBJECT_TO_STRING(union_pw_multi_aff) |
187 | //@} |
188 | |
189 | #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) |
190 | /// C++ wrapper for isl_*_dump() functions. |
191 | //@{ |
192 | |
193 | #define ISL_DUMP_OBJECT(name) \ |
194 | void dumpIslObj(const isl::name &Obj); \ |
195 | void dumpIslObj(isl_##name *Obj); |
196 | |
197 | ISL_DUMP_OBJECT(aff) |
198 | ISL_DUMP_OBJECT(aff_list) |
199 | ISL_DUMP_OBJECT(ast_expr) |
200 | ISL_DUMP_OBJECT(ast_node) |
201 | ISL_DUMP_OBJECT(ast_node_list) |
202 | ISL_DUMP_OBJECT(basic_map) |
203 | ISL_DUMP_OBJECT(basic_map_list) |
204 | ISL_DUMP_OBJECT(basic_set) |
205 | ISL_DUMP_OBJECT(basic_set_list) |
206 | ISL_DUMP_OBJECT(constraint) |
207 | ISL_DUMP_OBJECT(id) |
208 | ISL_DUMP_OBJECT(id_list) |
209 | ISL_DUMP_OBJECT(id_to_ast_expr) |
210 | ISL_DUMP_OBJECT(local_space) |
211 | ISL_DUMP_OBJECT(map) |
212 | ISL_DUMP_OBJECT(map_list) |
213 | ISL_DUMP_OBJECT(multi_aff) |
214 | ISL_DUMP_OBJECT(multi_pw_aff) |
215 | ISL_DUMP_OBJECT(multi_union_pw_aff) |
216 | ISL_DUMP_OBJECT(multi_val) |
217 | ISL_DUMP_OBJECT(point) |
218 | ISL_DUMP_OBJECT(pw_aff) |
219 | ISL_DUMP_OBJECT(pw_aff_list) |
220 | ISL_DUMP_OBJECT(pw_multi_aff) |
221 | ISL_DUMP_OBJECT(schedule) |
222 | ISL_DUMP_OBJECT(schedule_constraints) |
223 | ISL_DUMP_OBJECT(schedule_node) |
224 | ISL_DUMP_OBJECT(set) |
225 | ISL_DUMP_OBJECT(set_list) |
226 | ISL_DUMP_OBJECT(space) |
227 | ISL_DUMP_OBJECT(union_map) |
228 | ISL_DUMP_OBJECT(union_pw_aff) |
229 | ISL_DUMP_OBJECT(union_pw_aff_list) |
230 | ISL_DUMP_OBJECT(union_pw_multi_aff) |
231 | ISL_DUMP_OBJECT(union_set) |
232 | ISL_DUMP_OBJECT(union_set_list) |
233 | ISL_DUMP_OBJECT(val) |
234 | ISL_DUMP_OBJECT(val_list) |
235 | //@} |
236 | |
237 | /// Emit the equivaltent of the isl_*_dump output into a raw_ostream. |
238 | /// @{ |
239 | void dumpIslObj(const isl::schedule_node &Node, llvm::raw_ostream &OS); |
240 | void dumpIslObj(__isl_keep isl_schedule_node *node, llvm::raw_ostream &OS); |
241 | /// @} |
242 | #endif |
243 | |
244 | inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, |
245 | __isl_keep isl_union_map *Map) { |
246 | OS << polly::stringFromIslObj(Obj: Map, DefaultValue: "null" ); |
247 | return OS; |
248 | } |
249 | |
250 | inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, |
251 | __isl_keep isl_map *Map) { |
252 | OS << polly::stringFromIslObj(Obj: Map, DefaultValue: "null" ); |
253 | return OS; |
254 | } |
255 | |
256 | inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, |
257 | __isl_keep isl_set *Set) { |
258 | OS << polly::stringFromIslObj(Obj: Set, DefaultValue: "null" ); |
259 | return OS; |
260 | } |
261 | |
262 | inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, |
263 | __isl_keep isl_pw_aff *Map) { |
264 | OS << polly::stringFromIslObj(Obj: Map, DefaultValue: "null" ); |
265 | return OS; |
266 | } |
267 | |
268 | inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, |
269 | __isl_keep isl_pw_multi_aff *PMA) { |
270 | OS << polly::stringFromIslObj(Obj: PMA, DefaultValue: "null" ); |
271 | return OS; |
272 | } |
273 | |
274 | inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, |
275 | __isl_keep isl_multi_aff *MA) { |
276 | OS << polly::stringFromIslObj(Obj: MA, DefaultValue: "null" ); |
277 | return OS; |
278 | } |
279 | |
280 | inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, |
281 | __isl_keep isl_union_pw_multi_aff *UPMA) { |
282 | OS << polly::stringFromIslObj(Obj: UPMA, DefaultValue: "null" ); |
283 | return OS; |
284 | } |
285 | |
286 | inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, |
287 | __isl_keep isl_schedule *Schedule) { |
288 | OS << polly::stringFromIslObj(Obj: Schedule, DefaultValue: "null" ); |
289 | return OS; |
290 | } |
291 | |
292 | inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, |
293 | __isl_keep isl_space *Space) { |
294 | OS << polly::stringFromIslObj(Obj: Space, DefaultValue: "null" ); |
295 | return OS; |
296 | } |
297 | |
298 | /// Combine Prefix, Val (or Number) and Suffix to an isl-compatible name. |
299 | /// |
300 | /// In case @p UseInstructionNames is set, this function returns: |
301 | /// |
302 | /// @p Prefix + "_" + @p Val->getName() + @p Suffix |
303 | /// |
304 | /// otherwise |
305 | /// |
306 | /// @p Prefix + to_string(Number) + @p Suffix |
307 | /// |
308 | /// We ignore the value names by default, as they may change between release |
309 | /// and debug mode and can consequently not be used when aiming for reproducible |
310 | /// builds. However, for debugging named statements are often helpful, hence |
311 | /// we allow their optional use. |
312 | std::string getIslCompatibleName(const std::string &Prefix, |
313 | const llvm::Value *Val, long Number, |
314 | const std::string &Suffix, |
315 | bool UseInstructionNames); |
316 | |
317 | /// Combine Prefix, Name (or Number) and Suffix to an isl-compatible name. |
318 | /// |
319 | /// In case @p UseInstructionNames is set, this function returns: |
320 | /// |
321 | /// @p Prefix + "_" + Name + @p Suffix |
322 | /// |
323 | /// otherwise |
324 | /// |
325 | /// @p Prefix + to_string(Number) + @p Suffix |
326 | /// |
327 | /// We ignore @p Name by default, as they may change between release |
328 | /// and debug mode and can consequently not be used when aiming for reproducible |
329 | /// builds. However, for debugging named statements are often helpful, hence |
330 | /// we allow their optional use. |
331 | std::string getIslCompatibleName(const std::string &Prefix, |
332 | const std::string &Middle, long Number, |
333 | const std::string &Suffix, |
334 | bool UseInstructionNames); |
335 | |
336 | std::string getIslCompatibleName(const std::string &Prefix, |
337 | const std::string &Middle, |
338 | const std::string &Suffix); |
339 | |
340 | inline llvm::DiagnosticInfoOptimizationBase & |
341 | operator<<(llvm::DiagnosticInfoOptimizationBase &OS, |
342 | const isl::union_map &Obj) { |
343 | OS << stringFromIslObj(Obj); |
344 | return OS; |
345 | } |
346 | |
347 | /// Scope guard for code that allows arbitrary isl function to return an error |
348 | /// if the max-operations quota exceeds. |
349 | /// |
350 | /// This allows to opt-in code sections that have known long executions times. |
351 | /// code not in a hot path can continue to assume that no unexpected error |
352 | /// occurs. |
353 | /// |
354 | /// This is typically used inside a nested IslMaxOperationsGuard scope. The |
355 | /// IslMaxOperationsGuard defines the number of allowed base operations for some |
356 | /// code, IslQuotaScope defines where it is allowed to return an error result. |
357 | class IslQuotaScope final { |
358 | isl_ctx *IslCtx; |
359 | int OldOnError; |
360 | |
361 | public: |
362 | IslQuotaScope() : IslCtx(nullptr) {} |
363 | IslQuotaScope(const IslQuotaScope &) = delete; |
364 | IslQuotaScope(IslQuotaScope &&Other) |
365 | : IslCtx(Other.IslCtx), OldOnError(Other.OldOnError) { |
366 | Other.IslCtx = nullptr; |
367 | } |
368 | const IslQuotaScope &operator=(IslQuotaScope &&Other) { |
369 | std::swap(a&: this->IslCtx, b&: Other.IslCtx); |
370 | std::swap(a&: this->OldOnError, b&: Other.OldOnError); |
371 | return *this; |
372 | } |
373 | |
374 | /// Enter a quota-aware scope. |
375 | /// |
376 | /// Should not be used directly. Use IslMaxOperationsGuard::enter() instead. |
377 | explicit IslQuotaScope(isl_ctx *IslCtx, unsigned long LocalMaxOps) |
378 | : IslCtx(IslCtx) { |
379 | assert(IslCtx); |
380 | assert(isl_ctx_get_max_operations(IslCtx) == 0 && "Incorrect nesting" ); |
381 | if (LocalMaxOps == 0) { |
382 | this->IslCtx = nullptr; |
383 | return; |
384 | } |
385 | |
386 | OldOnError = isl_options_get_on_error(ctx: IslCtx); |
387 | isl_options_set_on_error(ctx: IslCtx, ISL_ON_ERROR_CONTINUE); |
388 | isl_ctx_reset_error(ctx: IslCtx); |
389 | isl_ctx_set_max_operations(ctx: IslCtx, max_operations: LocalMaxOps); |
390 | } |
391 | |
392 | ~IslQuotaScope() { |
393 | if (!IslCtx) |
394 | return; |
395 | |
396 | assert(isl_ctx_get_max_operations(IslCtx) > 0 && "Incorrect nesting" ); |
397 | assert(isl_options_get_on_error(IslCtx) == ISL_ON_ERROR_CONTINUE && |
398 | "Incorrect nesting" ); |
399 | isl_ctx_set_max_operations(ctx: IslCtx, max_operations: 0); |
400 | isl_options_set_on_error(ctx: IslCtx, val: OldOnError); |
401 | } |
402 | |
403 | /// Return whether the current quota has exceeded. |
404 | bool hasQuotaExceeded() const { |
405 | if (!IslCtx) |
406 | return false; |
407 | |
408 | return isl_ctx_last_error(ctx: IslCtx) == isl_error_quota; |
409 | } |
410 | }; |
411 | |
412 | /// Scoped limit of ISL operations. |
413 | /// |
414 | /// Limits the number of ISL operations during the lifetime of this object. The |
415 | /// idea is to use this as an RAII guard for the scope where the code is aware |
416 | /// that ISL can return errors even when all input is valid. After leaving the |
417 | /// scope, it will return to the error setting as it was before. That also means |
418 | /// that the error setting should not be changed while in that scope. |
419 | /// |
420 | /// Such scopes are not allowed to be nested because the previous operations |
421 | /// counter cannot be reset to the previous state, or one that adds the |
422 | /// operations while being in the nested scope. Use therefore is only allowed |
423 | /// while currently a no operations-limit is active. |
424 | class IslMaxOperationsGuard final { |
425 | private: |
426 | /// The ISL context to set the operations limit. |
427 | /// |
428 | /// If set to nullptr, there is no need for any action at the end of the |
429 | /// scope. |
430 | isl_ctx *IslCtx; |
431 | |
432 | /// Maximum number of operations for the scope. |
433 | unsigned long LocalMaxOps; |
434 | |
435 | /// When AutoEnter is enabled, holds the IslQuotaScope object. |
436 | IslQuotaScope TopLevelScope; |
437 | |
438 | public: |
439 | /// Enter a max operations scope. |
440 | /// |
441 | /// @param IslCtx The ISL context to set the operations limit for. |
442 | /// @param LocalMaxOps Maximum number of operations allowed in the |
443 | /// scope. If set to zero, no operations limit is enforced. |
444 | /// @param AutoEnter If true, automatically enters an IslQuotaScope such |
445 | /// that isl operations may return quota errors |
446 | /// immediately. If false, only starts the operations |
447 | /// counter, but isl does not return quota errors before |
448 | /// calling enter(). |
449 | IslMaxOperationsGuard(isl_ctx *IslCtx, unsigned long LocalMaxOps, |
450 | bool AutoEnter = true) |
451 | : IslCtx(IslCtx), LocalMaxOps(LocalMaxOps) { |
452 | assert(IslCtx); |
453 | assert(isl_ctx_get_max_operations(IslCtx) == 0 && |
454 | "Nested max operations not supported" ); |
455 | |
456 | // Users of this guard may check whether the last error was isl_error_quota. |
457 | // Reset the last error such that a previous out-of-quota error is not |
458 | // mistaken to have occurred in the in this quota, even if the max number of |
459 | // operations is set to infinite (LocalMaxOps == 0). |
460 | isl_ctx_reset_error(ctx: IslCtx); |
461 | |
462 | if (LocalMaxOps == 0) { |
463 | // No limit on operations; also disable restoring on_error/max_operations. |
464 | this->IslCtx = nullptr; |
465 | return; |
466 | } |
467 | |
468 | isl_ctx_reset_operations(ctx: IslCtx); |
469 | TopLevelScope = enter(AllowReturnNull: AutoEnter); |
470 | } |
471 | |
472 | /// Enter a scope that can handle out-of-quota errors. |
473 | /// |
474 | /// @param AllowReturnNull Whether the scoped code can handle out-of-quota |
475 | /// errors. If false, returns a dummy scope object that |
476 | /// does nothing. |
477 | IslQuotaScope enter(bool AllowReturnNull = true) { |
478 | return AllowReturnNull && IslCtx ? IslQuotaScope(IslCtx, LocalMaxOps) |
479 | : IslQuotaScope(); |
480 | } |
481 | |
482 | /// Return whether the current quota has exceeded. |
483 | bool hasQuotaExceeded() const { |
484 | if (!IslCtx) |
485 | return false; |
486 | |
487 | return isl_ctx_last_error(ctx: IslCtx) == isl_error_quota; |
488 | } |
489 | }; |
490 | } // end namespace polly |
491 | |
492 | #endif |
493 | |