1fe013be4SDimitry Andric /*
2fe013be4SDimitry Andric  * kmp_collapse.cpp -- loop collapse feature
3fe013be4SDimitry Andric  */
4fe013be4SDimitry Andric 
5fe013be4SDimitry Andric //===----------------------------------------------------------------------===//
6fe013be4SDimitry Andric //
7fe013be4SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
8fe013be4SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
9fe013be4SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
10fe013be4SDimitry Andric //
11fe013be4SDimitry Andric //===----------------------------------------------------------------------===//
12fe013be4SDimitry Andric 
13fe013be4SDimitry Andric #include "kmp.h"
14fe013be4SDimitry Andric #include "kmp_error.h"
15fe013be4SDimitry Andric #include "kmp_i18n.h"
16fe013be4SDimitry Andric #include "kmp_itt.h"
17fe013be4SDimitry Andric #include "kmp_stats.h"
18fe013be4SDimitry Andric #include "kmp_str.h"
19fe013be4SDimitry Andric #include "kmp_collapse.h"
20fe013be4SDimitry Andric 
21fe013be4SDimitry Andric #if OMPT_SUPPORT
22fe013be4SDimitry Andric #include "ompt-specific.h"
23fe013be4SDimitry Andric #endif
24fe013be4SDimitry Andric 
25fe013be4SDimitry Andric // OMPTODO: different style of comments (see kmp_sched)
26fe013be4SDimitry Andric // OMPTODO: OMPT/OMPD
27fe013be4SDimitry Andric 
28fe013be4SDimitry Andric // avoid inadevertently using a library based abs
__kmp_abs(const T val)29fe013be4SDimitry Andric template <typename T> T __kmp_abs(const T val) {
30fe013be4SDimitry Andric   return (val < 0) ? -val : val;
31fe013be4SDimitry Andric }
__kmp_abs(const kmp_uint32 val)32fe013be4SDimitry Andric kmp_uint32 __kmp_abs(const kmp_uint32 val) { return val; }
__kmp_abs(const kmp_uint64 val)33fe013be4SDimitry Andric kmp_uint64 __kmp_abs(const kmp_uint64 val) { return val; }
34fe013be4SDimitry Andric 
35fe013be4SDimitry Andric //----------------------------------------------------------------------------
36fe013be4SDimitry Andric // Common functions for working with rectangular and non-rectangular loops
37fe013be4SDimitry Andric //----------------------------------------------------------------------------
38fe013be4SDimitry Andric 
__kmp_sign(T val)39*c9157d92SDimitry Andric template <typename T> int __kmp_sign(T val) {
40*c9157d92SDimitry Andric   return (T(0) < val) - (val < T(0));
41*c9157d92SDimitry Andric }
42*c9157d92SDimitry Andric 
43*c9157d92SDimitry Andric template <typename T> class CollapseAllocator {
44*c9157d92SDimitry Andric   typedef T *pT;
45*c9157d92SDimitry Andric 
46*c9157d92SDimitry Andric private:
47*c9157d92SDimitry Andric   static const size_t allocaSize = 32; // size limit for stack allocations
48*c9157d92SDimitry Andric                                        // (8 bytes x 4 nested loops)
49*c9157d92SDimitry Andric   char stackAlloc[allocaSize];
50*c9157d92SDimitry Andric   static constexpr size_t maxElemCount = allocaSize / sizeof(T);
51*c9157d92SDimitry Andric   pT pTAlloc;
52*c9157d92SDimitry Andric 
53*c9157d92SDimitry Andric public:
CollapseAllocator(size_t n)54*c9157d92SDimitry Andric   CollapseAllocator(size_t n) : pTAlloc(reinterpret_cast<pT>(stackAlloc)) {
55*c9157d92SDimitry Andric     if (n > maxElemCount) {
56*c9157d92SDimitry Andric       pTAlloc = reinterpret_cast<pT>(__kmp_allocate(n * sizeof(T)));
57*c9157d92SDimitry Andric     }
58*c9157d92SDimitry Andric   }
~CollapseAllocator()59*c9157d92SDimitry Andric   ~CollapseAllocator() {
60*c9157d92SDimitry Andric     if (pTAlloc != reinterpret_cast<pT>(stackAlloc)) {
61*c9157d92SDimitry Andric       __kmp_free(pTAlloc);
62*c9157d92SDimitry Andric     }
63*c9157d92SDimitry Andric   }
operator [](int index)64*c9157d92SDimitry Andric   T &operator[](int index) { return pTAlloc[index]; }
operator const pT()65*c9157d92SDimitry Andric   operator const pT() { return pTAlloc; }
66*c9157d92SDimitry Andric };
67fe013be4SDimitry Andric 
68fe013be4SDimitry Andric //----------Loop canonicalization---------------------------------------------
69fe013be4SDimitry Andric 
70fe013be4SDimitry Andric // For loop nest (any shape):
71fe013be4SDimitry Andric // convert != to < or >;
72fe013be4SDimitry Andric // switch from using < or > to <= or >=.
73fe013be4SDimitry Andric // "bounds" array has to be allocated per thread.
74fe013be4SDimitry Andric // All other internal functions will work only with canonicalized loops.
75fe013be4SDimitry Andric template <typename T>
kmp_canonicalize_one_loop_XX(ident_t * loc,bounds_infoXX_template<T> * bounds)76fe013be4SDimitry Andric void kmp_canonicalize_one_loop_XX(
77fe013be4SDimitry Andric     ident_t *loc,
78fe013be4SDimitry Andric     /*in/out*/ bounds_infoXX_template<T> *bounds) {
79fe013be4SDimitry Andric 
80fe013be4SDimitry Andric   if (__kmp_env_consistency_check) {
81fe013be4SDimitry Andric     if (bounds->step == 0) {
82fe013be4SDimitry Andric       __kmp_error_construct(kmp_i18n_msg_CnsLoopIncrZeroProhibited, ct_pdo,
83fe013be4SDimitry Andric                             loc);
84fe013be4SDimitry Andric     }
85fe013be4SDimitry Andric   }
86fe013be4SDimitry Andric 
87fe013be4SDimitry Andric   if (bounds->comparison == comparison_t::comp_not_eq) {
88fe013be4SDimitry Andric     // We can convert this to < or >, depends on the sign of the step:
89fe013be4SDimitry Andric     if (bounds->step > 0) {
90fe013be4SDimitry Andric       bounds->comparison = comparison_t::comp_less;
91fe013be4SDimitry Andric     } else {
92fe013be4SDimitry Andric       bounds->comparison = comparison_t::comp_greater;
93fe013be4SDimitry Andric     }
94fe013be4SDimitry Andric   }
95fe013be4SDimitry Andric 
96fe013be4SDimitry Andric   if (bounds->comparison == comparison_t::comp_less) {
97fe013be4SDimitry Andric     // Note: ub0 can be unsigned. Should be Ok to hit overflow here,
98fe013be4SDimitry Andric     // because ub0 + ub1*j should be still positive (otherwise loop was not
99fe013be4SDimitry Andric     // well formed)
100fe013be4SDimitry Andric     bounds->ub0 -= 1;
101fe013be4SDimitry Andric     bounds->comparison = comparison_t::comp_less_or_eq;
102fe013be4SDimitry Andric   } else if (bounds->comparison == comparison_t::comp_greater) {
103fe013be4SDimitry Andric     bounds->ub0 += 1;
104fe013be4SDimitry Andric     bounds->comparison = comparison_t::comp_greater_or_eq;
105fe013be4SDimitry Andric   }
106fe013be4SDimitry Andric }
107fe013be4SDimitry Andric 
108fe013be4SDimitry Andric // Canonicalize loop nest. original_bounds_nest is an array of length n.
kmp_canonicalize_loop_nest(ident_t * loc,bounds_info_t * original_bounds_nest,kmp_index_t n)109fe013be4SDimitry Andric void kmp_canonicalize_loop_nest(ident_t *loc,
110fe013be4SDimitry Andric                                 /*in/out*/ bounds_info_t *original_bounds_nest,
111fe013be4SDimitry Andric                                 kmp_index_t n) {
112fe013be4SDimitry Andric 
113fe013be4SDimitry Andric   for (kmp_index_t ind = 0; ind < n; ++ind) {
114fe013be4SDimitry Andric     auto bounds = &(original_bounds_nest[ind]);
115fe013be4SDimitry Andric 
116fe013be4SDimitry Andric     switch (bounds->loop_type) {
117fe013be4SDimitry Andric     case loop_type_t::loop_type_int32:
118fe013be4SDimitry Andric       kmp_canonicalize_one_loop_XX<kmp_int32>(
119fe013be4SDimitry Andric           loc,
120fe013be4SDimitry Andric           /*in/out*/ (bounds_infoXX_template<kmp_int32> *)(bounds));
121fe013be4SDimitry Andric       break;
122fe013be4SDimitry Andric     case loop_type_t::loop_type_uint32:
123fe013be4SDimitry Andric       kmp_canonicalize_one_loop_XX<kmp_uint32>(
124fe013be4SDimitry Andric           loc,
125fe013be4SDimitry Andric           /*in/out*/ (bounds_infoXX_template<kmp_uint32> *)(bounds));
126fe013be4SDimitry Andric       break;
127fe013be4SDimitry Andric     case loop_type_t::loop_type_int64:
128fe013be4SDimitry Andric       kmp_canonicalize_one_loop_XX<kmp_int64>(
129fe013be4SDimitry Andric           loc,
130fe013be4SDimitry Andric           /*in/out*/ (bounds_infoXX_template<kmp_int64> *)(bounds));
131fe013be4SDimitry Andric       break;
132fe013be4SDimitry Andric     case loop_type_t::loop_type_uint64:
133fe013be4SDimitry Andric       kmp_canonicalize_one_loop_XX<kmp_uint64>(
134fe013be4SDimitry Andric           loc,
135fe013be4SDimitry Andric           /*in/out*/ (bounds_infoXX_template<kmp_uint64> *)(bounds));
136fe013be4SDimitry Andric       break;
137fe013be4SDimitry Andric     default:
138fe013be4SDimitry Andric       KMP_ASSERT(false);
139fe013be4SDimitry Andric     }
140fe013be4SDimitry Andric   }
141fe013be4SDimitry Andric }
142fe013be4SDimitry Andric 
143fe013be4SDimitry Andric //----------Calculating trip count on one level-------------------------------
144fe013be4SDimitry Andric 
145fe013be4SDimitry Andric // Calculate trip count on this loop level.
146fe013be4SDimitry Andric // We do this either for a rectangular loop nest,
147fe013be4SDimitry Andric // or after an adjustment bringing the loops to a parallelepiped shape.
148fe013be4SDimitry Andric // This number should not depend on the value of outer IV
149fe013be4SDimitry Andric // even if the formular has lb1 and ub1.
150fe013be4SDimitry Andric // Note: for non-rectangular loops don't use span for this, it's too big.
151fe013be4SDimitry Andric 
152fe013be4SDimitry Andric template <typename T>
kmp_calculate_trip_count_XX(bounds_infoXX_template<T> * bounds)153fe013be4SDimitry Andric kmp_loop_nest_iv_t kmp_calculate_trip_count_XX(
154fe013be4SDimitry Andric     /*in/out*/ bounds_infoXX_template<T> *bounds) {
155fe013be4SDimitry Andric 
156fe013be4SDimitry Andric   if (bounds->comparison == comparison_t::comp_less_or_eq) {
157fe013be4SDimitry Andric     if (bounds->ub0 < bounds->lb0) {
158fe013be4SDimitry Andric       // Note: after this we don't need to calculate inner loops,
159fe013be4SDimitry Andric       // but that should be an edge case:
160fe013be4SDimitry Andric       bounds->trip_count = 0;
161fe013be4SDimitry Andric     } else {
162fe013be4SDimitry Andric       // ub - lb may exceed signed type range; we need to cast to
163fe013be4SDimitry Andric       // kmp_loop_nest_iv_t anyway
164fe013be4SDimitry Andric       bounds->trip_count =
165fe013be4SDimitry Andric           static_cast<kmp_loop_nest_iv_t>(bounds->ub0 - bounds->lb0) /
166fe013be4SDimitry Andric               __kmp_abs(bounds->step) +
167fe013be4SDimitry Andric           1;
168fe013be4SDimitry Andric     }
169fe013be4SDimitry Andric   } else if (bounds->comparison == comparison_t::comp_greater_or_eq) {
170fe013be4SDimitry Andric     if (bounds->lb0 < bounds->ub0) {
171fe013be4SDimitry Andric       // Note: after this we don't need to calculate inner loops,
172fe013be4SDimitry Andric       // but that should be an edge case:
173fe013be4SDimitry Andric       bounds->trip_count = 0;
174fe013be4SDimitry Andric     } else {
175fe013be4SDimitry Andric       // lb - ub may exceed signed type range; we need to cast to
176fe013be4SDimitry Andric       // kmp_loop_nest_iv_t anyway
177fe013be4SDimitry Andric       bounds->trip_count =
178fe013be4SDimitry Andric           static_cast<kmp_loop_nest_iv_t>(bounds->lb0 - bounds->ub0) /
179fe013be4SDimitry Andric               __kmp_abs(bounds->step) +
180fe013be4SDimitry Andric           1;
181fe013be4SDimitry Andric     }
182fe013be4SDimitry Andric   } else {
183fe013be4SDimitry Andric     KMP_ASSERT(false);
184fe013be4SDimitry Andric   }
185fe013be4SDimitry Andric   return bounds->trip_count;
186fe013be4SDimitry Andric }
187fe013be4SDimitry Andric 
188fe013be4SDimitry Andric // Calculate trip count on this loop level.
kmp_calculate_trip_count(bounds_info_t * bounds)189fe013be4SDimitry Andric kmp_loop_nest_iv_t kmp_calculate_trip_count(/*in/out*/ bounds_info_t *bounds) {
190fe013be4SDimitry Andric 
191fe013be4SDimitry Andric   kmp_loop_nest_iv_t trip_count = 0;
192fe013be4SDimitry Andric 
193fe013be4SDimitry Andric   switch (bounds->loop_type) {
194fe013be4SDimitry Andric   case loop_type_t::loop_type_int32:
195fe013be4SDimitry Andric     trip_count = kmp_calculate_trip_count_XX<kmp_int32>(
196fe013be4SDimitry Andric         /*in/out*/ (bounds_infoXX_template<kmp_int32> *)(bounds));
197fe013be4SDimitry Andric     break;
198fe013be4SDimitry Andric   case loop_type_t::loop_type_uint32:
199fe013be4SDimitry Andric     trip_count = kmp_calculate_trip_count_XX<kmp_uint32>(
200fe013be4SDimitry Andric         /*in/out*/ (bounds_infoXX_template<kmp_uint32> *)(bounds));
201fe013be4SDimitry Andric     break;
202fe013be4SDimitry Andric   case loop_type_t::loop_type_int64:
203fe013be4SDimitry Andric     trip_count = kmp_calculate_trip_count_XX<kmp_int64>(
204fe013be4SDimitry Andric         /*in/out*/ (bounds_infoXX_template<kmp_int64> *)(bounds));
205fe013be4SDimitry Andric     break;
206fe013be4SDimitry Andric   case loop_type_t::loop_type_uint64:
207fe013be4SDimitry Andric     trip_count = kmp_calculate_trip_count_XX<kmp_uint64>(
208fe013be4SDimitry Andric         /*in/out*/ (bounds_infoXX_template<kmp_uint64> *)(bounds));
209fe013be4SDimitry Andric     break;
210fe013be4SDimitry Andric   default:
211fe013be4SDimitry Andric     KMP_ASSERT(false);
212fe013be4SDimitry Andric   }
213fe013be4SDimitry Andric 
214fe013be4SDimitry Andric   return trip_count;
215fe013be4SDimitry Andric }
216fe013be4SDimitry Andric 
217fe013be4SDimitry Andric //----------Trim original iv according to its type----------------------------
218fe013be4SDimitry Andric 
219fe013be4SDimitry Andric // Trim original iv according to its type.
220fe013be4SDimitry Andric // Return kmp_uint64 value which can be easily used in all internal calculations
221fe013be4SDimitry Andric // And can be statically cast back to original type in user code.
kmp_fix_iv(loop_type_t loop_iv_type,kmp_uint64 original_iv)222fe013be4SDimitry Andric kmp_uint64 kmp_fix_iv(loop_type_t loop_iv_type, kmp_uint64 original_iv) {
223fe013be4SDimitry Andric   kmp_uint64 res = 0;
224fe013be4SDimitry Andric 
225fe013be4SDimitry Andric   switch (loop_iv_type) {
226fe013be4SDimitry Andric   case loop_type_t::loop_type_int8:
227fe013be4SDimitry Andric     res = static_cast<kmp_uint64>(static_cast<kmp_int8>(original_iv));
228fe013be4SDimitry Andric     break;
229fe013be4SDimitry Andric   case loop_type_t::loop_type_uint8:
230fe013be4SDimitry Andric     res = static_cast<kmp_uint64>(static_cast<kmp_uint8>(original_iv));
231fe013be4SDimitry Andric     break;
232fe013be4SDimitry Andric   case loop_type_t::loop_type_int16:
233fe013be4SDimitry Andric     res = static_cast<kmp_uint64>(static_cast<kmp_int16>(original_iv));
234fe013be4SDimitry Andric     break;
235fe013be4SDimitry Andric   case loop_type_t::loop_type_uint16:
236fe013be4SDimitry Andric     res = static_cast<kmp_uint64>(static_cast<kmp_uint16>(original_iv));
237fe013be4SDimitry Andric     break;
238fe013be4SDimitry Andric   case loop_type_t::loop_type_int32:
239fe013be4SDimitry Andric     res = static_cast<kmp_uint64>(static_cast<kmp_int32>(original_iv));
240fe013be4SDimitry Andric     break;
241fe013be4SDimitry Andric   case loop_type_t::loop_type_uint32:
242fe013be4SDimitry Andric     res = static_cast<kmp_uint64>(static_cast<kmp_uint32>(original_iv));
243fe013be4SDimitry Andric     break;
244fe013be4SDimitry Andric   case loop_type_t::loop_type_int64:
245fe013be4SDimitry Andric     res = static_cast<kmp_uint64>(static_cast<kmp_int64>(original_iv));
246fe013be4SDimitry Andric     break;
247fe013be4SDimitry Andric   case loop_type_t::loop_type_uint64:
248fe013be4SDimitry Andric     res = static_cast<kmp_uint64>(original_iv);
249fe013be4SDimitry Andric     break;
250fe013be4SDimitry Andric   default:
251fe013be4SDimitry Andric     KMP_ASSERT(false);
252fe013be4SDimitry Andric   }
253fe013be4SDimitry Andric 
254fe013be4SDimitry Andric   return res;
255fe013be4SDimitry Andric }
256fe013be4SDimitry Andric 
257fe013be4SDimitry Andric //----------Compare two IVs (remember they have a type)-----------------------
258fe013be4SDimitry Andric 
kmp_ivs_eq(loop_type_t loop_iv_type,kmp_uint64 original_iv1,kmp_uint64 original_iv2)259fe013be4SDimitry Andric bool kmp_ivs_eq(loop_type_t loop_iv_type, kmp_uint64 original_iv1,
260fe013be4SDimitry Andric                 kmp_uint64 original_iv2) {
261fe013be4SDimitry Andric   bool res = false;
262fe013be4SDimitry Andric 
263fe013be4SDimitry Andric   switch (loop_iv_type) {
264fe013be4SDimitry Andric   case loop_type_t::loop_type_int8:
265fe013be4SDimitry Andric     res = static_cast<kmp_int8>(original_iv1) ==
266fe013be4SDimitry Andric           static_cast<kmp_int8>(original_iv2);
267fe013be4SDimitry Andric     break;
268fe013be4SDimitry Andric   case loop_type_t::loop_type_uint8:
269fe013be4SDimitry Andric     res = static_cast<kmp_uint8>(original_iv1) ==
270fe013be4SDimitry Andric           static_cast<kmp_uint8>(original_iv2);
271fe013be4SDimitry Andric     break;
272fe013be4SDimitry Andric   case loop_type_t::loop_type_int16:
273fe013be4SDimitry Andric     res = static_cast<kmp_int16>(original_iv1) ==
274fe013be4SDimitry Andric           static_cast<kmp_int16>(original_iv2);
275fe013be4SDimitry Andric     break;
276fe013be4SDimitry Andric   case loop_type_t::loop_type_uint16:
277fe013be4SDimitry Andric     res = static_cast<kmp_uint16>(original_iv1) ==
278fe013be4SDimitry Andric           static_cast<kmp_uint16>(original_iv2);
279fe013be4SDimitry Andric     break;
280fe013be4SDimitry Andric   case loop_type_t::loop_type_int32:
281fe013be4SDimitry Andric     res = static_cast<kmp_int32>(original_iv1) ==
282fe013be4SDimitry Andric           static_cast<kmp_int32>(original_iv2);
283fe013be4SDimitry Andric     break;
284fe013be4SDimitry Andric   case loop_type_t::loop_type_uint32:
285fe013be4SDimitry Andric     res = static_cast<kmp_uint32>(original_iv1) ==
286fe013be4SDimitry Andric           static_cast<kmp_uint32>(original_iv2);
287fe013be4SDimitry Andric     break;
288fe013be4SDimitry Andric   case loop_type_t::loop_type_int64:
289fe013be4SDimitry Andric     res = static_cast<kmp_int64>(original_iv1) ==
290fe013be4SDimitry Andric           static_cast<kmp_int64>(original_iv2);
291fe013be4SDimitry Andric     break;
292fe013be4SDimitry Andric   case loop_type_t::loop_type_uint64:
293fe013be4SDimitry Andric     res = static_cast<kmp_uint64>(original_iv1) ==
294fe013be4SDimitry Andric           static_cast<kmp_uint64>(original_iv2);
295fe013be4SDimitry Andric     break;
296fe013be4SDimitry Andric   default:
297fe013be4SDimitry Andric     KMP_ASSERT(false);
298fe013be4SDimitry Andric   }
299fe013be4SDimitry Andric 
300fe013be4SDimitry Andric   return res;
301fe013be4SDimitry Andric }
302fe013be4SDimitry Andric 
303fe013be4SDimitry Andric //----------Calculate original iv on one level--------------------------------
304fe013be4SDimitry Andric 
305fe013be4SDimitry Andric // Return true if the point fits into upper bounds on this level,
306fe013be4SDimitry Andric // false otherwise
307fe013be4SDimitry Andric template <typename T>
kmp_iv_is_in_upper_bound_XX(const bounds_infoXX_template<T> * bounds,const kmp_point_t original_ivs,kmp_index_t ind)308fe013be4SDimitry Andric bool kmp_iv_is_in_upper_bound_XX(const bounds_infoXX_template<T> *bounds,
309fe013be4SDimitry Andric                                  const kmp_point_t original_ivs,
310fe013be4SDimitry Andric                                  kmp_index_t ind) {
311fe013be4SDimitry Andric 
312fe013be4SDimitry Andric   T iv = static_cast<T>(original_ivs[ind]);
313fe013be4SDimitry Andric   T outer_iv = static_cast<T>(original_ivs[bounds->outer_iv]);
314fe013be4SDimitry Andric 
315fe013be4SDimitry Andric   if (((bounds->comparison == comparison_t::comp_less_or_eq) &&
316fe013be4SDimitry Andric        (iv > (bounds->ub0 + bounds->ub1 * outer_iv))) ||
317fe013be4SDimitry Andric       ((bounds->comparison == comparison_t::comp_greater_or_eq) &&
318fe013be4SDimitry Andric        (iv < (bounds->ub0 + bounds->ub1 * outer_iv)))) {
319fe013be4SDimitry Andric     // The calculated point is outside of loop upper boundary:
320fe013be4SDimitry Andric     return false;
321fe013be4SDimitry Andric   }
322fe013be4SDimitry Andric 
323fe013be4SDimitry Andric   return true;
324fe013be4SDimitry Andric }
325fe013be4SDimitry Andric 
326fe013be4SDimitry Andric // Calculate one iv corresponding to iteration on the level ind.
327fe013be4SDimitry Andric // Return true if it fits into lower-upper bounds on this level
328fe013be4SDimitry Andric // (if not, we need to re-calculate)
329fe013be4SDimitry Andric template <typename T>
kmp_calc_one_iv_XX(const bounds_infoXX_template<T> * bounds,kmp_point_t original_ivs,const kmp_iterations_t iterations,kmp_index_t ind,bool start_with_lower_bound,bool checkBounds)330fe013be4SDimitry Andric bool kmp_calc_one_iv_XX(const bounds_infoXX_template<T> *bounds,
331fe013be4SDimitry Andric                         /*in/out*/ kmp_point_t original_ivs,
332fe013be4SDimitry Andric                         const kmp_iterations_t iterations, kmp_index_t ind,
333fe013be4SDimitry Andric                         bool start_with_lower_bound, bool checkBounds) {
334fe013be4SDimitry Andric 
335fe013be4SDimitry Andric   kmp_uint64 temp = 0;
336fe013be4SDimitry Andric   T outer_iv = static_cast<T>(original_ivs[bounds->outer_iv]);
337fe013be4SDimitry Andric 
338fe013be4SDimitry Andric   if (start_with_lower_bound) {
339fe013be4SDimitry Andric     // we moved to the next iteration on one of outer loops, should start
340fe013be4SDimitry Andric     // with the lower bound here:
341fe013be4SDimitry Andric     temp = bounds->lb0 + bounds->lb1 * outer_iv;
342fe013be4SDimitry Andric   } else {
343fe013be4SDimitry Andric     auto iteration = iterations[ind];
344fe013be4SDimitry Andric     temp = bounds->lb0 + bounds->lb1 * outer_iv + iteration * bounds->step;
345fe013be4SDimitry Andric   }
346fe013be4SDimitry Andric 
347fe013be4SDimitry Andric   // Now trim original iv according to its type:
348fe013be4SDimitry Andric   original_ivs[ind] = kmp_fix_iv(bounds->loop_iv_type, temp);
349fe013be4SDimitry Andric 
350fe013be4SDimitry Andric   if (checkBounds) {
351fe013be4SDimitry Andric     return kmp_iv_is_in_upper_bound_XX(bounds, original_ivs, ind);
352fe013be4SDimitry Andric   } else {
353fe013be4SDimitry Andric     return true;
354fe013be4SDimitry Andric   }
355fe013be4SDimitry Andric }
356fe013be4SDimitry Andric 
kmp_calc_one_iv(const bounds_info_t * bounds,kmp_point_t original_ivs,const kmp_iterations_t iterations,kmp_index_t ind,bool start_with_lower_bound,bool checkBounds)357fe013be4SDimitry Andric bool kmp_calc_one_iv(const bounds_info_t *bounds,
358fe013be4SDimitry Andric                      /*in/out*/ kmp_point_t original_ivs,
359fe013be4SDimitry Andric                      const kmp_iterations_t iterations, kmp_index_t ind,
360fe013be4SDimitry Andric                      bool start_with_lower_bound, bool checkBounds) {
361fe013be4SDimitry Andric 
362fe013be4SDimitry Andric   switch (bounds->loop_type) {
363fe013be4SDimitry Andric   case loop_type_t::loop_type_int32:
364fe013be4SDimitry Andric     return kmp_calc_one_iv_XX<kmp_int32>(
365fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_int32> *)(bounds),
366fe013be4SDimitry Andric         /*in/out*/ original_ivs, iterations, ind, start_with_lower_bound,
367fe013be4SDimitry Andric         checkBounds);
368fe013be4SDimitry Andric     break;
369fe013be4SDimitry Andric   case loop_type_t::loop_type_uint32:
370fe013be4SDimitry Andric     return kmp_calc_one_iv_XX<kmp_uint32>(
371fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_uint32> *)(bounds),
372fe013be4SDimitry Andric         /*in/out*/ original_ivs, iterations, ind, start_with_lower_bound,
373fe013be4SDimitry Andric         checkBounds);
374fe013be4SDimitry Andric     break;
375fe013be4SDimitry Andric   case loop_type_t::loop_type_int64:
376fe013be4SDimitry Andric     return kmp_calc_one_iv_XX<kmp_int64>(
377fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_int64> *)(bounds),
378fe013be4SDimitry Andric         /*in/out*/ original_ivs, iterations, ind, start_with_lower_bound,
379fe013be4SDimitry Andric         checkBounds);
380fe013be4SDimitry Andric     break;
381fe013be4SDimitry Andric   case loop_type_t::loop_type_uint64:
382fe013be4SDimitry Andric     return kmp_calc_one_iv_XX<kmp_uint64>(
383fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_uint64> *)(bounds),
384fe013be4SDimitry Andric         /*in/out*/ original_ivs, iterations, ind, start_with_lower_bound,
385fe013be4SDimitry Andric         checkBounds);
386fe013be4SDimitry Andric     break;
387fe013be4SDimitry Andric   default:
388fe013be4SDimitry Andric     KMP_ASSERT(false);
389fe013be4SDimitry Andric     return false;
390fe013be4SDimitry Andric   }
391fe013be4SDimitry Andric }
392fe013be4SDimitry Andric 
393fe013be4SDimitry Andric //----------Calculate original iv on one level for rectangular loop nest------
394fe013be4SDimitry Andric 
395fe013be4SDimitry Andric // Calculate one iv corresponding to iteration on the level ind.
396fe013be4SDimitry Andric // Return true if it fits into lower-upper bounds on this level
397fe013be4SDimitry Andric // (if not, we need to re-calculate)
398fe013be4SDimitry Andric template <typename T>
kmp_calc_one_iv_rectang_XX(const bounds_infoXX_template<T> * bounds,kmp_uint64 * original_ivs,const kmp_iterations_t iterations,kmp_index_t ind)399fe013be4SDimitry Andric void kmp_calc_one_iv_rectang_XX(const bounds_infoXX_template<T> *bounds,
400fe013be4SDimitry Andric                                 /*in/out*/ kmp_uint64 *original_ivs,
401fe013be4SDimitry Andric                                 const kmp_iterations_t iterations,
402fe013be4SDimitry Andric                                 kmp_index_t ind) {
403fe013be4SDimitry Andric 
404fe013be4SDimitry Andric   auto iteration = iterations[ind];
405fe013be4SDimitry Andric 
406fe013be4SDimitry Andric   kmp_uint64 temp =
407fe013be4SDimitry Andric       bounds->lb0 +
408fe013be4SDimitry Andric       bounds->lb1 * static_cast<T>(original_ivs[bounds->outer_iv]) +
409fe013be4SDimitry Andric       iteration * bounds->step;
410fe013be4SDimitry Andric 
411fe013be4SDimitry Andric   // Now trim original iv according to its type:
412fe013be4SDimitry Andric   original_ivs[ind] = kmp_fix_iv(bounds->loop_iv_type, temp);
413fe013be4SDimitry Andric }
414fe013be4SDimitry Andric 
kmp_calc_one_iv_rectang(const bounds_info_t * bounds,kmp_uint64 * original_ivs,const kmp_iterations_t iterations,kmp_index_t ind)415fe013be4SDimitry Andric void kmp_calc_one_iv_rectang(const bounds_info_t *bounds,
416fe013be4SDimitry Andric                              /*in/out*/ kmp_uint64 *original_ivs,
417fe013be4SDimitry Andric                              const kmp_iterations_t iterations,
418fe013be4SDimitry Andric                              kmp_index_t ind) {
419fe013be4SDimitry Andric 
420fe013be4SDimitry Andric   switch (bounds->loop_type) {
421fe013be4SDimitry Andric   case loop_type_t::loop_type_int32:
422fe013be4SDimitry Andric     kmp_calc_one_iv_rectang_XX<kmp_int32>(
423fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_int32> *)(bounds),
424fe013be4SDimitry Andric         /*in/out*/ original_ivs, iterations, ind);
425fe013be4SDimitry Andric     break;
426fe013be4SDimitry Andric   case loop_type_t::loop_type_uint32:
427fe013be4SDimitry Andric     kmp_calc_one_iv_rectang_XX<kmp_uint32>(
428fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_uint32> *)(bounds),
429fe013be4SDimitry Andric         /*in/out*/ original_ivs, iterations, ind);
430fe013be4SDimitry Andric     break;
431fe013be4SDimitry Andric   case loop_type_t::loop_type_int64:
432fe013be4SDimitry Andric     kmp_calc_one_iv_rectang_XX<kmp_int64>(
433fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_int64> *)(bounds),
434fe013be4SDimitry Andric         /*in/out*/ original_ivs, iterations, ind);
435fe013be4SDimitry Andric     break;
436fe013be4SDimitry Andric   case loop_type_t::loop_type_uint64:
437fe013be4SDimitry Andric     kmp_calc_one_iv_rectang_XX<kmp_uint64>(
438fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_uint64> *)(bounds),
439fe013be4SDimitry Andric         /*in/out*/ original_ivs, iterations, ind);
440fe013be4SDimitry Andric     break;
441fe013be4SDimitry Andric   default:
442fe013be4SDimitry Andric     KMP_ASSERT(false);
443fe013be4SDimitry Andric   }
444fe013be4SDimitry Andric }
445fe013be4SDimitry Andric 
446fe013be4SDimitry Andric //----------------------------------------------------------------------------
447fe013be4SDimitry Andric // Rectangular loop nest
448fe013be4SDimitry Andric //----------------------------------------------------------------------------
449fe013be4SDimitry Andric 
450fe013be4SDimitry Andric //----------Canonicalize loop nest and calculate trip count-------------------
451fe013be4SDimitry Andric 
452fe013be4SDimitry Andric // Canonicalize loop nest and calculate overall trip count.
453fe013be4SDimitry Andric // "bounds_nest" has to be allocated per thread.
454fe013be4SDimitry Andric // API will modify original bounds_nest array to bring it to a canonical form
455fe013be4SDimitry Andric // (only <= and >=, no !=, <, >). If the original loop nest was already in a
456fe013be4SDimitry Andric // canonical form there will be no changes to bounds in bounds_nest array
457fe013be4SDimitry Andric // (only trip counts will be calculated).
458fe013be4SDimitry Andric // Returns trip count of overall space.
459fe013be4SDimitry Andric extern "C" kmp_loop_nest_iv_t
__kmpc_process_loop_nest_rectang(ident_t * loc,kmp_int32 gtid,bounds_info_t * original_bounds_nest,kmp_index_t n)460fe013be4SDimitry Andric __kmpc_process_loop_nest_rectang(ident_t *loc, kmp_int32 gtid,
461fe013be4SDimitry Andric                                  /*in/out*/ bounds_info_t *original_bounds_nest,
462fe013be4SDimitry Andric                                  kmp_index_t n) {
463fe013be4SDimitry Andric 
464fe013be4SDimitry Andric   kmp_canonicalize_loop_nest(loc, /*in/out*/ original_bounds_nest, n);
465fe013be4SDimitry Andric 
466fe013be4SDimitry Andric   kmp_loop_nest_iv_t total = 1;
467fe013be4SDimitry Andric 
468fe013be4SDimitry Andric   for (kmp_index_t ind = 0; ind < n; ++ind) {
469fe013be4SDimitry Andric     auto bounds = &(original_bounds_nest[ind]);
470fe013be4SDimitry Andric 
471fe013be4SDimitry Andric     kmp_loop_nest_iv_t trip_count = kmp_calculate_trip_count(/*in/out*/ bounds);
472fe013be4SDimitry Andric     total *= trip_count;
473fe013be4SDimitry Andric   }
474fe013be4SDimitry Andric 
475fe013be4SDimitry Andric   return total;
476fe013be4SDimitry Andric }
477fe013be4SDimitry Andric 
478fe013be4SDimitry Andric //----------Calculate old induction variables---------------------------------
479fe013be4SDimitry Andric 
480fe013be4SDimitry Andric // Calculate old induction variables corresponding to overall new_iv.
481fe013be4SDimitry Andric // Note: original IV will be returned as if it had kmp_uint64 type,
482fe013be4SDimitry Andric // will have to be converted to original type in user code.
483fe013be4SDimitry Andric // Note: trip counts should be already calculated by
484fe013be4SDimitry Andric // __kmpc_process_loop_nest_rectang.
485fe013be4SDimitry Andric // OMPTODO: special case 2, 3 nested loops: either do different
486fe013be4SDimitry Andric // interface without array or possibly template this over n
487fe013be4SDimitry Andric extern "C" void
__kmpc_calc_original_ivs_rectang(ident_t * loc,kmp_loop_nest_iv_t new_iv,const bounds_info_t * original_bounds_nest,kmp_uint64 * original_ivs,kmp_index_t n)488fe013be4SDimitry Andric __kmpc_calc_original_ivs_rectang(ident_t *loc, kmp_loop_nest_iv_t new_iv,
489fe013be4SDimitry Andric                                  const bounds_info_t *original_bounds_nest,
490fe013be4SDimitry Andric                                  /*out*/ kmp_uint64 *original_ivs,
491fe013be4SDimitry Andric                                  kmp_index_t n) {
492fe013be4SDimitry Andric 
493*c9157d92SDimitry Andric   CollapseAllocator<kmp_loop_nest_iv_t> iterations(n);
494fe013be4SDimitry Andric 
495fe013be4SDimitry Andric   // First, calc corresponding iteration in every original loop:
496fe013be4SDimitry Andric   for (kmp_index_t ind = n; ind > 0;) {
497fe013be4SDimitry Andric     --ind;
498fe013be4SDimitry Andric     auto bounds = &(original_bounds_nest[ind]);
499fe013be4SDimitry Andric 
500fe013be4SDimitry Andric     // should be optimized to OPDIVREM:
501fe013be4SDimitry Andric     auto temp = new_iv / bounds->trip_count;
502fe013be4SDimitry Andric     auto iteration = new_iv % bounds->trip_count;
503fe013be4SDimitry Andric     new_iv = temp;
504fe013be4SDimitry Andric 
505fe013be4SDimitry Andric     iterations[ind] = iteration;
506fe013be4SDimitry Andric   }
507fe013be4SDimitry Andric   KMP_ASSERT(new_iv == 0);
508fe013be4SDimitry Andric 
509fe013be4SDimitry Andric   for (kmp_index_t ind = 0; ind < n; ++ind) {
510fe013be4SDimitry Andric     auto bounds = &(original_bounds_nest[ind]);
511fe013be4SDimitry Andric 
512fe013be4SDimitry Andric     kmp_calc_one_iv_rectang(bounds, /*in/out*/ original_ivs, iterations, ind);
513fe013be4SDimitry Andric   }
514fe013be4SDimitry Andric }
515fe013be4SDimitry Andric 
516fe013be4SDimitry Andric //----------------------------------------------------------------------------
517fe013be4SDimitry Andric // Non-rectangular loop nest
518fe013be4SDimitry Andric //----------------------------------------------------------------------------
519fe013be4SDimitry Andric 
520fe013be4SDimitry Andric //----------Calculate maximum possible span of iv values on one level---------
521fe013be4SDimitry Andric 
522fe013be4SDimitry Andric // Calculate span for IV on this loop level for "<=" case.
523fe013be4SDimitry Andric // Note: it's for <= on this loop nest level, so lower bound should be smallest
524fe013be4SDimitry Andric // value, upper bound should be the biggest value. If the loop won't execute,
525fe013be4SDimitry Andric // 'smallest' may be bigger than 'biggest', but we'd better not switch them
526fe013be4SDimitry Andric // around.
527fe013be4SDimitry Andric template <typename T>
kmp_calc_span_lessoreq_XX(bounds_info_internalXX_template<T> * bounds,bounds_info_internal_t * bounds_nest)528fe013be4SDimitry Andric void kmp_calc_span_lessoreq_XX(
529fe013be4SDimitry Andric     /* in/out*/ bounds_info_internalXX_template<T> *bounds,
530fe013be4SDimitry Andric     /* in/out*/ bounds_info_internal_t *bounds_nest) {
531fe013be4SDimitry Andric 
532fe013be4SDimitry Andric   typedef typename traits_t<T>::unsigned_t UT;
533fe013be4SDimitry Andric   // typedef typename traits_t<T>::signed_t ST;
534fe013be4SDimitry Andric 
535fe013be4SDimitry Andric   // typedef typename big_span_t span_t;
536fe013be4SDimitry Andric   typedef T span_t;
537fe013be4SDimitry Andric 
538fe013be4SDimitry Andric   auto &bbounds = bounds->b;
539fe013be4SDimitry Andric 
540fe013be4SDimitry Andric   if ((bbounds.lb1 != 0) || (bbounds.ub1 != 0)) {
541fe013be4SDimitry Andric     // This dimention depends on one of previous ones; can't be the outermost
542fe013be4SDimitry Andric     // one.
543fe013be4SDimitry Andric     bounds_info_internalXX_template<T> *previous =
544fe013be4SDimitry Andric         reinterpret_cast<bounds_info_internalXX_template<T> *>(
545fe013be4SDimitry Andric             &(bounds_nest[bbounds.outer_iv]));
546fe013be4SDimitry Andric 
547fe013be4SDimitry Andric     // OMPTODO: assert that T is compatible with loop variable type on
548fe013be4SDimitry Andric     // 'previous' loop
549fe013be4SDimitry Andric 
550fe013be4SDimitry Andric     {
551fe013be4SDimitry Andric       span_t bound_candidate1 =
552fe013be4SDimitry Andric           bbounds.lb0 + bbounds.lb1 * previous->span_smallest;
553fe013be4SDimitry Andric       span_t bound_candidate2 =
554fe013be4SDimitry Andric           bbounds.lb0 + bbounds.lb1 * previous->span_biggest;
555fe013be4SDimitry Andric       if (bound_candidate1 < bound_candidate2) {
556fe013be4SDimitry Andric         bounds->span_smallest = bound_candidate1;
557fe013be4SDimitry Andric       } else {
558fe013be4SDimitry Andric         bounds->span_smallest = bound_candidate2;
559fe013be4SDimitry Andric       }
560fe013be4SDimitry Andric     }
561fe013be4SDimitry Andric 
562fe013be4SDimitry Andric     {
563fe013be4SDimitry Andric       // We can't adjust the upper bound with respect to step, because
564fe013be4SDimitry Andric       // lower bound might be off after adjustments
565fe013be4SDimitry Andric 
566fe013be4SDimitry Andric       span_t bound_candidate1 =
567fe013be4SDimitry Andric           bbounds.ub0 + bbounds.ub1 * previous->span_smallest;
568fe013be4SDimitry Andric       span_t bound_candidate2 =
569fe013be4SDimitry Andric           bbounds.ub0 + bbounds.ub1 * previous->span_biggest;
570fe013be4SDimitry Andric       if (bound_candidate1 < bound_candidate2) {
571fe013be4SDimitry Andric         bounds->span_biggest = bound_candidate2;
572fe013be4SDimitry Andric       } else {
573fe013be4SDimitry Andric         bounds->span_biggest = bound_candidate1;
574fe013be4SDimitry Andric       }
575fe013be4SDimitry Andric     }
576fe013be4SDimitry Andric   } else {
577fe013be4SDimitry Andric     // Rectangular:
578fe013be4SDimitry Andric     bounds->span_smallest = bbounds.lb0;
579fe013be4SDimitry Andric     bounds->span_biggest = bbounds.ub0;
580fe013be4SDimitry Andric   }
581fe013be4SDimitry Andric   if (!bounds->loop_bounds_adjusted) {
582fe013be4SDimitry Andric     // Here it's safe to reduce the space to the multiply of step.
583fe013be4SDimitry Andric     // OMPTODO: check if the formular is correct.
584fe013be4SDimitry Andric     // Also check if it would be safe to do this if we didn't adjust left side.
585fe013be4SDimitry Andric     bounds->span_biggest -=
586fe013be4SDimitry Andric         (static_cast<UT>(bbounds.ub0 - bbounds.lb0)) % bbounds.step; // abs?
587fe013be4SDimitry Andric   }
588fe013be4SDimitry Andric }
589fe013be4SDimitry Andric 
590fe013be4SDimitry Andric // Calculate span for IV on this loop level for ">=" case.
591fe013be4SDimitry Andric template <typename T>
kmp_calc_span_greateroreq_XX(bounds_info_internalXX_template<T> * bounds,bounds_info_internal_t * bounds_nest)592fe013be4SDimitry Andric void kmp_calc_span_greateroreq_XX(
593fe013be4SDimitry Andric     /* in/out*/ bounds_info_internalXX_template<T> *bounds,
594fe013be4SDimitry Andric     /* in/out*/ bounds_info_internal_t *bounds_nest) {
595fe013be4SDimitry Andric 
596fe013be4SDimitry Andric   typedef typename traits_t<T>::unsigned_t UT;
597fe013be4SDimitry Andric   // typedef typename traits_t<T>::signed_t ST;
598fe013be4SDimitry Andric 
599fe013be4SDimitry Andric   // typedef typename big_span_t span_t;
600fe013be4SDimitry Andric   typedef T span_t;
601fe013be4SDimitry Andric 
602fe013be4SDimitry Andric   auto &bbounds = bounds->b;
603fe013be4SDimitry Andric 
604fe013be4SDimitry Andric   if ((bbounds.lb1 != 0) || (bbounds.ub1 != 0)) {
605fe013be4SDimitry Andric     // This dimention depends on one of previous ones; can't be the outermost
606fe013be4SDimitry Andric     // one.
607fe013be4SDimitry Andric     bounds_info_internalXX_template<T> *previous =
608fe013be4SDimitry Andric         reinterpret_cast<bounds_info_internalXX_template<T> *>(
609fe013be4SDimitry Andric             &(bounds_nest[bbounds.outer_iv]));
610fe013be4SDimitry Andric 
611fe013be4SDimitry Andric     // OMPTODO: assert that T is compatible with loop variable type on
612fe013be4SDimitry Andric     // 'previous' loop
613fe013be4SDimitry Andric 
614fe013be4SDimitry Andric     {
615fe013be4SDimitry Andric       span_t bound_candidate1 =
616fe013be4SDimitry Andric           bbounds.lb0 + bbounds.lb1 * previous->span_smallest;
617fe013be4SDimitry Andric       span_t bound_candidate2 =
618fe013be4SDimitry Andric           bbounds.lb0 + bbounds.lb1 * previous->span_biggest;
619fe013be4SDimitry Andric       if (bound_candidate1 >= bound_candidate2) {
620fe013be4SDimitry Andric         bounds->span_smallest = bound_candidate1;
621fe013be4SDimitry Andric       } else {
622fe013be4SDimitry Andric         bounds->span_smallest = bound_candidate2;
623fe013be4SDimitry Andric       }
624fe013be4SDimitry Andric     }
625fe013be4SDimitry Andric 
626fe013be4SDimitry Andric     {
627fe013be4SDimitry Andric       // We can't adjust the upper bound with respect to step, because
628fe013be4SDimitry Andric       // lower bound might be off after adjustments
629fe013be4SDimitry Andric 
630fe013be4SDimitry Andric       span_t bound_candidate1 =
631fe013be4SDimitry Andric           bbounds.ub0 + bbounds.ub1 * previous->span_smallest;
632fe013be4SDimitry Andric       span_t bound_candidate2 =
633fe013be4SDimitry Andric           bbounds.ub0 + bbounds.ub1 * previous->span_biggest;
634fe013be4SDimitry Andric       if (bound_candidate1 >= bound_candidate2) {
635fe013be4SDimitry Andric         bounds->span_biggest = bound_candidate2;
636fe013be4SDimitry Andric       } else {
637fe013be4SDimitry Andric         bounds->span_biggest = bound_candidate1;
638fe013be4SDimitry Andric       }
639fe013be4SDimitry Andric     }
640fe013be4SDimitry Andric 
641fe013be4SDimitry Andric   } else {
642fe013be4SDimitry Andric     // Rectangular:
643fe013be4SDimitry Andric     bounds->span_biggest = bbounds.lb0;
644fe013be4SDimitry Andric     bounds->span_smallest = bbounds.ub0;
645fe013be4SDimitry Andric   }
646fe013be4SDimitry Andric   if (!bounds->loop_bounds_adjusted) {
647fe013be4SDimitry Andric     // Here it's safe to reduce the space to the multiply of step.
648fe013be4SDimitry Andric     // OMPTODO: check if the formular is correct.
649fe013be4SDimitry Andric     // Also check if it would be safe to do this if we didn't adjust left side.
650fe013be4SDimitry Andric     bounds->span_biggest -=
651fe013be4SDimitry Andric         (static_cast<UT>(bbounds.ub0 - bbounds.lb0)) % bbounds.step; // abs?
652fe013be4SDimitry Andric   }
653fe013be4SDimitry Andric }
654fe013be4SDimitry Andric 
655fe013be4SDimitry Andric // Calculate maximum possible span for IV on this loop level.
656fe013be4SDimitry Andric template <typename T>
kmp_calc_span_XX(bounds_info_internalXX_template<T> * bounds,bounds_info_internal_t * bounds_nest)657fe013be4SDimitry Andric void kmp_calc_span_XX(
658fe013be4SDimitry Andric     /* in/out*/ bounds_info_internalXX_template<T> *bounds,
659fe013be4SDimitry Andric     /* in/out*/ bounds_info_internal_t *bounds_nest) {
660fe013be4SDimitry Andric 
661fe013be4SDimitry Andric   if (bounds->b.comparison == comparison_t::comp_less_or_eq) {
662fe013be4SDimitry Andric     kmp_calc_span_lessoreq_XX(/* in/out*/ bounds, /* in/out*/ bounds_nest);
663fe013be4SDimitry Andric   } else {
664fe013be4SDimitry Andric     KMP_ASSERT(bounds->b.comparison == comparison_t::comp_greater_or_eq);
665fe013be4SDimitry Andric     kmp_calc_span_greateroreq_XX(/* in/out*/ bounds, /* in/out*/ bounds_nest);
666fe013be4SDimitry Andric   }
667fe013be4SDimitry Andric }
668fe013be4SDimitry Andric 
669fe013be4SDimitry Andric //----------All initial processing of the loop nest---------------------------
670fe013be4SDimitry Andric 
671fe013be4SDimitry Andric // Calculate new bounds for this loop level.
672fe013be4SDimitry Andric // To be able to work with the nest we need to get it to a parallelepiped shape.
673fe013be4SDimitry Andric // We need to stay in the original range of values, so that there will be no
674fe013be4SDimitry Andric // overflow, for that we'll adjust both upper and lower bounds as needed.
675fe013be4SDimitry Andric template <typename T>
kmp_calc_new_bounds_XX(bounds_info_internalXX_template<T> * bounds,bounds_info_internal_t * bounds_nest)676fe013be4SDimitry Andric void kmp_calc_new_bounds_XX(
677fe013be4SDimitry Andric     /* in/out*/ bounds_info_internalXX_template<T> *bounds,
678fe013be4SDimitry Andric     /* in/out*/ bounds_info_internal_t *bounds_nest) {
679fe013be4SDimitry Andric 
680fe013be4SDimitry Andric   auto &bbounds = bounds->b;
681fe013be4SDimitry Andric 
682fe013be4SDimitry Andric   if (bbounds.lb1 == bbounds.ub1) {
683fe013be4SDimitry Andric     // Already parallel, no need to adjust:
684fe013be4SDimitry Andric     bounds->loop_bounds_adjusted = false;
685fe013be4SDimitry Andric   } else {
686fe013be4SDimitry Andric     bounds->loop_bounds_adjusted = true;
687fe013be4SDimitry Andric 
688fe013be4SDimitry Andric     T old_lb1 = bbounds.lb1;
689fe013be4SDimitry Andric     T old_ub1 = bbounds.ub1;
690fe013be4SDimitry Andric 
691fe013be4SDimitry Andric     if (__kmp_sign(old_lb1) != __kmp_sign(old_ub1)) {
692fe013be4SDimitry Andric       // With this shape we can adjust to a rectangle:
693fe013be4SDimitry Andric       bbounds.lb1 = 0;
694fe013be4SDimitry Andric       bbounds.ub1 = 0;
695fe013be4SDimitry Andric     } else {
696fe013be4SDimitry Andric       // get upper and lower bounds to be parallel
697fe013be4SDimitry Andric       // with values in the old range.
698fe013be4SDimitry Andric       // Note: abs didn't work here.
699fe013be4SDimitry Andric       if (((old_lb1 < 0) && (old_lb1 < old_ub1)) ||
700fe013be4SDimitry Andric           ((old_lb1 > 0) && (old_lb1 > old_ub1))) {
701fe013be4SDimitry Andric         bbounds.lb1 = old_ub1;
702fe013be4SDimitry Andric       } else {
703fe013be4SDimitry Andric         bbounds.ub1 = old_lb1;
704fe013be4SDimitry Andric       }
705fe013be4SDimitry Andric     }
706fe013be4SDimitry Andric 
707fe013be4SDimitry Andric     // Now need to adjust lb0, ub0, otherwise in some cases space will shrink.
708fe013be4SDimitry Andric     // The idea here that for this IV we are now getting the same span
709fe013be4SDimitry Andric     // irrespective of the previous IV value.
710fe013be4SDimitry Andric     bounds_info_internalXX_template<T> *previous =
711fe013be4SDimitry Andric         reinterpret_cast<bounds_info_internalXX_template<T> *>(
712fe013be4SDimitry Andric             &bounds_nest[bbounds.outer_iv]);
713fe013be4SDimitry Andric 
714fe013be4SDimitry Andric     if (bbounds.comparison == comparison_t::comp_less_or_eq) {
715fe013be4SDimitry Andric       if (old_lb1 < bbounds.lb1) {
716fe013be4SDimitry Andric         KMP_ASSERT(old_lb1 < 0);
717fe013be4SDimitry Andric         // The length is good on outer_iv biggest number,
718fe013be4SDimitry Andric         // can use it to find where to move the lower bound:
719fe013be4SDimitry Andric 
720fe013be4SDimitry Andric         T sub = (bbounds.lb1 - old_lb1) * previous->span_biggest;
721fe013be4SDimitry Andric         bbounds.lb0 -= sub; // OMPTODO: what if it'll go out of unsigned space?
722fe013be4SDimitry Andric                             // e.g. it was 0?? (same below)
723fe013be4SDimitry Andric       } else if (old_lb1 > bbounds.lb1) {
724fe013be4SDimitry Andric         // still need to move lower bound:
725fe013be4SDimitry Andric         T add = (old_lb1 - bbounds.lb1) * previous->span_smallest;
726fe013be4SDimitry Andric         bbounds.lb0 += add;
727fe013be4SDimitry Andric       }
728fe013be4SDimitry Andric 
729fe013be4SDimitry Andric       if (old_ub1 > bbounds.ub1) {
730fe013be4SDimitry Andric         KMP_ASSERT(old_ub1 > 0);
731fe013be4SDimitry Andric         // The length is good on outer_iv biggest number,
732fe013be4SDimitry Andric         // can use it to find where to move upper bound:
733fe013be4SDimitry Andric 
734fe013be4SDimitry Andric         T add = (old_ub1 - bbounds.ub1) * previous->span_biggest;
735fe013be4SDimitry Andric         bbounds.ub0 += add;
736fe013be4SDimitry Andric       } else if (old_ub1 < bbounds.ub1) {
737fe013be4SDimitry Andric         // still need to move upper bound:
738fe013be4SDimitry Andric         T sub = (bbounds.ub1 - old_ub1) * previous->span_smallest;
739fe013be4SDimitry Andric         bbounds.ub0 -= sub;
740fe013be4SDimitry Andric       }
741fe013be4SDimitry Andric     } else {
742fe013be4SDimitry Andric       KMP_ASSERT(bbounds.comparison == comparison_t::comp_greater_or_eq);
743fe013be4SDimitry Andric       if (old_lb1 < bbounds.lb1) {
744fe013be4SDimitry Andric         KMP_ASSERT(old_lb1 < 0);
745fe013be4SDimitry Andric         T sub = (bbounds.lb1 - old_lb1) * previous->span_smallest;
746fe013be4SDimitry Andric         bbounds.lb0 -= sub;
747fe013be4SDimitry Andric       } else if (old_lb1 > bbounds.lb1) {
748fe013be4SDimitry Andric         T add = (old_lb1 - bbounds.lb1) * previous->span_biggest;
749fe013be4SDimitry Andric         bbounds.lb0 += add;
750fe013be4SDimitry Andric       }
751fe013be4SDimitry Andric 
752fe013be4SDimitry Andric       if (old_ub1 > bbounds.ub1) {
753fe013be4SDimitry Andric         KMP_ASSERT(old_ub1 > 0);
754fe013be4SDimitry Andric         T add = (old_ub1 - bbounds.ub1) * previous->span_smallest;
755fe013be4SDimitry Andric         bbounds.ub0 += add;
756fe013be4SDimitry Andric       } else if (old_ub1 < bbounds.ub1) {
757fe013be4SDimitry Andric         T sub = (bbounds.ub1 - old_ub1) * previous->span_biggest;
758fe013be4SDimitry Andric         bbounds.ub0 -= sub;
759fe013be4SDimitry Andric       }
760fe013be4SDimitry Andric     }
761fe013be4SDimitry Andric   }
762fe013be4SDimitry Andric }
763fe013be4SDimitry Andric 
764fe013be4SDimitry Andric // Do all processing for one canonicalized loop in the nest
765fe013be4SDimitry Andric // (assuming that outer loops already were processed):
766fe013be4SDimitry Andric template <typename T>
kmp_process_one_loop_XX(bounds_info_internalXX_template<T> * bounds,bounds_info_internal_t * bounds_nest)767fe013be4SDimitry Andric kmp_loop_nest_iv_t kmp_process_one_loop_XX(
768fe013be4SDimitry Andric     /* in/out*/ bounds_info_internalXX_template<T> *bounds,
769fe013be4SDimitry Andric     /*in/out*/ bounds_info_internal_t *bounds_nest) {
770fe013be4SDimitry Andric 
771fe013be4SDimitry Andric   kmp_calc_new_bounds_XX(/* in/out*/ bounds, /* in/out*/ bounds_nest);
772fe013be4SDimitry Andric   kmp_calc_span_XX(/* in/out*/ bounds, /* in/out*/ bounds_nest);
773fe013be4SDimitry Andric   return kmp_calculate_trip_count_XX(/*in/out*/ &(bounds->b));
774fe013be4SDimitry Andric }
775fe013be4SDimitry Andric 
776fe013be4SDimitry Andric // Non-rectangular loop nest, canonicalized to use <= or >=.
777fe013be4SDimitry Andric // Process loop nest to have a parallelepiped shape,
778fe013be4SDimitry Andric // calculate biggest spans for IV's on all levels and calculate overall trip
779fe013be4SDimitry Andric // count. "bounds_nest" has to be allocated per thread.
780fe013be4SDimitry Andric // Returns overall trip count (for adjusted space).
kmp_process_loop_nest(bounds_info_internal_t * bounds_nest,kmp_index_t n)781fe013be4SDimitry Andric kmp_loop_nest_iv_t kmp_process_loop_nest(
782fe013be4SDimitry Andric     /*in/out*/ bounds_info_internal_t *bounds_nest, kmp_index_t n) {
783fe013be4SDimitry Andric 
784fe013be4SDimitry Andric   kmp_loop_nest_iv_t total = 1;
785fe013be4SDimitry Andric 
786fe013be4SDimitry Andric   for (kmp_index_t ind = 0; ind < n; ++ind) {
787fe013be4SDimitry Andric     auto bounds = &(bounds_nest[ind]);
788fe013be4SDimitry Andric     kmp_loop_nest_iv_t trip_count = 0;
789fe013be4SDimitry Andric 
790fe013be4SDimitry Andric     switch (bounds->b.loop_type) {
791fe013be4SDimitry Andric     case loop_type_t::loop_type_int32:
792fe013be4SDimitry Andric       trip_count = kmp_process_one_loop_XX<kmp_int32>(
793fe013be4SDimitry Andric           /*in/out*/ (bounds_info_internalXX_template<kmp_int32> *)(bounds),
794fe013be4SDimitry Andric           /*in/out*/ bounds_nest);
795fe013be4SDimitry Andric       break;
796fe013be4SDimitry Andric     case loop_type_t::loop_type_uint32:
797fe013be4SDimitry Andric       trip_count = kmp_process_one_loop_XX<kmp_uint32>(
798fe013be4SDimitry Andric           /*in/out*/ (bounds_info_internalXX_template<kmp_uint32> *)(bounds),
799fe013be4SDimitry Andric           /*in/out*/ bounds_nest);
800fe013be4SDimitry Andric       break;
801fe013be4SDimitry Andric     case loop_type_t::loop_type_int64:
802fe013be4SDimitry Andric       trip_count = kmp_process_one_loop_XX<kmp_int64>(
803fe013be4SDimitry Andric           /*in/out*/ (bounds_info_internalXX_template<kmp_int64> *)(bounds),
804fe013be4SDimitry Andric           /*in/out*/ bounds_nest);
805fe013be4SDimitry Andric       break;
806fe013be4SDimitry Andric     case loop_type_t::loop_type_uint64:
807fe013be4SDimitry Andric       trip_count = kmp_process_one_loop_XX<kmp_uint64>(
808fe013be4SDimitry Andric           /*in/out*/ (bounds_info_internalXX_template<kmp_uint64> *)(bounds),
809fe013be4SDimitry Andric           /*in/out*/ bounds_nest);
810fe013be4SDimitry Andric       break;
811fe013be4SDimitry Andric     default:
812fe013be4SDimitry Andric       KMP_ASSERT(false);
813fe013be4SDimitry Andric     }
814fe013be4SDimitry Andric     total *= trip_count;
815fe013be4SDimitry Andric   }
816fe013be4SDimitry Andric 
817fe013be4SDimitry Andric   return total;
818fe013be4SDimitry Andric }
819fe013be4SDimitry Andric 
820fe013be4SDimitry Andric //----------Calculate iterations (in the original or updated space)-----------
821fe013be4SDimitry Andric 
822fe013be4SDimitry Andric // Calculate number of iterations in original or updated space resulting in
823fe013be4SDimitry Andric // original_ivs[ind] (only on this level, non-negative)
824fe013be4SDimitry Andric // (not counting initial iteration)
825fe013be4SDimitry Andric template <typename T>
826fe013be4SDimitry Andric kmp_loop_nest_iv_t
kmp_calc_number_of_iterations_XX(const bounds_infoXX_template<T> * bounds,const kmp_point_t original_ivs,kmp_index_t ind)827fe013be4SDimitry Andric kmp_calc_number_of_iterations_XX(const bounds_infoXX_template<T> *bounds,
828fe013be4SDimitry Andric                                  const kmp_point_t original_ivs,
829fe013be4SDimitry Andric                                  kmp_index_t ind) {
830fe013be4SDimitry Andric 
831fe013be4SDimitry Andric   kmp_loop_nest_iv_t iterations = 0;
832fe013be4SDimitry Andric 
833fe013be4SDimitry Andric   if (bounds->comparison == comparison_t::comp_less_or_eq) {
834fe013be4SDimitry Andric     iterations =
835fe013be4SDimitry Andric         (static_cast<T>(original_ivs[ind]) - bounds->lb0 -
836fe013be4SDimitry Andric          bounds->lb1 * static_cast<T>(original_ivs[bounds->outer_iv])) /
837fe013be4SDimitry Andric         __kmp_abs(bounds->step);
838fe013be4SDimitry Andric   } else {
839fe013be4SDimitry Andric     KMP_DEBUG_ASSERT(bounds->comparison == comparison_t::comp_greater_or_eq);
840fe013be4SDimitry Andric     iterations = (bounds->lb0 +
841fe013be4SDimitry Andric                   bounds->lb1 * static_cast<T>(original_ivs[bounds->outer_iv]) -
842fe013be4SDimitry Andric                   static_cast<T>(original_ivs[ind])) /
843fe013be4SDimitry Andric                  __kmp_abs(bounds->step);
844fe013be4SDimitry Andric   }
845fe013be4SDimitry Andric 
846fe013be4SDimitry Andric   return iterations;
847fe013be4SDimitry Andric }
848fe013be4SDimitry Andric 
849fe013be4SDimitry Andric // Calculate number of iterations in the original or updated space resulting in
850fe013be4SDimitry Andric // original_ivs[ind] (only on this level, non-negative)
kmp_calc_number_of_iterations(const bounds_info_t * bounds,const kmp_point_t original_ivs,kmp_index_t ind)851fe013be4SDimitry Andric kmp_loop_nest_iv_t kmp_calc_number_of_iterations(const bounds_info_t *bounds,
852fe013be4SDimitry Andric                                                  const kmp_point_t original_ivs,
853fe013be4SDimitry Andric                                                  kmp_index_t ind) {
854fe013be4SDimitry Andric 
855fe013be4SDimitry Andric   switch (bounds->loop_type) {
856fe013be4SDimitry Andric   case loop_type_t::loop_type_int32:
857fe013be4SDimitry Andric     return kmp_calc_number_of_iterations_XX<kmp_int32>(
858fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_int32> *)(bounds), original_ivs, ind);
859fe013be4SDimitry Andric     break;
860fe013be4SDimitry Andric   case loop_type_t::loop_type_uint32:
861fe013be4SDimitry Andric     return kmp_calc_number_of_iterations_XX<kmp_uint32>(
862fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_uint32> *)(bounds), original_ivs, ind);
863fe013be4SDimitry Andric     break;
864fe013be4SDimitry Andric   case loop_type_t::loop_type_int64:
865fe013be4SDimitry Andric     return kmp_calc_number_of_iterations_XX<kmp_int64>(
866fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_int64> *)(bounds), original_ivs, ind);
867fe013be4SDimitry Andric     break;
868fe013be4SDimitry Andric   case loop_type_t::loop_type_uint64:
869fe013be4SDimitry Andric     return kmp_calc_number_of_iterations_XX<kmp_uint64>(
870fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_uint64> *)(bounds), original_ivs, ind);
871fe013be4SDimitry Andric     break;
872fe013be4SDimitry Andric   default:
873fe013be4SDimitry Andric     KMP_ASSERT(false);
874fe013be4SDimitry Andric     return 0;
875fe013be4SDimitry Andric   }
876fe013be4SDimitry Andric }
877fe013be4SDimitry Andric 
878fe013be4SDimitry Andric //----------Calculate new iv corresponding to original ivs--------------------
879fe013be4SDimitry Andric 
880fe013be4SDimitry Andric // We got a point in the original loop nest.
881fe013be4SDimitry Andric // Take updated bounds and calculate what new_iv will correspond to this point.
882fe013be4SDimitry Andric // When we are getting original IVs from new_iv, we have to adjust to fit into
883fe013be4SDimitry Andric // original loops bounds. Getting new_iv for the adjusted original IVs will help
884fe013be4SDimitry Andric // with making more chunks non-empty.
885fe013be4SDimitry Andric kmp_loop_nest_iv_t
kmp_calc_new_iv_from_original_ivs(const bounds_info_internal_t * bounds_nest,const kmp_point_t original_ivs,kmp_index_t n)886fe013be4SDimitry Andric kmp_calc_new_iv_from_original_ivs(const bounds_info_internal_t *bounds_nest,
887fe013be4SDimitry Andric                                   const kmp_point_t original_ivs,
888fe013be4SDimitry Andric                                   kmp_index_t n) {
889fe013be4SDimitry Andric 
890fe013be4SDimitry Andric   kmp_loop_nest_iv_t new_iv = 0;
891fe013be4SDimitry Andric 
892fe013be4SDimitry Andric   for (kmp_index_t ind = 0; ind < n; ++ind) {
893fe013be4SDimitry Andric     auto bounds = &(bounds_nest[ind].b);
894fe013be4SDimitry Andric 
895fe013be4SDimitry Andric     new_iv = new_iv * bounds->trip_count +
896fe013be4SDimitry Andric              kmp_calc_number_of_iterations(bounds, original_ivs, ind);
897fe013be4SDimitry Andric   }
898fe013be4SDimitry Andric 
899fe013be4SDimitry Andric   return new_iv;
900fe013be4SDimitry Andric }
901fe013be4SDimitry Andric 
902fe013be4SDimitry Andric //----------Calculate original ivs for provided iterations--------------------
903fe013be4SDimitry Andric 
904fe013be4SDimitry Andric // Calculate original IVs for provided iterations, assuming iterations are
905fe013be4SDimitry Andric // calculated in the original space.
906fe013be4SDimitry Andric // Loop nest is in canonical form (with <= / >=).
kmp_calc_original_ivs_from_iterations(const bounds_info_t * original_bounds_nest,kmp_index_t n,kmp_point_t original_ivs,kmp_iterations_t iterations,kmp_index_t ind)907fe013be4SDimitry Andric bool kmp_calc_original_ivs_from_iterations(
908fe013be4SDimitry Andric     const bounds_info_t *original_bounds_nest, kmp_index_t n,
909fe013be4SDimitry Andric     /*in/out*/ kmp_point_t original_ivs,
910fe013be4SDimitry Andric     /*in/out*/ kmp_iterations_t iterations, kmp_index_t ind) {
911fe013be4SDimitry Andric 
912fe013be4SDimitry Andric   kmp_index_t lengthened_ind = n;
913fe013be4SDimitry Andric 
914fe013be4SDimitry Andric   for (; ind < n;) {
915fe013be4SDimitry Andric     auto bounds = &(original_bounds_nest[ind]);
916fe013be4SDimitry Andric     bool good = kmp_calc_one_iv(bounds, /*in/out*/ original_ivs, iterations,
917fe013be4SDimitry Andric                                 ind, (lengthened_ind < ind), true);
918fe013be4SDimitry Andric 
919fe013be4SDimitry Andric     if (!good) {
920fe013be4SDimitry Andric       // The calculated iv value is too big (or too small for >=):
921fe013be4SDimitry Andric       if (ind == 0) {
922fe013be4SDimitry Andric         // Space is empty:
923fe013be4SDimitry Andric         return false;
924fe013be4SDimitry Andric       } else {
925fe013be4SDimitry Andric         // Go to next iteration on the outer loop:
926fe013be4SDimitry Andric         --ind;
927fe013be4SDimitry Andric         ++iterations[ind];
928fe013be4SDimitry Andric         lengthened_ind = ind;
929fe013be4SDimitry Andric         for (kmp_index_t i = ind + 1; i < n; ++i) {
930fe013be4SDimitry Andric           iterations[i] = 0;
931fe013be4SDimitry Andric         }
932fe013be4SDimitry Andric         continue;
933fe013be4SDimitry Andric       }
934fe013be4SDimitry Andric     }
935fe013be4SDimitry Andric     ++ind;
936fe013be4SDimitry Andric   }
937fe013be4SDimitry Andric 
938fe013be4SDimitry Andric   return true;
939fe013be4SDimitry Andric }
940fe013be4SDimitry Andric 
941fe013be4SDimitry Andric //----------Calculate original ivs for the beginning of the loop nest---------
942fe013be4SDimitry Andric 
943fe013be4SDimitry Andric // Calculate IVs for the beginning of the loop nest.
944fe013be4SDimitry Andric // Note: lower bounds of all loops may not work -
945fe013be4SDimitry Andric // if on some of the iterations of the outer loops inner loops are empty.
946fe013be4SDimitry Andric // Loop nest is in canonical form (with <= / >=).
kmp_calc_original_ivs_for_start(const bounds_info_t * original_bounds_nest,kmp_index_t n,kmp_point_t original_ivs)947fe013be4SDimitry Andric bool kmp_calc_original_ivs_for_start(const bounds_info_t *original_bounds_nest,
948fe013be4SDimitry Andric                                      kmp_index_t n,
949fe013be4SDimitry Andric                                      /*out*/ kmp_point_t original_ivs) {
950fe013be4SDimitry Andric 
951fe013be4SDimitry Andric   // Iterations in the original space, multiplied by step:
952*c9157d92SDimitry Andric   CollapseAllocator<kmp_loop_nest_iv_t> iterations(n);
953fe013be4SDimitry Andric   for (kmp_index_t ind = n; ind > 0;) {
954fe013be4SDimitry Andric     --ind;
955fe013be4SDimitry Andric     iterations[ind] = 0;
956fe013be4SDimitry Andric   }
957fe013be4SDimitry Andric 
958fe013be4SDimitry Andric   // Now calculate the point:
959fe013be4SDimitry Andric   bool b = kmp_calc_original_ivs_from_iterations(original_bounds_nest, n,
960fe013be4SDimitry Andric                                                  /*in/out*/ original_ivs,
961fe013be4SDimitry Andric                                                  /*in/out*/ iterations, 0);
962fe013be4SDimitry Andric   return b;
963fe013be4SDimitry Andric }
964fe013be4SDimitry Andric 
965fe013be4SDimitry Andric //----------Calculate next point in the original loop space-------------------
966fe013be4SDimitry Andric 
967fe013be4SDimitry Andric // From current set of original IVs calculate next point.
968fe013be4SDimitry Andric // Return false if there is no next point in the loop bounds.
kmp_calc_next_original_ivs(const bounds_info_t * original_bounds_nest,kmp_index_t n,const kmp_point_t original_ivs,kmp_point_t next_original_ivs)969fe013be4SDimitry Andric bool kmp_calc_next_original_ivs(const bounds_info_t *original_bounds_nest,
970fe013be4SDimitry Andric                                 kmp_index_t n, const kmp_point_t original_ivs,
971fe013be4SDimitry Andric                                 /*out*/ kmp_point_t next_original_ivs) {
972fe013be4SDimitry Andric   // Iterations in the original space, multiplied by step (so can be negative):
973*c9157d92SDimitry Andric   CollapseAllocator<kmp_loop_nest_iv_t> iterations(n);
974fe013be4SDimitry Andric   // First, calc corresponding iteration in every original loop:
975fe013be4SDimitry Andric   for (kmp_index_t ind = 0; ind < n; ++ind) {
976fe013be4SDimitry Andric     auto bounds = &(original_bounds_nest[ind]);
977fe013be4SDimitry Andric     iterations[ind] = kmp_calc_number_of_iterations(bounds, original_ivs, ind);
978fe013be4SDimitry Andric   }
979fe013be4SDimitry Andric 
980fe013be4SDimitry Andric   for (kmp_index_t ind = 0; ind < n; ++ind) {
981fe013be4SDimitry Andric     next_original_ivs[ind] = original_ivs[ind];
982fe013be4SDimitry Andric   }
983fe013be4SDimitry Andric 
984fe013be4SDimitry Andric   // Next add one step to the iterations on the inner-most level, and see if we
985fe013be4SDimitry Andric   // need to move up the nest:
986fe013be4SDimitry Andric   kmp_index_t ind = n - 1;
987fe013be4SDimitry Andric   ++iterations[ind];
988fe013be4SDimitry Andric 
989fe013be4SDimitry Andric   bool b = kmp_calc_original_ivs_from_iterations(
990fe013be4SDimitry Andric       original_bounds_nest, n, /*in/out*/ next_original_ivs, iterations, ind);
991fe013be4SDimitry Andric 
992fe013be4SDimitry Andric   return b;
993fe013be4SDimitry Andric }
994fe013be4SDimitry Andric 
995fe013be4SDimitry Andric //----------Calculate chunk end in the original loop space--------------------
996fe013be4SDimitry Andric 
997fe013be4SDimitry Andric // For one level calculate old induction variable corresponding to overall
998fe013be4SDimitry Andric // new_iv for the chunk end.
999fe013be4SDimitry Andric // Return true if it fits into upper bound on this level
1000fe013be4SDimitry Andric // (if not, we need to re-calculate)
1001fe013be4SDimitry Andric template <typename T>
kmp_calc_one_iv_for_chunk_end_XX(const bounds_infoXX_template<T> * bounds,const bounds_infoXX_template<T> * updated_bounds,kmp_point_t original_ivs,const kmp_iterations_t iterations,kmp_index_t ind,bool start_with_lower_bound,bool compare_with_start,const kmp_point_t original_ivs_start)1002fe013be4SDimitry Andric bool kmp_calc_one_iv_for_chunk_end_XX(
1003fe013be4SDimitry Andric     const bounds_infoXX_template<T> *bounds,
1004fe013be4SDimitry Andric     const bounds_infoXX_template<T> *updated_bounds,
1005fe013be4SDimitry Andric     /*in/out*/ kmp_point_t original_ivs, const kmp_iterations_t iterations,
1006fe013be4SDimitry Andric     kmp_index_t ind, bool start_with_lower_bound, bool compare_with_start,
1007fe013be4SDimitry Andric     const kmp_point_t original_ivs_start) {
1008fe013be4SDimitry Andric 
1009fe013be4SDimitry Andric   // typedef  std::conditional<std::is_signed<T>::value, kmp_int64, kmp_uint64>
1010fe013be4SDimitry Andric   // big_span_t;
1011fe013be4SDimitry Andric 
1012fe013be4SDimitry Andric   // OMPTODO: is it good enough, or do we need ST or do we need big_span_t?
1013fe013be4SDimitry Andric   T temp = 0;
1014fe013be4SDimitry Andric 
1015fe013be4SDimitry Andric   T outer_iv = static_cast<T>(original_ivs[bounds->outer_iv]);
1016fe013be4SDimitry Andric 
1017fe013be4SDimitry Andric   if (start_with_lower_bound) {
1018fe013be4SDimitry Andric     // we moved to the next iteration on one of outer loops, may as well use
1019fe013be4SDimitry Andric     // the lower bound here:
1020fe013be4SDimitry Andric     temp = bounds->lb0 + bounds->lb1 * outer_iv;
1021fe013be4SDimitry Andric   } else {
1022fe013be4SDimitry Andric     // Start in expanded space, but:
1023fe013be4SDimitry Andric     // - we need to hit original space lower bound, so need to account for
1024fe013be4SDimitry Andric     // that
1025fe013be4SDimitry Andric     // - we have to go into original space, even if that means adding more
1026fe013be4SDimitry Andric     // iterations than was planned
1027fe013be4SDimitry Andric     // - we have to go past (or equal to) previous point (which is the chunk
1028fe013be4SDimitry Andric     // starting point)
1029fe013be4SDimitry Andric 
1030fe013be4SDimitry Andric     auto iteration = iterations[ind];
1031fe013be4SDimitry Andric 
1032fe013be4SDimitry Andric     auto step = bounds->step;
1033fe013be4SDimitry Andric 
1034fe013be4SDimitry Andric     // In case of >= it's negative:
1035fe013be4SDimitry Andric     auto accountForStep =
1036fe013be4SDimitry Andric         ((bounds->lb0 + bounds->lb1 * outer_iv) -
1037fe013be4SDimitry Andric          (updated_bounds->lb0 + updated_bounds->lb1 * outer_iv)) %
1038fe013be4SDimitry Andric         step;
1039fe013be4SDimitry Andric 
1040fe013be4SDimitry Andric     temp = updated_bounds->lb0 + updated_bounds->lb1 * outer_iv +
1041fe013be4SDimitry Andric            accountForStep + iteration * step;
1042fe013be4SDimitry Andric 
1043fe013be4SDimitry Andric     if (((bounds->comparison == comparison_t::comp_less_or_eq) &&
1044fe013be4SDimitry Andric          (temp < (bounds->lb0 + bounds->lb1 * outer_iv))) ||
1045fe013be4SDimitry Andric         ((bounds->comparison == comparison_t::comp_greater_or_eq) &&
1046fe013be4SDimitry Andric          (temp > (bounds->lb0 + bounds->lb1 * outer_iv)))) {
1047fe013be4SDimitry Andric       // Too small (or too big), didn't reach the original lower bound. Use
1048fe013be4SDimitry Andric       // heuristic:
1049fe013be4SDimitry Andric       temp = bounds->lb0 + bounds->lb1 * outer_iv + iteration / 2 * step;
1050fe013be4SDimitry Andric     }
1051fe013be4SDimitry Andric 
1052fe013be4SDimitry Andric     if (compare_with_start) {
1053fe013be4SDimitry Andric 
1054fe013be4SDimitry Andric       T start = static_cast<T>(original_ivs_start[ind]);
1055fe013be4SDimitry Andric 
1056fe013be4SDimitry Andric       temp = kmp_fix_iv(bounds->loop_iv_type, temp);
1057fe013be4SDimitry Andric 
1058fe013be4SDimitry Andric       // On all previous levels start of the chunk is same as the end, need to
1059fe013be4SDimitry Andric       // be really careful here:
1060fe013be4SDimitry Andric       if (((bounds->comparison == comparison_t::comp_less_or_eq) &&
1061fe013be4SDimitry Andric            (temp < start)) ||
1062fe013be4SDimitry Andric           ((bounds->comparison == comparison_t::comp_greater_or_eq) &&
1063fe013be4SDimitry Andric            (temp > start))) {
1064fe013be4SDimitry Andric         // End of the chunk can't be smaller (for >= bigger) than it's start.
1065fe013be4SDimitry Andric         // Use heuristic:
1066fe013be4SDimitry Andric         temp = start + iteration / 4 * step;
1067fe013be4SDimitry Andric       }
1068fe013be4SDimitry Andric     }
1069fe013be4SDimitry Andric   }
1070fe013be4SDimitry Andric 
1071fe013be4SDimitry Andric   original_ivs[ind] = temp = kmp_fix_iv(bounds->loop_iv_type, temp);
1072fe013be4SDimitry Andric 
1073fe013be4SDimitry Andric   if (((bounds->comparison == comparison_t::comp_less_or_eq) &&
1074fe013be4SDimitry Andric        (temp > (bounds->ub0 + bounds->ub1 * outer_iv))) ||
1075fe013be4SDimitry Andric       ((bounds->comparison == comparison_t::comp_greater_or_eq) &&
1076fe013be4SDimitry Andric        (temp < (bounds->ub0 + bounds->ub1 * outer_iv)))) {
1077fe013be4SDimitry Andric     // Too big (or too small for >=).
1078fe013be4SDimitry Andric     return false;
1079fe013be4SDimitry Andric   }
1080fe013be4SDimitry Andric 
1081fe013be4SDimitry Andric   return true;
1082fe013be4SDimitry Andric }
1083fe013be4SDimitry Andric 
1084fe013be4SDimitry Andric // For one level calculate old induction variable corresponding to overall
1085fe013be4SDimitry Andric // new_iv for the chunk end.
kmp_calc_one_iv_for_chunk_end(const bounds_info_t * bounds,const bounds_info_t * updated_bounds,kmp_point_t original_ivs,const kmp_iterations_t iterations,kmp_index_t ind,bool start_with_lower_bound,bool compare_with_start,const kmp_point_t original_ivs_start)1086fe013be4SDimitry Andric bool kmp_calc_one_iv_for_chunk_end(const bounds_info_t *bounds,
1087fe013be4SDimitry Andric                                    const bounds_info_t *updated_bounds,
1088fe013be4SDimitry Andric                                    /*in/out*/ kmp_point_t original_ivs,
1089fe013be4SDimitry Andric                                    const kmp_iterations_t iterations,
1090fe013be4SDimitry Andric                                    kmp_index_t ind, bool start_with_lower_bound,
1091fe013be4SDimitry Andric                                    bool compare_with_start,
1092fe013be4SDimitry Andric                                    const kmp_point_t original_ivs_start) {
1093fe013be4SDimitry Andric 
1094fe013be4SDimitry Andric   switch (bounds->loop_type) {
1095fe013be4SDimitry Andric   case loop_type_t::loop_type_int32:
1096fe013be4SDimitry Andric     return kmp_calc_one_iv_for_chunk_end_XX<kmp_int32>(
1097fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_int32> *)(bounds),
1098fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_int32> *)(updated_bounds),
1099fe013be4SDimitry Andric         /*in/out*/
1100fe013be4SDimitry Andric         original_ivs, iterations, ind, start_with_lower_bound,
1101fe013be4SDimitry Andric         compare_with_start, original_ivs_start);
1102fe013be4SDimitry Andric     break;
1103fe013be4SDimitry Andric   case loop_type_t::loop_type_uint32:
1104fe013be4SDimitry Andric     return kmp_calc_one_iv_for_chunk_end_XX<kmp_uint32>(
1105fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_uint32> *)(bounds),
1106fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_uint32> *)(updated_bounds),
1107fe013be4SDimitry Andric         /*in/out*/
1108fe013be4SDimitry Andric         original_ivs, iterations, ind, start_with_lower_bound,
1109fe013be4SDimitry Andric         compare_with_start, original_ivs_start);
1110fe013be4SDimitry Andric     break;
1111fe013be4SDimitry Andric   case loop_type_t::loop_type_int64:
1112fe013be4SDimitry Andric     return kmp_calc_one_iv_for_chunk_end_XX<kmp_int64>(
1113fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_int64> *)(bounds),
1114fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_int64> *)(updated_bounds),
1115fe013be4SDimitry Andric         /*in/out*/
1116fe013be4SDimitry Andric         original_ivs, iterations, ind, start_with_lower_bound,
1117fe013be4SDimitry Andric         compare_with_start, original_ivs_start);
1118fe013be4SDimitry Andric     break;
1119fe013be4SDimitry Andric   case loop_type_t::loop_type_uint64:
1120fe013be4SDimitry Andric     return kmp_calc_one_iv_for_chunk_end_XX<kmp_uint64>(
1121fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_uint64> *)(bounds),
1122fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_uint64> *)(updated_bounds),
1123fe013be4SDimitry Andric         /*in/out*/
1124fe013be4SDimitry Andric         original_ivs, iterations, ind, start_with_lower_bound,
1125fe013be4SDimitry Andric         compare_with_start, original_ivs_start);
1126fe013be4SDimitry Andric     break;
1127fe013be4SDimitry Andric   default:
1128fe013be4SDimitry Andric     KMP_ASSERT(false);
1129fe013be4SDimitry Andric     return false;
1130fe013be4SDimitry Andric   }
1131fe013be4SDimitry Andric }
1132fe013be4SDimitry Andric 
1133fe013be4SDimitry Andric // Calculate old induction variables corresponding to overall new_iv for the
1134fe013be4SDimitry Andric // chunk end. If due to space extension we are getting old IVs outside of the
1135fe013be4SDimitry Andric // boundaries, bring them into the boundaries. Need to do this in the runtime,
1136fe013be4SDimitry Andric // esp. on the lower bounds side. When getting result need to make sure that the
1137fe013be4SDimitry Andric // new chunk starts at next position to old chunk, not overlaps with it (this is
1138fe013be4SDimitry Andric // done elsewhere), and need to make sure end of the chunk is further than the
1139fe013be4SDimitry Andric // beginning of the chunk. We don't need an exact ending point here, just
1140fe013be4SDimitry Andric // something more-or-less close to the desired chunk length, bigger is fine
1141fe013be4SDimitry Andric // (smaller would be fine, but we risk going into infinite loop, so do smaller
1142fe013be4SDimitry Andric // only at the very end of the space). result: false if could not find the
1143fe013be4SDimitry Andric // ending point in the original loop space. In this case the caller can use
1144fe013be4SDimitry Andric // original upper bounds as the end of the chunk. Chunk won't be empty, because
1145fe013be4SDimitry Andric // it'll have at least the starting point, which is by construction in the
1146fe013be4SDimitry Andric // original space.
kmp_calc_original_ivs_for_chunk_end(const bounds_info_t * original_bounds_nest,kmp_index_t n,const bounds_info_internal_t * updated_bounds_nest,const kmp_point_t original_ivs_start,kmp_loop_nest_iv_t new_iv,kmp_point_t original_ivs)1147fe013be4SDimitry Andric bool kmp_calc_original_ivs_for_chunk_end(
1148fe013be4SDimitry Andric     const bounds_info_t *original_bounds_nest, kmp_index_t n,
1149fe013be4SDimitry Andric     const bounds_info_internal_t *updated_bounds_nest,
1150fe013be4SDimitry Andric     const kmp_point_t original_ivs_start, kmp_loop_nest_iv_t new_iv,
1151fe013be4SDimitry Andric     /*out*/ kmp_point_t original_ivs) {
1152fe013be4SDimitry Andric 
1153fe013be4SDimitry Andric   // Iterations in the expanded space:
1154*c9157d92SDimitry Andric   CollapseAllocator<kmp_loop_nest_iv_t> iterations(n);
1155fe013be4SDimitry Andric   // First, calc corresponding iteration in every modified loop:
1156fe013be4SDimitry Andric   for (kmp_index_t ind = n; ind > 0;) {
1157fe013be4SDimitry Andric     --ind;
1158fe013be4SDimitry Andric     auto &updated_bounds = updated_bounds_nest[ind];
1159fe013be4SDimitry Andric 
1160fe013be4SDimitry Andric     // should be optimized to OPDIVREM:
1161fe013be4SDimitry Andric     auto new_ind = new_iv / updated_bounds.b.trip_count;
1162fe013be4SDimitry Andric     auto iteration = new_iv % updated_bounds.b.trip_count;
1163fe013be4SDimitry Andric 
1164fe013be4SDimitry Andric     new_iv = new_ind;
1165fe013be4SDimitry Andric     iterations[ind] = iteration;
1166fe013be4SDimitry Andric   }
1167fe013be4SDimitry Andric   KMP_DEBUG_ASSERT(new_iv == 0);
1168fe013be4SDimitry Andric 
1169fe013be4SDimitry Andric   kmp_index_t lengthened_ind = n;
1170fe013be4SDimitry Andric   kmp_index_t equal_ind = -1;
1171fe013be4SDimitry Andric 
1172fe013be4SDimitry Andric   // Next calculate the point, but in original loop nest.
1173fe013be4SDimitry Andric   for (kmp_index_t ind = 0; ind < n;) {
1174fe013be4SDimitry Andric     auto bounds = &(original_bounds_nest[ind]);
1175fe013be4SDimitry Andric     auto updated_bounds = &(updated_bounds_nest[ind].b);
1176fe013be4SDimitry Andric 
1177fe013be4SDimitry Andric     bool good = kmp_calc_one_iv_for_chunk_end(
1178fe013be4SDimitry Andric         bounds, updated_bounds,
1179fe013be4SDimitry Andric         /*in/out*/ original_ivs, iterations, ind, (lengthened_ind < ind),
1180fe013be4SDimitry Andric         (equal_ind >= ind - 1), original_ivs_start);
1181fe013be4SDimitry Andric 
1182fe013be4SDimitry Andric     if (!good) {
1183fe013be4SDimitry Andric       // Too big (or too small for >=).
1184fe013be4SDimitry Andric       if (ind == 0) {
1185fe013be4SDimitry Andric         // Need to reduce to the end.
1186fe013be4SDimitry Andric         return false;
1187fe013be4SDimitry Andric       } else {
1188fe013be4SDimitry Andric         // Go to next iteration on outer loop:
1189fe013be4SDimitry Andric         --ind;
1190fe013be4SDimitry Andric         ++(iterations[ind]);
1191fe013be4SDimitry Andric         lengthened_ind = ind;
1192fe013be4SDimitry Andric         if (equal_ind >= lengthened_ind) {
1193fe013be4SDimitry Andric           // We've changed the number of iterations here,
1194fe013be4SDimitry Andric           // can't be same anymore:
1195fe013be4SDimitry Andric           equal_ind = lengthened_ind - 1;
1196fe013be4SDimitry Andric         }
1197fe013be4SDimitry Andric         for (kmp_index_t i = ind + 1; i < n; ++i) {
1198fe013be4SDimitry Andric           iterations[i] = 0;
1199fe013be4SDimitry Andric         }
1200fe013be4SDimitry Andric         continue;
1201fe013be4SDimitry Andric       }
1202fe013be4SDimitry Andric     }
1203fe013be4SDimitry Andric 
1204fe013be4SDimitry Andric     if ((equal_ind == ind - 1) &&
1205fe013be4SDimitry Andric         (kmp_ivs_eq(bounds->loop_iv_type, original_ivs[ind],
1206fe013be4SDimitry Andric                     original_ivs_start[ind]))) {
1207fe013be4SDimitry Andric       equal_ind = ind;
1208fe013be4SDimitry Andric     } else if ((equal_ind > ind - 1) &&
1209fe013be4SDimitry Andric                !(kmp_ivs_eq(bounds->loop_iv_type, original_ivs[ind],
1210fe013be4SDimitry Andric                             original_ivs_start[ind]))) {
1211fe013be4SDimitry Andric       equal_ind = ind - 1;
1212fe013be4SDimitry Andric     }
1213fe013be4SDimitry Andric     ++ind;
1214fe013be4SDimitry Andric   }
1215fe013be4SDimitry Andric 
1216fe013be4SDimitry Andric   return true;
1217fe013be4SDimitry Andric }
1218fe013be4SDimitry Andric 
1219fe013be4SDimitry Andric //----------Calculate upper bounds for the last chunk-------------------------
1220fe013be4SDimitry Andric 
1221fe013be4SDimitry Andric // Calculate one upper bound for the end.
1222fe013be4SDimitry Andric template <typename T>
kmp_calc_one_iv_end_XX(const bounds_infoXX_template<T> * bounds,kmp_point_t original_ivs,kmp_index_t ind)1223fe013be4SDimitry Andric void kmp_calc_one_iv_end_XX(const bounds_infoXX_template<T> *bounds,
1224fe013be4SDimitry Andric                             /*in/out*/ kmp_point_t original_ivs,
1225fe013be4SDimitry Andric                             kmp_index_t ind) {
1226fe013be4SDimitry Andric 
1227fe013be4SDimitry Andric   T temp = bounds->ub0 +
1228fe013be4SDimitry Andric            bounds->ub1 * static_cast<T>(original_ivs[bounds->outer_iv]);
1229fe013be4SDimitry Andric 
1230fe013be4SDimitry Andric   original_ivs[ind] = kmp_fix_iv(bounds->loop_iv_type, temp);
1231fe013be4SDimitry Andric }
1232fe013be4SDimitry Andric 
kmp_calc_one_iv_end(const bounds_info_t * bounds,kmp_point_t original_ivs,kmp_index_t ind)1233fe013be4SDimitry Andric void kmp_calc_one_iv_end(const bounds_info_t *bounds,
1234fe013be4SDimitry Andric                          /*in/out*/ kmp_point_t original_ivs, kmp_index_t ind) {
1235fe013be4SDimitry Andric 
1236fe013be4SDimitry Andric   switch (bounds->loop_type) {
1237fe013be4SDimitry Andric   default:
1238fe013be4SDimitry Andric     KMP_ASSERT(false);
1239fe013be4SDimitry Andric     break;
1240fe013be4SDimitry Andric   case loop_type_t::loop_type_int32:
1241fe013be4SDimitry Andric     kmp_calc_one_iv_end_XX<kmp_int32>(
1242fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_int32> *)(bounds),
1243fe013be4SDimitry Andric         /*in/out*/ original_ivs, ind);
1244fe013be4SDimitry Andric     break;
1245fe013be4SDimitry Andric   case loop_type_t::loop_type_uint32:
1246fe013be4SDimitry Andric     kmp_calc_one_iv_end_XX<kmp_uint32>(
1247fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_uint32> *)(bounds),
1248fe013be4SDimitry Andric         /*in/out*/ original_ivs, ind);
1249fe013be4SDimitry Andric     break;
1250fe013be4SDimitry Andric   case loop_type_t::loop_type_int64:
1251fe013be4SDimitry Andric     kmp_calc_one_iv_end_XX<kmp_int64>(
1252fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_int64> *)(bounds),
1253fe013be4SDimitry Andric         /*in/out*/ original_ivs, ind);
1254fe013be4SDimitry Andric     break;
1255fe013be4SDimitry Andric   case loop_type_t::loop_type_uint64:
1256fe013be4SDimitry Andric     kmp_calc_one_iv_end_XX<kmp_uint64>(
1257fe013be4SDimitry Andric         (bounds_infoXX_template<kmp_uint64> *)(bounds),
1258fe013be4SDimitry Andric         /*in/out*/ original_ivs, ind);
1259fe013be4SDimitry Andric     break;
1260fe013be4SDimitry Andric   }
1261fe013be4SDimitry Andric }
1262fe013be4SDimitry Andric 
1263fe013be4SDimitry Andric // Calculate upper bounds for the last loop iteration. Just use original upper
1264fe013be4SDimitry Andric // bounds (adjusted when canonicalized to use <= / >=). No need to check that
1265fe013be4SDimitry Andric // this point is in the original space (it's likely not)
kmp_calc_original_ivs_for_end(const bounds_info_t * const original_bounds_nest,kmp_index_t n,kmp_point_t original_ivs)1266fe013be4SDimitry Andric void kmp_calc_original_ivs_for_end(
1267fe013be4SDimitry Andric     const bounds_info_t *const original_bounds_nest, kmp_index_t n,
1268fe013be4SDimitry Andric     /*out*/ kmp_point_t original_ivs) {
1269fe013be4SDimitry Andric   for (kmp_index_t ind = 0; ind < n; ++ind) {
1270fe013be4SDimitry Andric     auto bounds = &(original_bounds_nest[ind]);
1271fe013be4SDimitry Andric     kmp_calc_one_iv_end(bounds, /*in/out*/ original_ivs, ind);
1272fe013be4SDimitry Andric   }
1273fe013be4SDimitry Andric }
1274fe013be4SDimitry Andric 
1275fe013be4SDimitry Andric //----------Init API for non-rectangular loops--------------------------------
1276fe013be4SDimitry Andric 
1277fe013be4SDimitry Andric // Init API for collapsed loops (static, no chunks defined).
1278fe013be4SDimitry Andric // "bounds_nest" has to be allocated per thread.
1279fe013be4SDimitry Andric // API will modify original bounds_nest array to bring it to a canonical form
1280fe013be4SDimitry Andric // (only <= and >=, no !=, <, >). If the original loop nest was already in a
1281fe013be4SDimitry Andric // canonical form there will be no changes to bounds in bounds_nest array
1282fe013be4SDimitry Andric // (only trip counts will be calculated). Internally API will expand the space
1283fe013be4SDimitry Andric // to parallelogram/parallelepiped, calculate total, calculate bounds for the
1284fe013be4SDimitry Andric // chunks in terms of the new IV, re-calc them in terms of old IVs (especially
1285fe013be4SDimitry Andric // important on the left side, to hit the lower bounds and not step over), and
1286fe013be4SDimitry Andric // pick the correct chunk for this thread (so it will calculate chunks up to the
1287fe013be4SDimitry Andric // needed one). It could be optimized to calculate just this chunk, potentially
1288fe013be4SDimitry Andric // a bit less well distributed among threads. It is designed to make sure that
1289fe013be4SDimitry Andric // threads will receive predictable chunks, deterministically (so that next nest
1290fe013be4SDimitry Andric // of loops with similar characteristics will get exactly same chunks on same
1291fe013be4SDimitry Andric // threads).
1292fe013be4SDimitry Andric // Current contract: chunk_bounds_nest has only lb0 and ub0,
1293fe013be4SDimitry Andric // lb1 and ub1 are set to 0 and can be ignored. (This may change in the future).
1294fe013be4SDimitry Andric extern "C" kmp_int32
__kmpc_for_collapsed_init(ident_t * loc,kmp_int32 gtid,bounds_info_t * original_bounds_nest,bounds_info_t * chunk_bounds_nest,kmp_index_t n,kmp_int32 * plastiter)1295fe013be4SDimitry Andric __kmpc_for_collapsed_init(ident_t *loc, kmp_int32 gtid,
1296fe013be4SDimitry Andric                           /*in/out*/ bounds_info_t *original_bounds_nest,
1297fe013be4SDimitry Andric                           /*out*/ bounds_info_t *chunk_bounds_nest,
1298fe013be4SDimitry Andric                           kmp_index_t n, /*out*/ kmp_int32 *plastiter) {
1299fe013be4SDimitry Andric 
1300fe013be4SDimitry Andric   KMP_DEBUG_ASSERT(plastiter && original_bounds_nest);
1301fe013be4SDimitry Andric   KE_TRACE(10, ("__kmpc_for_collapsed_init called (%d)\n", gtid));
1302fe013be4SDimitry Andric 
1303fe013be4SDimitry Andric   if (__kmp_env_consistency_check) {
1304fe013be4SDimitry Andric     __kmp_push_workshare(gtid, ct_pdo, loc);
1305fe013be4SDimitry Andric   }
1306fe013be4SDimitry Andric 
1307fe013be4SDimitry Andric   kmp_canonicalize_loop_nest(loc, /*in/out*/ original_bounds_nest, n);
1308fe013be4SDimitry Andric 
1309*c9157d92SDimitry Andric   CollapseAllocator<bounds_info_internal_t> updated_bounds_nest(n);
1310fe013be4SDimitry Andric 
1311fe013be4SDimitry Andric   for (kmp_index_t i = 0; i < n; ++i) {
1312fe013be4SDimitry Andric     updated_bounds_nest[i].b = original_bounds_nest[i];
1313fe013be4SDimitry Andric   }
1314fe013be4SDimitry Andric 
1315fe013be4SDimitry Andric   kmp_loop_nest_iv_t total =
1316fe013be4SDimitry Andric       kmp_process_loop_nest(/*in/out*/ updated_bounds_nest, n);
1317fe013be4SDimitry Andric 
1318fe013be4SDimitry Andric   if (plastiter != NULL) {
1319fe013be4SDimitry Andric     *plastiter = FALSE;
1320fe013be4SDimitry Andric   }
1321fe013be4SDimitry Andric 
1322fe013be4SDimitry Andric   if (total == 0) {
1323fe013be4SDimitry Andric     // Loop won't execute:
1324fe013be4SDimitry Andric     return FALSE;
1325fe013be4SDimitry Andric   }
1326fe013be4SDimitry Andric 
1327fe013be4SDimitry Andric   // OMPTODO: DISTRIBUTE is not supported yet
1328fe013be4SDimitry Andric   __kmp_assert_valid_gtid(gtid);
1329fe013be4SDimitry Andric   kmp_uint32 tid = __kmp_tid_from_gtid(gtid);
1330fe013be4SDimitry Andric 
1331fe013be4SDimitry Andric   kmp_info_t *th = __kmp_threads[gtid];
1332fe013be4SDimitry Andric   kmp_team_t *team = th->th.th_team;
1333fe013be4SDimitry Andric   kmp_uint32 nth = team->t.t_nproc; // Number of threads
1334fe013be4SDimitry Andric 
1335fe013be4SDimitry Andric   KMP_DEBUG_ASSERT(tid < nth);
1336fe013be4SDimitry Andric 
1337*c9157d92SDimitry Andric   CollapseAllocator<kmp_uint64> original_ivs_start(n);
1338fe013be4SDimitry Andric 
1339fe013be4SDimitry Andric   if (!kmp_calc_original_ivs_for_start(original_bounds_nest, n,
1340fe013be4SDimitry Andric                                        /*out*/ original_ivs_start)) {
1341fe013be4SDimitry Andric     // Loop won't execute:
1342fe013be4SDimitry Andric     return FALSE;
1343fe013be4SDimitry Andric   }
1344fe013be4SDimitry Andric 
1345fe013be4SDimitry Andric   // Not doing this optimization for one thread:
1346fe013be4SDimitry Andric   // (1) more to test
1347fe013be4SDimitry Andric   // (2) without it current contract that chunk_bounds_nest has only lb0 and
1348fe013be4SDimitry Andric   // ub0, lb1 and ub1 are set to 0 and can be ignored.
1349fe013be4SDimitry Andric   // if (nth == 1) {
1350fe013be4SDimitry Andric   //  // One thread:
1351fe013be4SDimitry Andric   //  // Copy all info from original_bounds_nest, it'll be good enough.
1352fe013be4SDimitry Andric 
1353fe013be4SDimitry Andric   //  for (kmp_index_t i = 0; i < n; ++i) {
1354fe013be4SDimitry Andric   //    chunk_bounds_nest[i] = original_bounds_nest[i];
1355fe013be4SDimitry Andric   //  }
1356fe013be4SDimitry Andric 
1357fe013be4SDimitry Andric   //  if (plastiter != NULL) {
1358fe013be4SDimitry Andric   //    *plastiter = TRUE;
1359fe013be4SDimitry Andric   //  }
1360fe013be4SDimitry Andric   //  return TRUE;
1361fe013be4SDimitry Andric   //}
1362fe013be4SDimitry Andric 
1363fe013be4SDimitry Andric   kmp_loop_nest_iv_t new_iv = kmp_calc_new_iv_from_original_ivs(
1364fe013be4SDimitry Andric       updated_bounds_nest, original_ivs_start, n);
1365fe013be4SDimitry Andric 
1366fe013be4SDimitry Andric   bool last_iter = false;
1367fe013be4SDimitry Andric 
1368fe013be4SDimitry Andric   for (; nth > 0;) {
1369fe013be4SDimitry Andric     // We could calculate chunk size once, but this is to compensate that the
1370fe013be4SDimitry Andric     // original space is not parallelepiped and some threads can be left
1371fe013be4SDimitry Andric     // without work:
1372fe013be4SDimitry Andric     KMP_DEBUG_ASSERT(total >= new_iv);
1373fe013be4SDimitry Andric 
1374fe013be4SDimitry Andric     kmp_loop_nest_iv_t total_left = total - new_iv;
1375fe013be4SDimitry Andric     kmp_loop_nest_iv_t chunk_size = total_left / nth;
1376fe013be4SDimitry Andric     kmp_loop_nest_iv_t remainder = total_left % nth;
1377fe013be4SDimitry Andric 
1378fe013be4SDimitry Andric     kmp_loop_nest_iv_t curr_chunk_size = chunk_size;
1379fe013be4SDimitry Andric 
1380fe013be4SDimitry Andric     if (remainder > 0) {
1381fe013be4SDimitry Andric       ++curr_chunk_size;
1382fe013be4SDimitry Andric       --remainder;
1383fe013be4SDimitry Andric     }
1384fe013be4SDimitry Andric 
1385fe013be4SDimitry Andric #if defined(KMP_DEBUG)
1386fe013be4SDimitry Andric     kmp_loop_nest_iv_t new_iv_for_start = new_iv;
1387fe013be4SDimitry Andric #endif
1388fe013be4SDimitry Andric 
1389fe013be4SDimitry Andric     if (curr_chunk_size > 1) {
1390fe013be4SDimitry Andric       new_iv += curr_chunk_size - 1;
1391fe013be4SDimitry Andric     }
1392fe013be4SDimitry Andric 
1393*c9157d92SDimitry Andric     CollapseAllocator<kmp_uint64> original_ivs_end(n);
1394fe013be4SDimitry Andric     if ((nth == 1) || (new_iv >= total - 1)) {
1395fe013be4SDimitry Andric       // Do this one till the end - just in case we miscalculated
1396fe013be4SDimitry Andric       // and either too much is left to process or new_iv is a bit too big:
1397fe013be4SDimitry Andric       kmp_calc_original_ivs_for_end(original_bounds_nest, n,
1398fe013be4SDimitry Andric                                     /*out*/ original_ivs_end);
1399fe013be4SDimitry Andric 
1400fe013be4SDimitry Andric       last_iter = true;
1401fe013be4SDimitry Andric     } else {
1402fe013be4SDimitry Andric       // Note: here we make sure it's past (or equal to) the previous point.
1403fe013be4SDimitry Andric       if (!kmp_calc_original_ivs_for_chunk_end(original_bounds_nest, n,
1404fe013be4SDimitry Andric                                                updated_bounds_nest,
1405fe013be4SDimitry Andric                                                original_ivs_start, new_iv,
1406fe013be4SDimitry Andric                                                /*out*/ original_ivs_end)) {
1407fe013be4SDimitry Andric         // We could not find the ending point, use the original upper bounds:
1408fe013be4SDimitry Andric         kmp_calc_original_ivs_for_end(original_bounds_nest, n,
1409fe013be4SDimitry Andric                                       /*out*/ original_ivs_end);
1410fe013be4SDimitry Andric 
1411fe013be4SDimitry Andric         last_iter = true;
1412fe013be4SDimitry Andric       }
1413fe013be4SDimitry Andric     }
1414fe013be4SDimitry Andric 
1415fe013be4SDimitry Andric #if defined(KMP_DEBUG)
1416fe013be4SDimitry Andric     auto new_iv_for_end = kmp_calc_new_iv_from_original_ivs(
1417fe013be4SDimitry Andric         updated_bounds_nest, original_ivs_end, n);
1418fe013be4SDimitry Andric     KMP_DEBUG_ASSERT(new_iv_for_end >= new_iv_for_start);
1419fe013be4SDimitry Andric #endif
1420fe013be4SDimitry Andric 
1421fe013be4SDimitry Andric     if (last_iter && (tid != 0)) {
1422fe013be4SDimitry Andric       // We are done, this was last chunk, but no chunk for current thread was
1423fe013be4SDimitry Andric       // found:
1424fe013be4SDimitry Andric       return FALSE;
1425fe013be4SDimitry Andric     }
1426fe013be4SDimitry Andric 
1427fe013be4SDimitry Andric     if (tid == 0) {
1428fe013be4SDimitry Andric       // We found the chunk for this thread, now we need to check if it's the
1429fe013be4SDimitry Andric       // last chunk or not:
1430fe013be4SDimitry Andric 
1431*c9157d92SDimitry Andric       CollapseAllocator<kmp_uint64> original_ivs_next_start(n);
1432fe013be4SDimitry Andric       if (last_iter ||
1433fe013be4SDimitry Andric           !kmp_calc_next_original_ivs(original_bounds_nest, n, original_ivs_end,
1434fe013be4SDimitry Andric                                       /*out*/ original_ivs_next_start)) {
1435fe013be4SDimitry Andric         // no more loop iterations left to process,
1436fe013be4SDimitry Andric         // this means that currently found chunk is the last chunk:
1437fe013be4SDimitry Andric         if (plastiter != NULL) {
1438fe013be4SDimitry Andric           *plastiter = TRUE;
1439fe013be4SDimitry Andric         }
1440fe013be4SDimitry Andric       }
1441fe013be4SDimitry Andric 
1442fe013be4SDimitry Andric       // Fill in chunk bounds:
1443fe013be4SDimitry Andric       for (kmp_index_t i = 0; i < n; ++i) {
1444fe013be4SDimitry Andric         chunk_bounds_nest[i] =
1445fe013be4SDimitry Andric             original_bounds_nest[i]; // To fill in types, etc. - optional
1446fe013be4SDimitry Andric         chunk_bounds_nest[i].lb0_u64 = original_ivs_start[i];
1447fe013be4SDimitry Andric         chunk_bounds_nest[i].lb1_u64 = 0;
1448fe013be4SDimitry Andric 
1449fe013be4SDimitry Andric         chunk_bounds_nest[i].ub0_u64 = original_ivs_end[i];
1450fe013be4SDimitry Andric         chunk_bounds_nest[i].ub1_u64 = 0;
1451fe013be4SDimitry Andric       }
1452fe013be4SDimitry Andric 
1453fe013be4SDimitry Andric       return TRUE;
1454fe013be4SDimitry Andric     }
1455fe013be4SDimitry Andric 
1456fe013be4SDimitry Andric     --tid;
1457fe013be4SDimitry Andric     --nth;
1458fe013be4SDimitry Andric 
1459fe013be4SDimitry Andric     bool next_chunk = kmp_calc_next_original_ivs(
1460fe013be4SDimitry Andric         original_bounds_nest, n, original_ivs_end, /*out*/ original_ivs_start);
1461fe013be4SDimitry Andric     if (!next_chunk) {
1462fe013be4SDimitry Andric       // no more loop iterations to process,
1463fe013be4SDimitry Andric       // the prevoius chunk was the last chunk
1464fe013be4SDimitry Andric       break;
1465fe013be4SDimitry Andric     }
1466fe013be4SDimitry Andric 
1467fe013be4SDimitry Andric     // original_ivs_start is next to previous chunk original_ivs_end,
1468fe013be4SDimitry Andric     // we need to start new chunk here, so chunks will be one after another
1469fe013be4SDimitry Andric     // without any gap or overlap:
1470fe013be4SDimitry Andric     new_iv = kmp_calc_new_iv_from_original_ivs(updated_bounds_nest,
1471fe013be4SDimitry Andric                                                original_ivs_start, n);
1472fe013be4SDimitry Andric   }
1473fe013be4SDimitry Andric 
1474fe013be4SDimitry Andric   return FALSE;
1475fe013be4SDimitry Andric }
1476