PR target/66148
[official-gcc.git] / libcilkrts / runtime / reducer_impl.cpp
blobf20b9bc459274684763689d599b65ed73e287973
1 /* reducer_impl.cpp -*-C++-*-
3 *************************************************************************
5 * @copyright
6 * Copyright (C) 2009-2013, Intel Corporation
7 * All rights reserved.
8 *
9 * @copyright
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
14 * * Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * * Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in
18 * the documentation and/or other materials provided with the
19 * distribution.
20 * * Neither the name of Intel Corporation nor the names of its
21 * contributors may be used to endorse or promote products derived
22 * from this software without specific prior written permission.
24 * @copyright
25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
30 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
32 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
33 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
35 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
38 * Patents Pending, Intel Corporation.
39 **************************************************************************/
41 /**
42 * Support for reducers
45 // ICL: Don't complain about conversion from pointer to same-sized integral type
46 // in hashfun. That's why we're using size_t
47 #ifdef _WIN32
48 # pragma warning(disable: 1684)
49 #endif
51 #include "reducer_impl.h"
52 #include "scheduler.h"
53 #include "bug.h"
54 #include "os.h"
55 #include "global_state.h"
56 #include "frame_malloc.h"
58 #include "cilk/hyperobject_base.h"
59 #include "cilktools/cilkscreen.h"
60 #include "internal/abi.h"
62 #if REDPAR_DEBUG > 0
63 #include <stdio.h>
64 #include <stdlib.h>
65 #endif
68 #define DBG if(0) // if(1) enables some internal checks
70 // Check that w is the currently executing worker. This method is a
71 // no-op unless the debug level is set high enough.
72 static inline void verify_current_wkr(__cilkrts_worker *w)
74 #if REDPAR_DEBUG >= 5
75 __cilkrts_worker* tmp = __cilkrts_get_tls_worker();
76 if (w != tmp) {
77 fprintf(stderr, "W=%d, actual=%d... missing a refresh....\n",
78 w->self,
79 tmp->self);
81 CILK_ASSERT(w == tmp); // __cilkrts_get_tls_worker());
82 #endif
85 // Suppress clang warning that the expression result is unused
86 #if defined(__clang__) && (! defined(__INTEL_COMPILER))
87 # pragma clang diagnostic push
88 # pragma clang diagnostic ignored "-Wunused-value"
89 #endif // __clang__
91 /// Helper class to disable and re-enable Cilkscreen
92 struct DisableCilkscreen
94 DisableCilkscreen () { __cilkscreen_disable_checking(); }
95 ~DisableCilkscreen () { __cilkscreen_enable_checking(); }
98 /// Helper class to enable and re-disable Cilkscreen
99 struct EnableCilkscreen
101 EnableCilkscreen () { __cilkscreen_enable_checking(); }
102 ~EnableCilkscreen () { __cilkscreen_disable_checking(); }
105 #if defined(__clang__) && (! defined(__INTEL_COMPILER))
106 # pragma clang diagnostic pop
107 #endif // __clang__
110 * @brief Element for a hyperobject
112 struct elem {
113 void *key; ///< Shared key for this hyperobject
114 __cilkrts_hyperobject_base *hb; ///< Base of the hyperobject.
115 void *view; ///< Strand-private view of this hyperobject
116 /// Destroy and deallocate the view object for this element and set view to
117 /// null.
118 void destroy();
120 /// Returns true if this element contains a leftmost view.
121 bool is_leftmost() const;
124 /** Bucket containing at most NMAX elements */
125 struct bucket {
126 /// Size of the array of elements for this bucket
127 size_t nmax;
130 * We use the ``struct hack'' to allocate an array of variable
131 * dimension at the end of the struct. However, we allocate a
132 * total of NMAX+1 elements instead of NMAX. The last one always
133 * has key == 0, which we use as a termination criterion
135 elem el[1];
139 * Class that implements the map for reducers so we can find the
140 * view for a strand.
142 struct cilkred_map {
143 /** Handy pointer to the global state */
144 global_state_t *g;
146 /** Number of elements in table */
147 size_t nelem;
149 /** Number of buckets */
150 size_t nbuckets;
152 /** Array of pointers to buckets */
153 bucket **buckets;
155 /** Set true if merging (for debugging purposes) */
156 bool merging;
158 /** Set true for leftmost reducer map */
159 bool is_leftmost;
161 /** @brief Return element mapped to 'key' or null if not found. */
162 elem *lookup(void *key);
165 * @brief Insert key/value element into hash map without rehashing.
166 * Does not check for duplicate key.
168 elem *insert_no_rehash(__cilkrts_worker *w,
169 void *key,
170 __cilkrts_hyperobject_base *hb,
171 void *value);
174 * @brief Insert key/value element into hash map, rehashing if necessary.
175 * Does not check for duplicate key.
177 inline elem *rehash_and_insert(__cilkrts_worker *w,
178 void *key,
179 __cilkrts_hyperobject_base *hb,
180 void *value);
182 /** @brief Grow bucket by one element, reallocating bucket if necessary */
183 static elem *grow(__cilkrts_worker *w, bucket **bp);
185 /** @brief Rehash a worker's reducer map */
186 void rehash(__cilkrts_worker *);
189 * @brief Returns true if a rehash is needed due to the number of elements that
190 * have been inserted.
192 inline bool need_rehash_p() const;
194 /** @brief Allocate and initialize the buckets */
195 void make_buckets(__cilkrts_worker *w, size_t nbuckets);
198 * Specify behavior when the same key is present in both maps passed
199 * into merge().
201 enum merge_kind
203 MERGE_UNORDERED, ///< Assertion fails
204 MERGE_INTO_LEFT, ///< Merges the argument from the right into the left
205 MERGE_INTO_RIGHT ///< Merges the argument from the left into the right
209 * @brief Merge another reducer map into this one, destroying the other map in
210 * the process.
212 __cilkrts_worker* merge(__cilkrts_worker *current_wkr,
213 cilkred_map *other_map,
214 enum merge_kind kind);
216 /** @brief check consistency of a reducer map */
217 void check(bool allow_null_view);
219 /** @brief Test whether the cilkred_map is empty */
220 bool is_empty() { return nelem == 0; }
223 static inline struct cilkred_map* install_new_reducer_map(__cilkrts_worker *w) {
224 cilkred_map *h;
225 h = __cilkrts_make_reducer_map(w);
226 w->reducer_map = h;
227 return h;
230 static size_t sizeof_bucket(size_t nmax)
232 bucket *b = 0;
233 return (sizeof(*b) + nmax * sizeof(b->el[0]));
236 static bucket *alloc_bucket(__cilkrts_worker *w, size_t nmax)
238 bucket *b = (bucket *)
239 __cilkrts_frame_malloc(w, sizeof_bucket(nmax));
240 b->nmax = nmax;
241 return b;
244 static void free_bucket(__cilkrts_worker *w, bucket **bp)
246 bucket *b = *bp;
247 if (b) {
248 __cilkrts_frame_free(w, b, sizeof_bucket(b->nmax));
249 *bp = 0;
253 /* round up nmax to fill a memory allocator block completely */
254 static size_t roundup(size_t nmax)
256 size_t sz = sizeof_bucket(nmax);
258 /* round up size to a full malloc block */
259 sz = __cilkrts_frame_malloc_roundup(sz);
261 /* invert sizeof_bucket() */
262 nmax = ((sz - sizeof(bucket)) / sizeof(elem));
264 return nmax;
267 static bool is_power_of_2(size_t n)
269 return (n & (n - 1)) == 0;
272 void cilkred_map::make_buckets(__cilkrts_worker *w,
273 size_t new_nbuckets)
275 nbuckets = new_nbuckets;
277 CILK_ASSERT(is_power_of_2(nbuckets));
278 #if defined __GNUC__ && defined __ICC
279 /* bug workaround -- suppress calls to _intel_fast_memset */
280 bucket *volatile*new_buckets = (bucket *volatile*)
281 #else
282 bucket **new_buckets = (bucket **)
283 #endif
284 __cilkrts_frame_malloc(w, nbuckets * sizeof(*(buckets)));
286 #if REDPAR_DEBUG >= 1
287 fprintf(stderr, "W=%d, desc=make_buckets, new_buckets=%p, new_nbuckets=%zd\n",
288 w->self, new_buckets, new_nbuckets);
289 #endif
291 for (size_t i = 0; i < new_nbuckets; ++i)
292 new_buckets[i] = 0;
293 #if defined __GNUC__ && defined __ICC
294 buckets = (bucket **)new_buckets;
295 #else
296 buckets = new_buckets;
297 #endif
298 nelem = 0;
301 static void free_buckets(__cilkrts_worker *w,
302 bucket **buckets,
303 size_t nbuckets)
305 size_t i;
307 #if REDPAR_DEBUG >= 1
308 verify_current_wkr(w);
309 fprintf(stderr, "W=%d, desc=free_buckets, buckets=%p, size=%zd\n",
310 w->self, buckets,
311 nbuckets * sizeof(*buckets));
312 #endif
314 for (i = 0; i < nbuckets; ++i)
315 free_bucket(w, buckets + i);
317 __cilkrts_frame_free(w, buckets, nbuckets * sizeof(*buckets));
320 static size_t minsz(size_t nelem)
322 return 1U + nelem + nelem / 8U;
325 static size_t nextsz(size_t nelem)
327 return 2 * nelem;
330 bool cilkred_map::need_rehash_p() const
332 return minsz(nelem) > nbuckets;
335 static inline size_t hashfun(const cilkred_map *h, void *key)
337 size_t k = (size_t) key;
339 k ^= k >> 21;
340 k ^= k >> 8;
341 k ^= k >> 3;
343 return k & (h->nbuckets - 1);
346 // Given a __cilkrts_hyperobject_base, return the key to that hyperobject in
347 // the reducer map.
348 static inline void* get_hyperobject_key(__cilkrts_hyperobject_base *hb)
350 // The current implementation uses the address of the lefmost view as the
351 // key.
352 return reinterpret_cast<char*>(hb) + hb->__view_offset;
355 // Given a hyperobject key, return a pointer to the leftmost object. In the
356 // current implementation, the address of the leftmost object IS the key, so
357 // this function is an effective noop.
358 static inline void* get_leftmost_view(void *key)
360 return key;
363 /* debugging support: check consistency of a reducer map */
364 void cilkred_map::check(bool allow_null_view)
366 size_t count = 0;
368 CILK_ASSERT(buckets);
369 for (size_t i = 0; i < nbuckets; ++i) {
370 bucket *b = buckets[i];
371 if (b)
372 for (elem *el = b->el; el->key; ++el) {
373 CILK_ASSERT(allow_null_view || el->view);
374 ++count;
377 CILK_ASSERT(nelem == count);
378 /*global_reducer_map::check();*/
381 /* grow bucket by one element, reallocating bucket if necessary */
382 elem *cilkred_map::grow(__cilkrts_worker *w,
383 bucket **bp)
385 size_t i, nmax, nnmax;
386 bucket *b, *nb;
388 b = *bp;
389 if (b) {
390 nmax = b->nmax;
391 /* find empty element if any */
392 for (i = 0; i < nmax; ++i)
393 if (b->el[i].key == 0)
394 return &(b->el[i]);
395 /* do not use the last one even if empty */
396 } else {
397 nmax = 0;
400 verify_current_wkr(w);
401 /* allocate a new bucket */
402 nnmax = roundup(2 * nmax);
403 nb = alloc_bucket(w, nnmax);
406 /* copy old bucket into new */
407 for (i = 0; i < nmax; ++i)
408 nb->el[i] = b->el[i];
410 free_bucket(w, bp); *bp = nb;
412 /* zero out extra elements */
413 for (; i < nnmax; ++i)
414 nb->el[i].key = 0;
416 /* zero out the last one */
417 nb->el[i].key = 0;
419 return &(nb->el[nmax]);
422 elem *cilkred_map::insert_no_rehash(__cilkrts_worker *w,
423 void *key,
424 __cilkrts_hyperobject_base *hb,
425 void *view)
428 #if REDPAR_DEBUG >= 2
429 fprintf(stderr, "[W=%d, desc=insert_no_rehash, this_map=%p]\n",
430 w->self, this);
431 verify_current_wkr(w);
432 #endif
434 CILK_ASSERT((w == 0 && g == 0) || w->g == g);
435 CILK_ASSERT(key != 0);
436 CILK_ASSERT(view != 0);
438 elem *el = grow(w, &(buckets[hashfun(this, key)]));
440 #if REDPAR_DEBUG >= 3
441 fprintf(stderr, "[W=%d, this=%p, inserting key=%p, view=%p, el = %p]\n",
442 w->self, this, key, view, el);
443 #endif
445 el->key = key;
446 el->hb = hb;
447 el->view = view;
448 ++nelem;
450 return el;
453 void cilkred_map::rehash(__cilkrts_worker *w)
455 #if REDPAR_DEBUG >= 1
456 fprintf(stderr, "[W=%d, desc=rehash, this_map=%p, g=%p, w->g=%p]\n",
457 w->self, this, g, w->g);
458 verify_current_wkr(w);
459 #endif
460 CILK_ASSERT((w == 0 && g == 0) || w->g == g);
462 size_t onbuckets = nbuckets;
463 size_t onelem = nelem;
464 bucket **obuckets = buckets;
465 size_t i;
466 bucket *b;
468 make_buckets(w, nextsz(nbuckets));
470 for (i = 0; i < onbuckets; ++i) {
471 b = obuckets[i];
472 if (b) {
473 elem *oel;
474 for (oel = b->el; oel->key; ++oel)
475 insert_no_rehash(w, oel->key, oel->hb, oel->view);
479 CILK_ASSERT(nelem == onelem);
481 free_buckets(w, obuckets, onbuckets);
484 elem *cilkred_map::rehash_and_insert(__cilkrts_worker *w,
485 void *key,
486 __cilkrts_hyperobject_base *hb,
487 void *view)
490 #if REDPAR_DEBUG >= 1
491 fprintf(stderr, "W=%d, this_map =%p, inserting key=%p, view=%p\n",
492 w->self, this, key, view);
493 verify_current_wkr(w);
494 #endif
496 if (need_rehash_p())
497 rehash(w);
499 return insert_no_rehash(w, key, hb, view);
503 elem *cilkred_map::lookup(void *key)
505 bucket *b = buckets[hashfun(this, key)];
507 if (b) {
508 elem *el;
509 for (el = b->el; el->key; ++el) {
510 if (el->key == key) {
511 CILK_ASSERT(el->view);
512 return el;
517 return 0;
520 void elem::destroy()
522 if (! is_leftmost()) {
524 // Call destroy_fn and deallocate_fn on the view, but not if it's the
525 // leftmost view.
526 cilk_c_monoid *monoid = &(hb->__c_monoid);
527 cilk_c_reducer_destroy_fn_t destroy_fn = monoid->destroy_fn;
528 cilk_c_reducer_deallocate_fn_t deallocate_fn = monoid->deallocate_fn;
530 destroy_fn((void*)hb, view);
531 deallocate_fn((void*)hb, view);
534 view = 0;
537 inline
538 bool elem::is_leftmost() const
540 // implementation uses the address of the leftmost view as the key, so if
541 // key == view, then this element refers to the leftmost view.
542 return key == view;
545 /* remove the reducer from the current reducer map. If the reducer
546 exists in maps other than the current one, the behavior is
547 undefined. */
548 extern "C"
549 CILK_EXPORT void __CILKRTS_STRAND_STALE(
550 __cilkrts_hyper_destroy(__cilkrts_hyperobject_base *hb))
552 // Disable Cilkscreen for the duration of this call. The destructor for
553 // this class will re-enable Cilkscreen when the method returns. This
554 // will prevent Cilkscreen from reporting apparent races in reducers
555 DisableCilkscreen x;
557 __cilkrts_worker* w = __cilkrts_get_tls_worker();
558 if (! w) {
559 // If no worker, then Cilk is not running and there is no reducer
560 // map. Do nothing. The reducer's destructor will take care of
561 // destroying the leftmost view.
562 return;
565 const char *UNSYNCED_REDUCER_MSG =
566 "Destroying a reducer while it is visible to unsynced child tasks, or\n"
567 "calling CILK_C_UNREGISTER_REDUCER() on an unregistered reducer.\n"
568 "Did you forget a _Cilk_sync or CILK_C_REGISTER_REDUCER()?";
570 cilkred_map* h = w->reducer_map;
571 if (NULL == h)
572 cilkos_error(UNSYNCED_REDUCER_MSG); // Does not return
574 if (h->merging) {
575 verify_current_wkr(w);
576 __cilkrts_bug("User error: hyperobject used by another hyperobject");
579 void* key = get_hyperobject_key(hb);
580 elem *el = h->lookup(key);
582 // Verify that the reducer is being destroyed from the leftmost strand for
583 // which the reducer is defined.
584 if (! (el && el->is_leftmost()))
585 cilkos_error(UNSYNCED_REDUCER_MSG);
587 #if REDPAR_DEBUG >= 3
588 fprintf(stderr, "[W=%d, key=%p, lookup in map %p, found el=%p, about to destroy]\n",
589 w->self, key, h, el);
590 #endif
592 // Remove the element from the hash bucket. Do not bother shrinking
593 // the bucket. Note that the destroy() function does not actually
594 // call the destructor for the leftmost view.
595 el->destroy();
596 do {
597 el[0] = el[1];
598 ++el;
599 } while (el->key);
600 --h->nelem;
602 #if REDPAR_DEBUG >= 2
603 fprintf(stderr, "[W=%d, desc=hyper_destroy_finish, key=%p, w->reducer_map=%p]\n",
604 w->self, key, w->reducer_map);
605 #endif
608 extern "C"
609 CILK_EXPORT
610 void __cilkrts_hyper_create(__cilkrts_hyperobject_base *hb)
612 // This function registers the specified hyperobject in the current
613 // reducer map and registers the initial value of the hyperobject as the
614 // leftmost view of the reducer.
615 __cilkrts_worker *w = __cilkrts_get_tls_worker();
616 if (! w) {
617 // If there is no worker, then there is nothing to do: The iniitial
618 // value will automatically be used as the left-most view when we
619 // enter Cilk.
620 return;
623 // Disable Cilkscreen for the duration of this call. The destructor for
624 // this class will re-enable Cilkscreen when the method returns. This
625 // will prevent Cilkscreen from reporting apparent races in reducers
626 DisableCilkscreen x;
628 void* key = get_hyperobject_key(hb);
629 void* view = get_leftmost_view(key);
630 cilkred_map *h = w->reducer_map;
632 if (__builtin_expect(!h, 0)) {
633 h = install_new_reducer_map(w);
634 #if REDPAR_DEBUG >= 2
635 fprintf(stderr, "[W=%d, hb=%p, hyper_create, isntalled new map %p, view=%p]\n",
636 w->self, hb, h, view);
637 #endif
640 /* Must not exist. */
641 CILK_ASSERT(h->lookup(key) == NULL);
643 #if REDPAR_DEBUG >= 3
644 verify_current_wkr(w);
645 fprintf(stderr, "[W=%d, hb=%p, lookup in map %p of view %p, should be null]\n",
646 w->self, hb, h, view);
647 fprintf(stderr, "W=%d, h=%p, inserting key %p, view%p\n",
648 w->self,
650 &(hb->__c_monoid),
651 view);
652 #endif
654 if (h->merging)
655 __cilkrts_bug("User error: hyperobject used by another hyperobject");
657 CILK_ASSERT(w->reducer_map == h);
658 // The address of the leftmost value is the same as the key for lookup.
659 (void) h->rehash_and_insert(w, view, hb, view);
662 extern "C"
663 CILK_EXPORT void* __CILKRTS_STRAND_PURE(
664 __cilkrts_hyper_lookup(__cilkrts_hyperobject_base *hb))
666 __cilkrts_worker* w = __cilkrts_get_tls_worker_fast();
667 void* key = get_hyperobject_key(hb);
668 if (! w)
669 return get_leftmost_view(key);
671 // Disable Cilkscreen for the duration of this call. This will
672 // prevent Cilkscreen from reporting apparent races in reducers
673 DisableCilkscreen dguard;
675 if (__builtin_expect(w->g->force_reduce, 0))
676 __cilkrts_promote_own_deque(w);
677 cilkred_map* h = w->reducer_map;
679 if (__builtin_expect(!h, 0)) {
680 h = install_new_reducer_map(w);
683 if (h->merging)
684 __cilkrts_bug("User error: hyperobject used by another hyperobject");
685 elem* el = h->lookup(key);
686 if (! el) {
687 /* lookup failed; insert a new default element */
688 void *rep;
691 /* re-enable cilkscreen while calling the constructor */
692 EnableCilkscreen eguard;
693 if (h->is_leftmost)
695 // This special case is called only if the reducer was not
696 // registered using __cilkrts_hyper_create, e.g., if this is a
697 // C reducer in global scope or if there is no bound worker.
698 rep = get_leftmost_view(key);
700 else
702 rep = hb->__c_monoid.allocate_fn((void*)hb,
703 hb->__view_size);
704 // TBD: Handle exception on identity function
705 hb->__c_monoid.identity_fn((void*)hb, rep);
709 #if REDPAR_DEBUG >= 3
710 fprintf(stderr, "W=%d, h=%p, inserting key %p, view%p\n",
711 w->self,
713 &(hb->__c_monoid),
714 rep);
715 CILK_ASSERT(w->reducer_map == h);
716 #endif
717 el = h->rehash_and_insert(w, key, hb, rep);
720 return el->view;
723 extern "C" CILK_EXPORT
724 void* __cilkrts_hyperobject_alloc(void* ignore, std::size_t bytes)
726 return std::malloc(bytes);
729 extern "C" CILK_EXPORT
730 void __cilkrts_hyperobject_dealloc(void* ignore, void* view)
732 std::free(view);
735 /* No-op destroy function */
736 extern "C" CILK_EXPORT
737 void __cilkrts_hyperobject_noop_destroy(void* ignore, void* ignore2)
741 cilkred_map *__cilkrts_make_reducer_map(__cilkrts_worker *w)
743 CILK_ASSERT(w);
745 cilkred_map *h;
746 size_t nbuckets = 1; /* default value */
748 h = (cilkred_map *)__cilkrts_frame_malloc(w, sizeof(*h));
749 #if REDPAR_DEBUG >= 1
750 fprintf(stderr, "[W=%d, desc=make_reducer_frame_malloc_reducer_map, h=%p]\n",
751 w->self, h);
752 #endif
754 h->g = w ? w->g : 0;
755 h->make_buckets(w, nbuckets);
756 h->merging = false;
757 h->is_leftmost = false;
759 return h;
762 /* Destroy a reducer map. The map must have been allocated
763 from the worker's global context and should have been
764 allocated from the same worker. */
765 void __cilkrts_destroy_reducer_map(__cilkrts_worker *w, cilkred_map *h)
767 CILK_ASSERT((w == 0 && h->g == 0) || w->g == h->g);
768 verify_current_wkr(w);
770 /* the reducer map is allowed to contain el->view == NULL here (and
771 only here). We set el->view == NULL only when we know that the
772 map will be destroyed immediately afterwards. */
773 DBG h->check(/*allow_null_view=*/true);
775 bucket *b;
776 size_t i;
778 for (i = 0; i < h->nbuckets; ++i) {
779 b = h->buckets[i];
780 if (b) {
781 elem *el;
782 for (el = b->el; el->key; ++el) {
783 if (el->view)
784 el->destroy();
789 free_buckets(w, h->buckets, h->nbuckets);
791 #if REDPAR_DEBUG >= 1
792 fprintf(stderr, "W=%d, destroy_red_map, freeing map h=%p, size=%zd\n",
793 w->self, h, sizeof(*h));
794 #endif
796 __cilkrts_frame_free(w, h, sizeof(*h));
799 /* Set the specified reducer map as the leftmost map if is_leftmost is true,
800 otherwise, set it to not be the leftmost map. */
801 void __cilkrts_set_leftmost_reducer_map(cilkred_map *h, int is_leftmost)
803 h->is_leftmost = is_leftmost;
807 __cilkrts_worker* cilkred_map::merge(__cilkrts_worker *w,
808 cilkred_map *other_map,
809 enum merge_kind kind)
811 // Disable Cilkscreen while the we merge the maps. The destructor for
812 // the guard class will re-enable Cilkscreen when it goes out of scope.
813 // This will prevent Cilkscreen from reporting apparent races in between
814 // the reduce function and the reducer operations. The Cilk runtime
815 // guarantees that a pair of reducer maps will only be merged when no
816 // other strand will access them.
817 DisableCilkscreen guard;
819 #if REDPAR_DEBUG >= 2
820 fprintf(stderr, "[W=%d, desc=merge, this_map=%p, other_map=%p]\n",
821 w->self,
822 this, other_map);
823 #endif
824 // Remember the current stack frame.
825 __cilkrts_stack_frame *current_sf = w->current_stack_frame;
826 merging = true;
827 other_map->merging = true;
829 // Merging to the leftmost view is a special case because every leftmost
830 // element must be initialized before the merge.
831 CILK_ASSERT(!other_map->is_leftmost /* || kind == MERGE_UNORDERED */);
832 bool merge_to_leftmost = (this->is_leftmost
833 /* && !other_map->is_leftmost */);
835 DBG check(/*allow_null_view=*/false);
836 DBG other_map->check(/*allow_null_view=*/false);
838 for (size_t i = 0; i < other_map->nbuckets; ++i) {
839 bucket *b = other_map->buckets[i];
840 if (b) {
841 for (elem *other_el = b->el; other_el->key; ++other_el) {
842 /* Steal the value from the other map, which will be
843 destroyed at the end of this operation. */
844 void *other_view = other_el->view;
845 CILK_ASSERT(other_view);
847 void *key = other_el->key;
848 __cilkrts_hyperobject_base *hb = other_el->hb;
849 elem *this_el = lookup(key);
851 if (this_el == 0 && merge_to_leftmost) {
852 /* Initialize leftmost view before merging. */
853 void* leftmost = get_leftmost_view(key);
854 // leftmost == other_view can be true if the initial view
855 // was created in other than the leftmost strand of the
856 // spawn tree, but then made visible to subsequent strands
857 // (E.g., the reducer was allocated on the heap and the
858 // pointer was returned to the caller.) In such cases,
859 // parallel semantics says that syncing with earlier
860 // strands will always result in 'this_el' being null,
861 // thus propagating the initial view up the spawn tree
862 // until it reaches the leftmost strand. When synching
863 // with the leftmost strand, leftmost == other_view will be
864 // true and we must avoid reducing the initial view with
865 // itself.
866 if (leftmost != other_view)
867 this_el = rehash_and_insert(w, key, hb, leftmost);
870 if (this_el == 0) {
871 /* move object from other map into this one */
872 rehash_and_insert(w, key, hb, other_view);
873 other_el->view = 0;
874 continue; /* No element-level merge necessary */
877 /* The same key is present in both maps with values
878 A and B. Three choices: fail, A OP B, B OP A. */
879 switch (kind)
881 case MERGE_UNORDERED:
882 __cilkrts_bug("TLS Reducer race");
883 break;
884 case MERGE_INTO_RIGHT:
885 /* Swap elements in order to preserve object
886 identity */
887 other_el->view = this_el->view;
888 this_el->view = other_view;
889 /* FALL THROUGH */
890 case MERGE_INTO_LEFT: {
891 /* Stealing should be disabled during reduce
892 (even if force-reduce is enabled). */
894 #if DISABLE_PARALLEL_REDUCERS
895 __cilkrts_stack_frame * volatile *saved_protected_tail;
896 saved_protected_tail = __cilkrts_disallow_stealing(w, NULL);
897 #endif
900 CILK_ASSERT(current_sf->worker == w);
901 CILK_ASSERT(w->current_stack_frame == current_sf);
903 /* TBD: if reduce throws an exception we need to stop it
904 here. */
905 hb->__c_monoid.reduce_fn((void*)hb,
906 this_el->view,
907 other_el->view);
908 w = current_sf->worker;
910 #if REDPAR_DEBUG >= 2
911 verify_current_wkr(w);
912 CILK_ASSERT(w->current_stack_frame == current_sf);
913 #endif
916 #if DISABLE_PARALLEL_REDUCERS
917 /* Restore stealing */
918 __cilkrts_restore_stealing(w, saved_protected_tail);
919 #endif
921 } break;
926 this->is_leftmost = this->is_leftmost || other_map->is_leftmost;
927 merging = false;
928 other_map->merging = false;
929 verify_current_wkr(w);
930 __cilkrts_destroy_reducer_map(w, other_map);
931 return w;
936 * Print routine for debugging the merging of reducer maps.
937 * A no-op unless REDPAR_DEBUG set high enough.
939 static inline
940 void debug_map_merge(__cilkrts_worker *w,
941 cilkred_map *left_map,
942 cilkred_map *right_map,
943 __cilkrts_worker **final_wkr)
945 #if REDPAR_DEBUG >= 2
946 fprintf(stderr, "[W=%d, desc=finish_merge, left_map=%p, right_map=%p, w->reducer_map=%p, right_ans=%p, final_wkr=%d]\n",
947 w->self, left_map, right_map, w->reducer_map, right_map, (*final_wkr)->self);
948 #endif
953 * merge RIGHT into LEFT;
954 * return whichever map allows for faster merge, and destroy the other one.
956 * *w_ptr should be the currently executing worker.
957 * *w_ptr may change during execution if the reduction is parallel.
959 cilkred_map*
960 merge_reducer_maps(__cilkrts_worker **w_ptr,
961 cilkred_map *left_map,
962 cilkred_map *right_map)
964 __cilkrts_worker *w = *w_ptr;
965 if (!left_map) {
966 debug_map_merge(w, left_map, right_map, w_ptr);
967 return right_map;
970 if (!right_map) {
971 debug_map_merge(w, left_map, right_map, w_ptr);
972 return left_map;
975 /* Special case, if left_map is leftmost, then always merge into it.
976 For C reducers this forces lazy creation of the leftmost views. */
977 if (left_map->is_leftmost || left_map->nelem > right_map->nelem) {
978 *w_ptr = left_map->merge(w, right_map, cilkred_map::MERGE_INTO_LEFT);
979 debug_map_merge(*w_ptr, left_map, right_map, w_ptr);
980 return left_map;
981 } else {
982 *w_ptr = right_map->merge(w, left_map, cilkred_map::MERGE_INTO_RIGHT);
983 debug_map_merge(*w_ptr, left_map, right_map, w_ptr);
984 return right_map;
989 * Merges RIGHT into LEFT, and then repeatedly calls
990 * merge_reducer_maps_helper() until (*w_ptr)->reducer_map is NULL.
992 * *w_ptr may change as reductions execute.
994 cilkred_map*
995 repeated_merge_reducer_maps(__cilkrts_worker **w_ptr,
996 cilkred_map *left_map,
997 cilkred_map *right_map)
999 // Note: if right_map == NULL but w->reducer_map != NULL, then
1000 // this loop will reduce w->reducer_map into left_map.
1001 do {
1002 left_map = merge_reducer_maps(w_ptr, left_map, right_map);
1003 verify_current_wkr(*w_ptr);
1005 // Pull any newly created reducer map and loop around again.
1006 right_map = (*w_ptr)->reducer_map;
1007 (*w_ptr)->reducer_map = NULL;
1008 } while (right_map);
1009 return left_map;
1012 /* End reducer_impl.cpp */