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.
valFromAPInt(isl_ctx * Ctx,const llvm::APInt Int,bool IsSigned)86 inline isl::val valFromAPInt(isl_ctx *Ctx, const llvm::APInt Int,
87 bool IsSigned) {
88 return isl::manage(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.
APIntFromVal(isl::val V)147 inline llvm::APInt APIntFromVal(isl::val V) {
148 return APIntFromVal(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 /// C++ wrapper for isl_*_dump() functions.
190 //@{
191 #define ISL_DUMP_OBJECT(name) \
192 inline void dumpIslObj(const isl::name &Obj) { isl_##name##_dump(Obj.get()); }
193
194 ISL_DUMP_OBJECT(aff)
195 ISL_DUMP_OBJECT(aff_list)
196 ISL_DUMP_OBJECT(ast_expr)
197 ISL_DUMP_OBJECT(ast_node)
198 ISL_DUMP_OBJECT(ast_node_list)
199 ISL_DUMP_OBJECT(basic_map)
200 ISL_DUMP_OBJECT(basic_map_list)
201 ISL_DUMP_OBJECT(basic_set)
202 ISL_DUMP_OBJECT(basic_set_list)
203 ISL_DUMP_OBJECT(constraint)
204 ISL_DUMP_OBJECT(id)
205 ISL_DUMP_OBJECT(id_list)
206 ISL_DUMP_OBJECT(id_to_ast_expr)
207 ISL_DUMP_OBJECT(local_space)
208 ISL_DUMP_OBJECT(map)
209 ISL_DUMP_OBJECT(map_list)
210 ISL_DUMP_OBJECT(multi_aff)
211 ISL_DUMP_OBJECT(multi_pw_aff)
212 ISL_DUMP_OBJECT(multi_union_pw_aff)
213 ISL_DUMP_OBJECT(multi_val)
214 ISL_DUMP_OBJECT(point)
215 ISL_DUMP_OBJECT(pw_aff)
216 ISL_DUMP_OBJECT(pw_aff_list)
217 ISL_DUMP_OBJECT(pw_multi_aff)
218 ISL_DUMP_OBJECT(schedule)
219 ISL_DUMP_OBJECT(schedule_constraints)
220 ISL_DUMP_OBJECT(schedule_node)
221 ISL_DUMP_OBJECT(set)
222 ISL_DUMP_OBJECT(set_list)
223 ISL_DUMP_OBJECT(space)
224 ISL_DUMP_OBJECT(union_map)
225 ISL_DUMP_OBJECT(union_pw_aff)
226 ISL_DUMP_OBJECT(union_pw_aff_list)
227 ISL_DUMP_OBJECT(union_pw_multi_aff)
228 ISL_DUMP_OBJECT(union_set)
229 ISL_DUMP_OBJECT(union_set_list)
230 ISL_DUMP_OBJECT(val)
231 ISL_DUMP_OBJECT(val_list)
232 //@}
233
234 /// Emit the equivaltent of the isl_*_dump output into a raw_ostream.
235 /// @{
236 void dumpIslObj(const isl::schedule_node &Node, llvm::raw_ostream &OS);
237 void dumpIslObj(__isl_keep isl_schedule_node *node, llvm::raw_ostream &OS);
238 /// @}
239
240 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
241 __isl_keep isl_union_map *Map) {
242 OS << polly::stringFromIslObj(Map, "null");
243 return OS;
244 }
245
246 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
247 __isl_keep isl_map *Map) {
248 OS << polly::stringFromIslObj(Map, "null");
249 return OS;
250 }
251
252 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
253 __isl_keep isl_set *Set) {
254 OS << polly::stringFromIslObj(Set, "null");
255 return OS;
256 }
257
258 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
259 __isl_keep isl_pw_aff *Map) {
260 OS << polly::stringFromIslObj(Map, "null");
261 return OS;
262 }
263
264 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
265 __isl_keep isl_pw_multi_aff *PMA) {
266 OS << polly::stringFromIslObj(PMA, "null");
267 return OS;
268 }
269
270 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
271 __isl_keep isl_multi_aff *MA) {
272 OS << polly::stringFromIslObj(MA, "null");
273 return OS;
274 }
275
276 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
277 __isl_keep isl_union_pw_multi_aff *UPMA) {
278 OS << polly::stringFromIslObj(UPMA, "null");
279 return OS;
280 }
281
282 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
283 __isl_keep isl_schedule *Schedule) {
284 OS << polly::stringFromIslObj(Schedule, "null");
285 return OS;
286 }
287
288 inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
289 __isl_keep isl_space *Space) {
290 OS << polly::stringFromIslObj(Space, "null");
291 return OS;
292 }
293
294 /// Combine Prefix, Val (or Number) and Suffix to an isl-compatible name.
295 ///
296 /// In case @p UseInstructionNames is set, this function returns:
297 ///
298 /// @p Prefix + "_" + @p Val->getName() + @p Suffix
299 ///
300 /// otherwise
301 ///
302 /// @p Prefix + to_string(Number) + @p Suffix
303 ///
304 /// We ignore the value names by default, as they may change between release
305 /// and debug mode and can consequently not be used when aiming for reproducible
306 /// builds. However, for debugging named statements are often helpful, hence
307 /// we allow their optional use.
308 std::string getIslCompatibleName(const std::string &Prefix,
309 const llvm::Value *Val, long Number,
310 const std::string &Suffix,
311 bool UseInstructionNames);
312
313 /// Combine Prefix, Name (or Number) and Suffix to an isl-compatible name.
314 ///
315 /// In case @p UseInstructionNames is set, this function returns:
316 ///
317 /// @p Prefix + "_" + Name + @p Suffix
318 ///
319 /// otherwise
320 ///
321 /// @p Prefix + to_string(Number) + @p Suffix
322 ///
323 /// We ignore @p Name by default, as they may change between release
324 /// and debug mode and can consequently not be used when aiming for reproducible
325 /// builds. However, for debugging named statements are often helpful, hence
326 /// we allow their optional use.
327 std::string getIslCompatibleName(const std::string &Prefix,
328 const std::string &Middle, long Number,
329 const std::string &Suffix,
330 bool UseInstructionNames);
331
332 std::string getIslCompatibleName(const std::string &Prefix,
333 const std::string &Middle,
334 const std::string &Suffix);
335
336 inline llvm::DiagnosticInfoOptimizationBase &
337 operator<<(llvm::DiagnosticInfoOptimizationBase &OS,
338 const isl::union_map &Obj) {
339 OS << stringFromIslObj(Obj);
340 return OS;
341 }
342
343 /// Scope guard for code that allows arbitrary isl function to return an error
344 /// if the max-operations quota exceeds.
345 ///
346 /// This allows to opt-in code sections that have known long executions times.
347 /// code not in a hot path can continue to assume that no unexpected error
348 /// occurs.
349 ///
350 /// This is typically used inside a nested IslMaxOperationsGuard scope. The
351 /// IslMaxOperationsGuard defines the number of allowed base operations for some
352 /// code, IslQuotaScope defines where it is allowed to return an error result.
353 class IslQuotaScope {
354 isl_ctx *IslCtx;
355 int OldOnError;
356
357 public:
IslQuotaScope()358 IslQuotaScope() : IslCtx(nullptr) {}
359 IslQuotaScope(const IslQuotaScope &) = delete;
IslQuotaScope(IslQuotaScope && Other)360 IslQuotaScope(IslQuotaScope &&Other)
361 : IslCtx(Other.IslCtx), OldOnError(Other.OldOnError) {
362 Other.IslCtx = nullptr;
363 }
364 const IslQuotaScope &operator=(IslQuotaScope &&Other) {
365 std::swap(this->IslCtx, Other.IslCtx);
366 std::swap(this->OldOnError, Other.OldOnError);
367 return *this;
368 }
369
370 /// Enter a quota-aware scope.
371 ///
372 /// Should not be used directly. Use IslMaxOperationsGuard::enter() instead.
IslQuotaScope(isl_ctx * IslCtx,unsigned long LocalMaxOps)373 explicit IslQuotaScope(isl_ctx *IslCtx, unsigned long LocalMaxOps)
374 : IslCtx(IslCtx) {
375 assert(IslCtx);
376 assert(isl_ctx_get_max_operations(IslCtx) == 0 && "Incorrect nesting");
377 if (LocalMaxOps == 0) {
378 this->IslCtx = nullptr;
379 return;
380 }
381
382 OldOnError = isl_options_get_on_error(IslCtx);
383 isl_options_set_on_error(IslCtx, ISL_ON_ERROR_CONTINUE);
384 isl_ctx_reset_error(IslCtx);
385 isl_ctx_set_max_operations(IslCtx, LocalMaxOps);
386 }
387
~IslQuotaScope()388 ~IslQuotaScope() {
389 if (!IslCtx)
390 return;
391
392 assert(isl_ctx_get_max_operations(IslCtx) > 0 && "Incorrect nesting");
393 assert(isl_options_get_on_error(IslCtx) == ISL_ON_ERROR_CONTINUE &&
394 "Incorrect nesting");
395 isl_ctx_set_max_operations(IslCtx, 0);
396 isl_options_set_on_error(IslCtx, OldOnError);
397 }
398
399 /// Return whether the current quota has exceeded.
hasQuotaExceeded()400 bool hasQuotaExceeded() const {
401 if (!IslCtx)
402 return false;
403
404 return isl_ctx_last_error(IslCtx) == isl_error_quota;
405 }
406 };
407
408 /// Scoped limit of ISL operations.
409 ///
410 /// Limits the number of ISL operations during the lifetime of this object. The
411 /// idea is to use this as an RAII guard for the scope where the code is aware
412 /// that ISL can return errors even when all input is valid. After leaving the
413 /// scope, it will return to the error setting as it was before. That also means
414 /// that the error setting should not be changed while in that scope.
415 ///
416 /// Such scopes are not allowed to be nested because the previous operations
417 /// counter cannot be reset to the previous state, or one that adds the
418 /// operations while being in the nested scope. Use therefore is only allowed
419 /// while currently a no operations-limit is active.
420 class IslMaxOperationsGuard {
421 private:
422 /// The ISL context to set the operations limit.
423 ///
424 /// If set to nullptr, there is no need for any action at the end of the
425 /// scope.
426 isl_ctx *IslCtx;
427
428 /// Maximum number of operations for the scope.
429 unsigned long LocalMaxOps;
430
431 /// When AutoEnter is enabled, holds the IslQuotaScope object.
432 IslQuotaScope TopLevelScope;
433
434 public:
435 /// Enter a max operations scope.
436 ///
437 /// @param IslCtx The ISL context to set the operations limit for.
438 /// @param LocalMaxOps Maximum number of operations allowed in the
439 /// scope. If set to zero, no operations limit is enforced.
440 /// @param AutoEnter If true, automatically enters an IslQuotaScope such
441 /// that isl operations may return quota errors
442 /// immediately. If false, only starts the operations
443 /// counter, but isl does not return quota errors before
444 /// calling enter().
445 IslMaxOperationsGuard(isl_ctx *IslCtx, unsigned long LocalMaxOps,
446 bool AutoEnter = true)
IslCtx(IslCtx)447 : IslCtx(IslCtx), LocalMaxOps(LocalMaxOps) {
448 assert(IslCtx);
449 assert(isl_ctx_get_max_operations(IslCtx) == 0 &&
450 "Nested max operations not supported");
451
452 // Users of this guard may check whether the last error was isl_error_quota.
453 // Reset the last error such that a previous out-of-quota error is not
454 // mistaken to have occurred in the in this quota, even if the max number of
455 // operations is set to infinite (LocalMaxOps == 0).
456 isl_ctx_reset_error(IslCtx);
457
458 if (LocalMaxOps == 0) {
459 // No limit on operations; also disable restoring on_error/max_operations.
460 this->IslCtx = nullptr;
461 return;
462 }
463
464 isl_ctx_reset_operations(IslCtx);
465 TopLevelScope = enter(AutoEnter);
466 }
467
468 /// Enter a scope that can handle out-of-quota errors.
469 ///
470 /// @param AllowReturnNull Whether the scoped code can handle out-of-quota
471 /// errors. If false, returns a dummy scope object that
472 /// does nothing.
473 IslQuotaScope enter(bool AllowReturnNull = true) {
474 return AllowReturnNull && IslCtx ? IslQuotaScope(IslCtx, LocalMaxOps)
475 : IslQuotaScope();
476 }
477
478 /// Return whether the current quota has exceeded.
hasQuotaExceeded()479 bool hasQuotaExceeded() const {
480 if (!IslCtx)
481 return false;
482
483 return isl_ctx_last_error(IslCtx) == isl_error_quota;
484 }
485 };
486 } // end namespace polly
487
488 #endif
489