1/*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LLVM_LICENSE_HEADER@
5 */
6
7/*
8 *  import_byref.c
9 *  Examples
10 *
11 *  Created by Blaine Garst on 2/1/08.
12 *  Copyright 2008 __MyCompanyName__. All rights reserved.
13 *
14 */
15
16
17#include "driver.h"
18
19//
20// Example closure code generated for specific Blocks
21//
22
23//
24// Fully bound computation on const imported locals
25//
26
27#if __BLOCKS__
28int import_byref_real(int verbose) {
29    int x = rand();
30    int y = 15;
31    void (^myClosure)(void) = ^ (void) { | y | setGlobalInt(x+y); ++y;};
32    x++;
33    callVoidVoid(myClosure);
34
35    int globalValue = getGlobalInt();
36    int desiredValue = x + y - 2;
37    if (error_found("import byref", globalValue, desiredValue, verbose)) return 1;
38
39#if FULL_CLOSURES
40// not yet
41    void (^myClosureCopy)(void) = Block_copy(myClosure);
42    callVoidVoid(myClosureCopy, NULL);
43
44    globalValue = getGlobalInt();
45    if (error_found("import byref copy", globalValue, desiredValue, verbose)) return 1;
46
47    Block_release(myClosureCopy);
48#endif
49    return 0;
50}
51
52#endif __BLOCKS__
53
54// the closure data structure sythesized for the import_byref
55struct import_byref_shared_struct {
56    struct Block_byref base;
57    int y;
58};
59
60
61struct import_byref_struct {
62  struct Block_basic base;
63  const int x;
64  struct import_byref_shared_struct *shared_struct;
65};
66
67void print_byref_struct(struct import_byref_shared_struct *ibs) {
68   printf("byref structure @ %p:\n", ibs);
69   printf("forwarding: %p\nrefcount: %d\nsize: %d\n", ibs->base.forwarding, ibs->base.flags, ibs->base.size);
70   printf("y: %d\n\n", ibs->y);
71}
72
73// the "thunks" compiled for the invoke entry point of the import_byref
74
75void invoke_import_byref(struct import_byref_struct *aBlock) {
76  // no return value so just a void invoke
77  // the compound statement rewritten to reference locals via the const copies.
78  {
79    setGlobalInt(aBlock->x + aBlock->shared_struct->y);
80    ++aBlock->shared_struct->y;
81   }
82}
83
84// fix up the just copied closure
85void copy_import_byref(struct import_byref_struct *dst, struct import_byref_struct *src) {
86
87   // do closure specific work
88   // the new closure can't reference the stack, so update it's pointer.
89   // _Block_byref_assign_copy will either copy it to the heap or bump the heap refcount
90   //print_byref_struct(aBlock->shared_struct);
91   _Block_byref_assign_copy(&dst->shared_struct, src->shared_struct);
92    //print_byref_struct(aBlock->shared_struct);
93
94}
95
96// the closure 'destructor'
97// Only called when destroying a heap based closure
98void destroy_import_byref(struct import_byref_struct *aBlock) {
99    // do closure specific unwork
100    // lose the heap based closure's reference to the shared struct
101    // The lexical scope will also have this call synthesized
102    Block_release_byref(aBlock->shared_struct);
103}
104
105// The rewritten version of the code above
106
107int import_byref(int verbose) {
108    int x = rand();
109    // XXX move this into support routine
110    struct import_byref_shared_struct shared_struct;
111    shared_struct.base.flags = 0;//BLOCK_HAS_COPY_DISPOSE;
112    shared_struct.base.forwarding = &shared_struct;
113    shared_struct.base.size = sizeof(struct import_byref_shared_struct);
114
115    // this byref does not contain
116    // 1) another closure
117    // 2) an id
118    // 3) a C++ stack object
119    // and so, there is nothing for it to do on preserve/destroy
120    shared_struct.base.byref_destroy = NULL;
121    shared_struct.base.byref_keep = NULL;
122
123    shared_struct.y = 15;
124    struct import_byref_struct myClosure = {
125        { 0, BLOCK_HAS_COPY_DISPOSE, sizeof(struct import_byref_struct),
126            (void (*)(void *))invoke_import_byref,
127            (void (*)(void *, void *))copy_import_byref,
128            (void (*)(void *))destroy_import_byref
129        },
130        x, // capture x
131        &shared_struct // capture y
132    };
133    x++;
134    callVoidVoid(&myClosure.base);
135
136    int globalValue = getGlobalInt();
137    int desiredValue = x + ((struct import_byref_shared_struct *)(shared_struct.base.forwarding))->y - 2;
138    if (error_found("import byref", globalValue, desiredValue, verbose)) return 1;
139
140    struct import_byref_struct *myClosureCopy = Block_copy(&myClosure);
141    callVoidVoid(&myClosureCopy->base);
142
143    globalValue = getGlobalInt();
144    ++desiredValue;  // y was incr'ed after first invocation
145    if (error_found("import byref copy", globalValue, desiredValue, verbose)) return 1;
146
147    Block_release(myClosureCopy);
148
149    // the following is synthesized sometime after the last use of all shared variables
150    // the first time any closure that uses shared_struct is copied, an extra refcount on that new
151    // copy is provided for this stack frame's use.  This stack frame needs to relinquish that code.
152
153    // XXX if shared_struct.base.forwarding != &shared_struct) ...
154    Block_release_byref(shared_struct.base.forwarding);
155
156    return 0;
157}
158