xref: /freebsd/contrib/llvm-project/compiler-rt/lib/BlocksRuntime/runtime.c (revision 0b57cec536236d46e3dba9bd041533462f33dbb7)
1 /*
2  * runtime.c
3  *
4  * Copyright 2008-2010 Apple, Inc. Permission is hereby granted, free of charge,
5  * to any person obtaining a copy of this software and associated documentation
6  * files (the "Software"), to deal in the Software without restriction,
7  * including without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to permit
9  * persons to whom the Software is furnished to do so, subject to the following
10  * conditions:
11  *
12  * The above copyright notice and this permission notice shall be included in
13  * all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21  * SOFTWARE.
22  *
23  */
24 
25 #include "Block_private.h"
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <stdint.h>
30 
31 #include "config.h"
32 
33 #ifdef HAVE_AVAILABILITY_MACROS_H
34 #include <AvailabilityMacros.h>
35 #endif /* HAVE_AVAILABILITY_MACROS_H */
36 
37 #ifdef HAVE_TARGET_CONDITIONALS_H
38 #include <TargetConditionals.h>
39 #endif /* HAVE_TARGET_CONDITIONALS_H */
40 
41 #if defined(HAVE_OSATOMIC_COMPARE_AND_SWAP_INT) && defined(HAVE_OSATOMIC_COMPARE_AND_SWAP_LONG)
42 
43 #ifdef HAVE_LIBKERN_OSATOMIC_H
44 #include <libkern/OSAtomic.h>
45 #endif /* HAVE_LIBKERN_OSATOMIC_H */
46 
47 #elif defined(__WIN32__) || defined(_WIN32)
48 #define _CRT_SECURE_NO_WARNINGS 1
49 #include <windows.h>
50 
OSAtomicCompareAndSwapLong(long oldl,long newl,long volatile * dst)51 static __inline bool OSAtomicCompareAndSwapLong(long oldl, long newl, long volatile *dst) {
52     /* fixme barrier is overkill -- see objc-os.h */
53     long original = InterlockedCompareExchange(dst, newl, oldl);
54     return (original == oldl);
55 }
56 
OSAtomicCompareAndSwapInt(int oldi,int newi,int volatile * dst)57 static __inline bool OSAtomicCompareAndSwapInt(int oldi, int newi, int volatile *dst) {
58     /* fixme barrier is overkill -- see objc-os.h */
59     int original = InterlockedCompareExchange(dst, newi, oldi);
60     return (original == oldi);
61 }
62 
63 /*
64  * Check to see if the GCC atomic built-ins are available.  If we're on
65  * a 64-bit system, make sure we have an 8-byte atomic function
66  * available.
67  *
68  */
69 
70 #elif defined(HAVE_SYNC_BOOL_COMPARE_AND_SWAP_INT) && defined(HAVE_SYNC_BOOL_COMPARE_AND_SWAP_LONG)
71 
OSAtomicCompareAndSwapLong(long oldl,long newl,long volatile * dst)72 static __inline bool OSAtomicCompareAndSwapLong(long oldl, long newl, long volatile *dst) {
73   return __sync_bool_compare_and_swap(dst, oldl, newl);
74 }
75 
OSAtomicCompareAndSwapInt(int oldi,int newi,int volatile * dst)76 static __inline bool OSAtomicCompareAndSwapInt(int oldi, int newi, int volatile *dst) {
77   return __sync_bool_compare_and_swap(dst, oldi, newi);
78 }
79 
80 #else
81 #error unknown atomic compare-and-swap primitive
82 #endif /* HAVE_OSATOMIC_COMPARE_AND_SWAP_INT && HAVE_OSATOMIC_COMPARE_AND_SWAP_LONG */
83 
84 
85 /*
86  * Globals:
87  */
88 
89 static void *_Block_copy_class = _NSConcreteMallocBlock;
90 static void *_Block_copy_finalizing_class = _NSConcreteMallocBlock;
91 static int _Block_copy_flag = BLOCK_NEEDS_FREE;
92 static int _Byref_flag_initial_value = BLOCK_NEEDS_FREE | 2;
93 
94 static const int WANTS_ONE = (1 << 16);
95 
96 static bool isGC = false;
97 
98 /*
99  * Internal Utilities:
100  */
101 
102 #if 0
103 static unsigned long int latching_incr_long(unsigned long int *where) {
104     while (1) {
105         unsigned long int old_value = *(volatile unsigned long int *)where;
106         if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
107             return BLOCK_REFCOUNT_MASK;
108         }
109         if (OSAtomicCompareAndSwapLong(old_value, old_value+1, (volatile long int *)where)) {
110             return old_value+1;
111         }
112     }
113 }
114 #endif /* if 0 */
115 
latching_incr_int(int * where)116 static int latching_incr_int(int *where) {
117     while (1) {
118         int old_value = *(volatile int *)where;
119         if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
120             return BLOCK_REFCOUNT_MASK;
121         }
122         if (OSAtomicCompareAndSwapInt(old_value, old_value+1, (volatile int *)where)) {
123             return old_value+1;
124         }
125     }
126 }
127 
128 #if 0
129 static int latching_decr_long(unsigned long int *where) {
130     while (1) {
131         unsigned long int old_value = *(volatile int *)where;
132         if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
133             return BLOCK_REFCOUNT_MASK;
134         }
135         if ((old_value & BLOCK_REFCOUNT_MASK) == 0) {
136             return 0;
137         }
138         if (OSAtomicCompareAndSwapLong(old_value, old_value-1, (volatile long int *)where)) {
139             return old_value-1;
140         }
141     }
142 }
143 #endif /* if 0 */
144 
latching_decr_int(int * where)145 static int latching_decr_int(int *where) {
146     while (1) {
147         int old_value = *(volatile int *)where;
148         if ((old_value & BLOCK_REFCOUNT_MASK) == BLOCK_REFCOUNT_MASK) {
149             return BLOCK_REFCOUNT_MASK;
150         }
151         if ((old_value & BLOCK_REFCOUNT_MASK) == 0) {
152             return 0;
153         }
154         if (OSAtomicCompareAndSwapInt(old_value, old_value-1, (volatile int *)where)) {
155             return old_value-1;
156         }
157     }
158 }
159 
160 
161 /*
162  * GC support stub routines:
163  */
164 #if 0
165 #pragma mark GC Support Routines
166 #endif /* if 0 */
167 
168 
_Block_alloc_default(const unsigned long size,const bool initialCountIsOne,const bool isObject)169 static void *_Block_alloc_default(const unsigned long size, const bool initialCountIsOne, const bool isObject) {
170     return malloc(size);
171 }
172 
_Block_assign_default(void * value,void ** destptr)173 static void _Block_assign_default(void *value, void **destptr) {
174     *destptr = value;
175 }
176 
_Block_setHasRefcount_default(const void * ptr,const bool hasRefcount)177 static void _Block_setHasRefcount_default(const void *ptr, const bool hasRefcount) {
178 }
179 
_Block_do_nothing(const void * aBlock)180 static void _Block_do_nothing(const void *aBlock) { }
181 
_Block_retain_object_default(const void * ptr)182 static void _Block_retain_object_default(const void *ptr) {
183     if (!ptr) return;
184 }
185 
_Block_release_object_default(const void * ptr)186 static void _Block_release_object_default(const void *ptr) {
187     if (!ptr) return;
188 }
189 
_Block_assign_weak_default(const void * ptr,void * dest)190 static void _Block_assign_weak_default(const void *ptr, void *dest) {
191     *(void **)dest = (void *)ptr;
192 }
193 
_Block_memmove_default(void * dst,void * src,unsigned long size)194 static void _Block_memmove_default(void *dst, void *src, unsigned long size) {
195     memmove(dst, src, (size_t)size);
196 }
197 
_Block_memmove_gc_broken(void * dest,void * src,unsigned long size)198 static void _Block_memmove_gc_broken(void *dest, void *src, unsigned long size) {
199     void **destp = (void **)dest;
200     void **srcp = (void **)src;
201     while (size) {
202         _Block_assign_default(*srcp, destp);
203         destp++;
204         srcp++;
205         size -= sizeof(void *);
206     }
207 }
208 
209 /*
210  * GC support callout functions - initially set to stub routines:
211  */
212 
213 static void *(*_Block_allocator)(const unsigned long, const bool isOne, const bool isObject) = _Block_alloc_default;
214 static void (*_Block_deallocator)(const void *) = (void (*)(const void *))free;
215 static void (*_Block_assign)(void *value, void **destptr) = _Block_assign_default;
216 static void (*_Block_setHasRefcount)(const void *ptr, const bool hasRefcount) = _Block_setHasRefcount_default;
217 static void (*_Block_retain_object)(const void *ptr) = _Block_retain_object_default;
218 static void (*_Block_release_object)(const void *ptr) = _Block_release_object_default;
219 static void (*_Block_assign_weak)(const void *dest, void *ptr) = _Block_assign_weak_default;
220 static void (*_Block_memmove)(void *dest, void *src, unsigned long size) = _Block_memmove_default;
221 
222 
223 /*
224  * GC support SPI functions - called from ObjC runtime and CoreFoundation:
225  */
226 
227 /* Public SPI
228  * Called from objc-auto to turn on GC.
229  * version 3, 4 arg, but changed 1st arg
230  */
_Block_use_GC(void * (* alloc)(const unsigned long,const bool isOne,const bool isObject),void (* setHasRefcount)(const void *,const bool),void (* gc_assign)(void *,void **),void (* gc_assign_weak)(const void *,void *),void (* gc_memmove)(void *,void *,unsigned long))231 void _Block_use_GC( void *(*alloc)(const unsigned long, const bool isOne, const bool isObject),
232                     void (*setHasRefcount)(const void *, const bool),
233                     void (*gc_assign)(void *, void **),
234                     void (*gc_assign_weak)(const void *, void *),
235                     void (*gc_memmove)(void *, void *, unsigned long)) {
236 
237     isGC = true;
238     _Block_allocator = alloc;
239     _Block_deallocator = _Block_do_nothing;
240     _Block_assign = gc_assign;
241     _Block_copy_flag = BLOCK_IS_GC;
242     _Block_copy_class = _NSConcreteAutoBlock;
243     /* blocks with ctors & dtors need to have the dtor run from a class with a finalizer */
244     _Block_copy_finalizing_class = _NSConcreteFinalizingBlock;
245     _Block_setHasRefcount = setHasRefcount;
246     _Byref_flag_initial_value = BLOCK_IS_GC;   // no refcount
247     _Block_retain_object = _Block_do_nothing;
248     _Block_release_object = _Block_do_nothing;
249     _Block_assign_weak = gc_assign_weak;
250     _Block_memmove = gc_memmove;
251 }
252 
253 /* transitional */
_Block_use_GC5(void * (* alloc)(const unsigned long,const bool isOne,const bool isObject),void (* setHasRefcount)(const void *,const bool),void (* gc_assign)(void *,void **),void (* gc_assign_weak)(const void *,void *))254 void _Block_use_GC5( void *(*alloc)(const unsigned long, const bool isOne, const bool isObject),
255                     void (*setHasRefcount)(const void *, const bool),
256                     void (*gc_assign)(void *, void **),
257                     void (*gc_assign_weak)(const void *, void *)) {
258     /* until objc calls _Block_use_GC it will call us; supply a broken internal memmove implementation until then */
259     _Block_use_GC(alloc, setHasRefcount, gc_assign, gc_assign_weak, _Block_memmove_gc_broken);
260 }
261 
262 
263 /*
264  * Called from objc-auto to alternatively turn on retain/release.
265  * Prior to this the only "object" support we can provide is for those
266  * super special objects that live in libSystem, namely dispatch queues.
267  * Blocks and Block_byrefs have their own special entry points.
268  *
269  */
_Block_use_RR(void (* retain)(const void *),void (* release)(const void *))270 void _Block_use_RR( void (*retain)(const void *),
271                     void (*release)(const void *)) {
272     _Block_retain_object = retain;
273     _Block_release_object = release;
274 }
275 
276 /*
277  * Internal Support routines for copying:
278  */
279 
280 #if 0
281 #pragma mark Copy/Release support
282 #endif /* if 0 */
283 
284 /* Copy, or bump refcount, of a block.  If really copying, call the copy helper if present. */
_Block_copy_internal(const void * arg,const int flags)285 static void *_Block_copy_internal(const void *arg, const int flags) {
286     struct Block_layout *aBlock;
287     const bool wantsOne = (WANTS_ONE & flags) == WANTS_ONE;
288 
289     //printf("_Block_copy_internal(%p, %x)\n", arg, flags);
290     if (!arg) return NULL;
291 
292 
293     // The following would be better done as a switch statement
294     aBlock = (struct Block_layout *)arg;
295     if (aBlock->flags & BLOCK_NEEDS_FREE) {
296         // latches on high
297         latching_incr_int(&aBlock->flags);
298         return aBlock;
299     }
300     else if (aBlock->flags & BLOCK_IS_GC) {
301         // GC refcounting is expensive so do most refcounting here.
302         if (wantsOne && ((latching_incr_int(&aBlock->flags) & BLOCK_REFCOUNT_MASK) == 1)) {
303             // Tell collector to hang on this - it will bump the GC refcount version
304             _Block_setHasRefcount(aBlock, true);
305         }
306         return aBlock;
307     }
308     else if (aBlock->flags & BLOCK_IS_GLOBAL) {
309         return aBlock;
310     }
311 
312     // Its a stack block.  Make a copy.
313     if (!isGC) {
314         struct Block_layout *result = malloc(aBlock->descriptor->size);
315         if (!result) return (void *)0;
316         memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
317         // reset refcount
318         result->flags &= ~(BLOCK_REFCOUNT_MASK);    // XXX not needed
319         result->flags |= BLOCK_NEEDS_FREE | 1;
320         result->isa = _NSConcreteMallocBlock;
321         if (result->flags & BLOCK_HAS_COPY_DISPOSE) {
322             //printf("calling block copy helper %p(%p, %p)...\n", aBlock->descriptor->copy, result, aBlock);
323             (*aBlock->descriptor->copy)(result, aBlock); // do fixup
324         }
325         return result;
326     }
327     else {
328         // Under GC want allocation with refcount 1 so we ask for "true" if wantsOne
329         // This allows the copy helper routines to make non-refcounted block copies under GC
330         unsigned long int flags = aBlock->flags;
331         bool hasCTOR = (flags & BLOCK_HAS_CTOR) != 0;
332         struct Block_layout *result = _Block_allocator(aBlock->descriptor->size, wantsOne, hasCTOR);
333         if (!result) return (void *)0;
334         memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
335         // reset refcount
336         // if we copy a malloc block to a GC block then we need to clear NEEDS_FREE.
337         flags &= ~(BLOCK_NEEDS_FREE|BLOCK_REFCOUNT_MASK);   // XXX not needed
338         if (wantsOne)
339             flags |= BLOCK_IS_GC | 1;
340         else
341             flags |= BLOCK_IS_GC;
342         result->flags = flags;
343         if (flags & BLOCK_HAS_COPY_DISPOSE) {
344             //printf("calling block copy helper...\n");
345             (*aBlock->descriptor->copy)(result, aBlock); // do fixup
346         }
347         if (hasCTOR) {
348             result->isa = _NSConcreteFinalizingBlock;
349         }
350         else {
351             result->isa = _NSConcreteAutoBlock;
352         }
353         return result;
354     }
355 }
356 
357 
358 /*
359  * Runtime entry points for maintaining the sharing knowledge of byref data blocks.
360  *
361  * A closure has been copied and its fixup routine is asking us to fix up the reference to the shared byref data
362  * Closures that aren't copied must still work, so everyone always accesses variables after dereferencing the forwarding ptr.
363  * We ask if the byref pointer that we know about has already been copied to the heap, and if so, increment it.
364  * Otherwise we need to copy it and update the stack forwarding pointer
365  * XXX We need to account for weak/nonretained read-write barriers.
366  */
367 
_Block_byref_assign_copy(void * dest,const void * arg,const int flags)368 static void _Block_byref_assign_copy(void *dest, const void *arg, const int flags) {
369     struct Block_byref **destp = (struct Block_byref **)dest;
370     struct Block_byref *src = (struct Block_byref *)arg;
371 
372     //printf("_Block_byref_assign_copy called, byref destp %p, src %p, flags %x\n", destp, src, flags);
373     //printf("src dump: %s\n", _Block_byref_dump(src));
374     if (src->forwarding->flags & BLOCK_IS_GC) {
375         ;   // don't need to do any more work
376     }
377     else if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
378         //printf("making copy\n");
379         // src points to stack
380         bool isWeak = ((flags & (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK)) == (BLOCK_FIELD_IS_BYREF|BLOCK_FIELD_IS_WEAK));
381         // if its weak ask for an object (only matters under GC)
382         struct Block_byref *copy = (struct Block_byref *)_Block_allocator(src->size, false, isWeak);
383         copy->flags = src->flags | _Byref_flag_initial_value; // non-GC one for caller, one for stack
384         copy->forwarding = copy; // patch heap copy to point to itself (skip write-barrier)
385         src->forwarding = copy;  // patch stack to point to heap copy
386         copy->size = src->size;
387         if (isWeak) {
388             copy->isa = &_NSConcreteWeakBlockVariable;  // mark isa field so it gets weak scanning
389         }
390         if (src->flags & BLOCK_HAS_COPY_DISPOSE) {
391             // Trust copy helper to copy everything of interest
392             // If more than one field shows up in a byref block this is wrong XXX
393             copy->byref_keep = src->byref_keep;
394             copy->byref_destroy = src->byref_destroy;
395             (*src->byref_keep)(copy, src);
396         }
397         else {
398             // just bits.  Blast 'em using _Block_memmove in case they're __strong
399             _Block_memmove(
400                 (void *)&copy->byref_keep,
401                 (void *)&src->byref_keep,
402                 src->size - sizeof(struct Block_byref_header));
403         }
404     }
405     // already copied to heap
406     else if ((src->forwarding->flags & BLOCK_NEEDS_FREE) == BLOCK_NEEDS_FREE) {
407         latching_incr_int(&src->forwarding->flags);
408     }
409     // assign byref data block pointer into new Block
410     _Block_assign(src->forwarding, (void **)destp);
411 }
412 
413 // Old compiler SPI
_Block_byref_release(const void * arg)414 static void _Block_byref_release(const void *arg) {
415     struct Block_byref *shared_struct = (struct Block_byref *)arg;
416     int refcount;
417 
418     // dereference the forwarding pointer since the compiler isn't doing this anymore (ever?)
419     shared_struct = shared_struct->forwarding;
420 
421     //printf("_Block_byref_release %p called, flags are %x\n", shared_struct, shared_struct->flags);
422     // To support C++ destructors under GC we arrange for there to be a finalizer for this
423     // by using an isa that directs the code to a finalizer that calls the byref_destroy method.
424     if ((shared_struct->flags & BLOCK_NEEDS_FREE) == 0) {
425         return; // stack or GC or global
426     }
427     refcount = shared_struct->flags & BLOCK_REFCOUNT_MASK;
428     if (refcount <= 0) {
429         printf("_Block_byref_release: Block byref data structure at %p underflowed\n", arg);
430     }
431     else if ((latching_decr_int(&shared_struct->flags) & BLOCK_REFCOUNT_MASK) == 0) {
432         //printf("disposing of heap based byref block\n");
433         if (shared_struct->flags & BLOCK_HAS_COPY_DISPOSE) {
434             //printf("calling out to helper\n");
435             (*shared_struct->byref_destroy)(shared_struct);
436         }
437         _Block_deallocator((struct Block_layout *)shared_struct);
438     }
439 }
440 
441 
442 /*
443  *
444  * API supporting SPI
445  * _Block_copy, _Block_release, and (old) _Block_destroy
446  *
447  */
448 
449 #if 0
450 #pragma mark SPI/API
451 #endif /* if 0 */
452 
_Block_copy(const void * arg)453 void *_Block_copy(const void *arg) {
454     return _Block_copy_internal(arg, WANTS_ONE);
455 }
456 
457 
458 // API entry point to release a copied Block
_Block_release(void * arg)459 void _Block_release(void *arg) {
460     struct Block_layout *aBlock = (struct Block_layout *)arg;
461     int32_t newCount;
462     if (!aBlock) return;
463     newCount = latching_decr_int(&aBlock->flags) & BLOCK_REFCOUNT_MASK;
464     if (newCount > 0) return;
465     // Hit zero
466     if (aBlock->flags & BLOCK_IS_GC) {
467         // Tell GC we no longer have our own refcounts.  GC will decr its refcount
468         // and unless someone has done a CFRetain or marked it uncollectable it will
469         // now be subject to GC reclamation.
470         _Block_setHasRefcount(aBlock, false);
471     }
472     else if (aBlock->flags & BLOCK_NEEDS_FREE) {
473         if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)(*aBlock->descriptor->dispose)(aBlock);
474         _Block_deallocator(aBlock);
475     }
476     else if (aBlock->flags & BLOCK_IS_GLOBAL) {
477         ;
478     }
479     else {
480         printf("Block_release called upon a stack Block: %p, ignored\n", (void *)aBlock);
481     }
482 }
483 
484 
485 
486 // Old Compiler SPI point to release a copied Block used by the compiler in dispose helpers
_Block_destroy(const void * arg)487 static void _Block_destroy(const void *arg) {
488     struct Block_layout *aBlock;
489     if (!arg) return;
490     aBlock = (struct Block_layout *)arg;
491     if (aBlock->flags & BLOCK_IS_GC) {
492         // assert(aBlock->Block_flags & BLOCK_HAS_CTOR);
493         return; // ignore, we are being called because of a DTOR
494     }
495     _Block_release(aBlock);
496 }
497 
498 
499 
500 /*
501  *
502  * SPI used by other layers
503  *
504  */
505 
506 // SPI, also internal.  Called from NSAutoBlock only under GC
_Block_copy_collectable(const void * aBlock)507 void *_Block_copy_collectable(const void *aBlock) {
508     return _Block_copy_internal(aBlock, 0);
509 }
510 
511 
512 // SPI
Block_size(void * arg)513 unsigned long int Block_size(void *arg) {
514     return ((struct Block_layout *)arg)->descriptor->size;
515 }
516 
517 
518 #if 0
519 #pragma mark Compiler SPI entry points
520 #endif /* if 0 */
521 
522 
523 /*******************************************************
524 
525 Entry points used by the compiler - the real API!
526 
527 
528 A Block can reference four different kinds of things that require help when the Block is copied to the heap.
529 1) C++ stack based objects
530 2) References to Objective-C objects
531 3) Other Blocks
532 4) __block variables
533 
534 In these cases helper functions are synthesized by the compiler for use in Block_copy and Block_release, called the copy and dispose helpers.  The copy helper emits a call to the C++ const copy constructor for C++ stack based objects and for the rest calls into the runtime support function _Block_object_assign.  The dispose helper has a call to the C++ destructor for case 1 and a call into _Block_object_dispose for the rest.
535 
536 The flags parameter of _Block_object_assign and _Block_object_dispose is set to
537 	* BLOCK_FIELD_IS_OBJECT (3), for the case of an Objective-C Object,
538 	* BLOCK_FIELD_IS_BLOCK (7), for the case of another Block, and
539 	* BLOCK_FIELD_IS_BYREF (8), for the case of a __block variable.
540 If the __block variable is marked weak the compiler also or's in BLOCK_FIELD_IS_WEAK (16).
541 
542 So the Block copy/dispose helpers should only ever generate the four flag values of 3, 7, 8, and 24.
543 
544 When  a __block variable is either a C++ object, an Objective-C object, or another Block then the compiler also generates copy/dispose helper functions.  Similarly to the Block copy helper, the "__block" copy helper (formerly and still a.k.a. "byref" copy helper) will do a C++ copy constructor (not a const one though!) and the dispose helper will do the destructor.  And similarly the helpers will call into the same two support functions with the same values for objects and Blocks with the additional BLOCK_BYREF_CALLER (128) bit of information supplied.
545 
546 So the __block copy/dispose helpers will generate flag values of 3 or 7 for objects and Blocks respectively, with BLOCK_FIELD_IS_WEAK (16) or'ed as appropriate and always 128 or'd in, for the following set of possibilities:
547 	__block id                   128+3
548         __weak block id              128+3+16
549 	__block (^Block)             128+7
550 	__weak __block (^Block)      128+7+16
551 
552 The implementation of the two routines would be improved by switch statements enumerating the eight cases.
553 
554 ********************************************************/
555 
556 /*
557  * When Blocks or Block_byrefs hold objects then their copy routine helpers use this entry point
558  * to do the assignment.
559  */
_Block_object_assign(void * destAddr,const void * object,const int flags)560 void _Block_object_assign(void *destAddr, const void *object, const int flags) {
561     //printf("_Block_object_assign(*%p, %p, %x)\n", destAddr, object, flags);
562     if ((flags & BLOCK_BYREF_CALLER) == BLOCK_BYREF_CALLER) {
563         if ((flags & BLOCK_FIELD_IS_WEAK) == BLOCK_FIELD_IS_WEAK) {
564             _Block_assign_weak(object, destAddr);
565         }
566         else {
567             // do *not* retain or *copy* __block variables whatever they are
568             _Block_assign((void *)object, destAddr);
569         }
570     }
571     else if ((flags & BLOCK_FIELD_IS_BYREF) == BLOCK_FIELD_IS_BYREF)  {
572         // copying a __block reference from the stack Block to the heap
573         // flags will indicate if it holds a __weak reference and needs a special isa
574         _Block_byref_assign_copy(destAddr, object, flags);
575     }
576     // (this test must be before next one)
577     else if ((flags & BLOCK_FIELD_IS_BLOCK) == BLOCK_FIELD_IS_BLOCK) {
578         // copying a Block declared variable from the stack Block to the heap
579         _Block_assign(_Block_copy_internal(object, flags), destAddr);
580     }
581     // (this test must be after previous one)
582     else if ((flags & BLOCK_FIELD_IS_OBJECT) == BLOCK_FIELD_IS_OBJECT) {
583         //printf("retaining object at %p\n", object);
584         _Block_retain_object(object);
585         //printf("done retaining object at %p\n", object);
586         _Block_assign((void *)object, destAddr);
587     }
588 }
589 
590 // When Blocks or Block_byrefs hold objects their destroy helper routines call this entry point
591 // to help dispose of the contents
592 // Used initially only for __attribute__((NSObject)) marked pointers.
_Block_object_dispose(const void * object,const int flags)593 void _Block_object_dispose(const void *object, const int flags) {
594     //printf("_Block_object_dispose(%p, %x)\n", object, flags);
595     if (flags & BLOCK_FIELD_IS_BYREF)  {
596         // get rid of the __block data structure held in a Block
597         _Block_byref_release(object);
598     }
599     else if ((flags & (BLOCK_FIELD_IS_BLOCK|BLOCK_BYREF_CALLER)) == BLOCK_FIELD_IS_BLOCK) {
600         // get rid of a referenced Block held by this Block
601         // (ignore __block Block variables, compiler doesn't need to call us)
602         _Block_destroy(object);
603     }
604     else if ((flags & (BLOCK_FIELD_IS_WEAK|BLOCK_FIELD_IS_BLOCK|BLOCK_BYREF_CALLER)) == BLOCK_FIELD_IS_OBJECT) {
605         // get rid of a referenced object held by this Block
606         // (ignore __block object variables, compiler doesn't need to call us)
607         _Block_release_object(object);
608     }
609 }
610 
611 
612 /*
613  * Debugging support:
614  */
615 #if 0
616 #pragma mark Debugging
617 #endif /* if 0 */
618 
619 
_Block_dump(const void * block)620 const char *_Block_dump(const void *block) {
621     struct Block_layout *closure = (struct Block_layout *)block;
622     static char buffer[512];
623     char *cp = buffer;
624     if (closure == NULL) {
625         sprintf(cp, "NULL passed to _Block_dump\n");
626         return buffer;
627     }
628     if (! (closure->flags & BLOCK_HAS_DESCRIPTOR)) {
629         printf("Block compiled by obsolete compiler, please recompile source for this Block\n");
630         exit(1);
631     }
632     cp += sprintf(cp, "^%p (new layout) =\n", (void *)closure);
633     if (closure->isa == NULL) {
634         cp += sprintf(cp, "isa: NULL\n");
635     }
636     else if (closure->isa == _NSConcreteStackBlock) {
637         cp += sprintf(cp, "isa: stack Block\n");
638     }
639     else if (closure->isa == _NSConcreteMallocBlock) {
640         cp += sprintf(cp, "isa: malloc heap Block\n");
641     }
642     else if (closure->isa == _NSConcreteAutoBlock) {
643         cp += sprintf(cp, "isa: GC heap Block\n");
644     }
645     else if (closure->isa == _NSConcreteGlobalBlock) {
646         cp += sprintf(cp, "isa: global Block\n");
647     }
648     else if (closure->isa == _NSConcreteFinalizingBlock) {
649         cp += sprintf(cp, "isa: finalizing Block\n");
650     }
651     else {
652         cp += sprintf(cp, "isa?: %p\n", (void *)closure->isa);
653     }
654     cp += sprintf(cp, "flags:");
655     if (closure->flags & BLOCK_HAS_DESCRIPTOR) {
656         cp += sprintf(cp, " HASDESCRIPTOR");
657     }
658     if (closure->flags & BLOCK_NEEDS_FREE) {
659         cp += sprintf(cp, " FREEME");
660     }
661     if (closure->flags & BLOCK_IS_GC) {
662         cp += sprintf(cp, " ISGC");
663     }
664     if (closure->flags & BLOCK_HAS_COPY_DISPOSE) {
665         cp += sprintf(cp, " HASHELP");
666     }
667     if (closure->flags & BLOCK_HAS_CTOR) {
668         cp += sprintf(cp, " HASCTOR");
669     }
670     cp += sprintf(cp, "\nrefcount: %u\n", closure->flags & BLOCK_REFCOUNT_MASK);
671     cp += sprintf(cp, "invoke: %p\n", (void *)(uintptr_t)closure->invoke);
672     {
673         struct Block_descriptor *dp = closure->descriptor;
674         cp += sprintf(cp, "descriptor: %p\n", (void *)dp);
675         cp += sprintf(cp, "descriptor->reserved: %lu\n", dp->reserved);
676         cp += sprintf(cp, "descriptor->size: %lu\n", dp->size);
677 
678         if (closure->flags & BLOCK_HAS_COPY_DISPOSE) {
679             cp += sprintf(cp, "descriptor->copy helper: %p\n", (void *)(uintptr_t)dp->copy);
680             cp += sprintf(cp, "descriptor->dispose helper: %p\n", (void *)(uintptr_t)dp->dispose);
681         }
682     }
683     return buffer;
684 }
685 
686 
_Block_byref_dump(struct Block_byref * src)687 const char *_Block_byref_dump(struct Block_byref *src) {
688     static char buffer[256];
689     char *cp = buffer;
690     cp += sprintf(cp, "byref data block %p contents:\n", (void *)src);
691     cp += sprintf(cp, "  forwarding: %p\n", (void *)src->forwarding);
692     cp += sprintf(cp, "  flags: 0x%x\n", src->flags);
693     cp += sprintf(cp, "  size: %d\n", src->size);
694     if (src->flags & BLOCK_HAS_COPY_DISPOSE) {
695         cp += sprintf(cp, "  copy helper: %p\n", (void *)(uintptr_t)src->byref_keep);
696         cp += sprintf(cp, "  dispose helper: %p\n", (void *)(uintptr_t)src->byref_destroy);
697     }
698     return buffer;
699 }
700 
701