summaryrefslogtreecommitdiff
path: root/ghc/runtime/gum/Unpack.lc
blob: 96a7d622bc28e4c489919363e5c741a0d1666470 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
%
% (c) Parade/AQUA Projects, Glasgow University, 1995
%     Kevin Hammond, February 15th. 1995
%
%     This is for GUM only.
%
%************************************************************************
%*                                                                      *
\section[Unpack.lc]{Unpacking closures which have been exported to remote processors}
%*									*
%************************************************************************

This module defines routines for unpacking closures in the parallel runtime
system (GUM).

\begin{code}
#ifdef PAR /* whole file */

#include "rtsdefs.h"
EXTDATA_RO(FetchMe_info);
\end{code}

Local Definitions.

\begin{code}
static globalAddr PendingGABuffer[(PACK_BUFFER_SIZE-PACK_HDR_SIZE)*2];
\end{code}

@CommonUp@ commons up two closures which we have discovered to be
variants of the same object.  One is made an indirection to the other.

\begin{code}
void
CommonUp(src, dst)
P_ src;
P_ dst;
{
    P_ bqe;

    ASSERT(src != dst);
    switch (INFO_TYPE(INFO_PTR(src))) {
    case INFO_SPEC_RBH_TYPE:
	bqe = (P_) SPEC_RBH_BQ(src);
	break;
    case INFO_GEN_RBH_TYPE:
	bqe = (P_) GEN_RBH_BQ(src);
	break;
    case INFO_FETCHME_TYPE:
	bqe = Nil_closure;
	break;
    case INFO_FMBQ_TYPE:
	bqe = (P_) FMBQ_ENTRIES(src);
	break;
    default:
	/* Don't common up anything else */
	return;

    }
    /* Note that UPD_IND does *not* awaken the bq */
    UPD_IND(src, dst);
    ASSERT(!IS_BIG_MOTHER(INFO_PTR(dst)));
    if (IS_MUTABLE(INFO_PTR(bqe)))
	AwakenBlockingQueue(bqe);
}

\end{code}

@UnpackGraph@ unpacks the graph contained in a message buffer.  It
returns a pointer to the new graph.  The @gamap@ parameter is set to
point to an array of (oldGA,newGA) pairs which were created as a
result of unpacking the buffer; @nGAs@ is set to the number of GA
pairs which were created.

The format of graph in the pack buffer is as defined in @Pack.lc@.

\begin{code}
P_
UnpackGraph(buffer, gamap, nGAs)
P_ buffer;
globalAddr **gamap;
W_ *nGAs;
{
    W_ size, ptrs, nonptrs, vhs;

    P_ bufptr = buffer + PACK_HDR_SIZE;

    P_ slotptr;

    globalAddr ga;
    P_ closure, existing;
    P_ ip, oldip;

    W_ bufsize;
    P_ graphroot, graph, parent;
    W_ pptr = 0, pptrs = 0, pvhs;

    int i;

    globalAddr *gaga = PendingGABuffer;

    InitClosureQueue();

    /* Unpack the header */
    bufsize = buffer[0];

    /* allocate heap */
    if (bufsize > 0) {
	graph = AllocateHeap(bufsize);
        ASSERT(graph != NULL);
    }

    parent = NULL;

    do {
	/* This is where we will ultimately save the closure's address */
	slotptr = bufptr;

	/* First, unpack the next GA or PLC */
	ga.weight = *bufptr++;

	if (ga.weight > 0) {
	    ga.loc.gc.gtid = *bufptr++;
	    ga.loc.gc.slot = *bufptr++;
	} else
	    ga.loc.plc = (P_) *bufptr++;

	/* Now unpack the closure body, if there is one */
	if (isFixed(&ga)) {
	  /* No more to unpack; just set closure to local address */
#ifdef PACK_DEBUG
	  fprintf(stderr, "Unpacked PLC at %x \n", ga.loc.plc); 
#endif
	  closure = ga.loc.plc;
	} else if (isOffset(&ga)) {
	    /* No more to unpack; just set closure to cached address */
	    ASSERT(parent != NULL);
	    closure = (P_) buffer[ga.loc.gc.slot];
	} else {

	    /* Now we have to build something. */

	  ASSERT(bufsize > 0);

	  /*
	   * Close your eyes.  You don't want to see where we're looking. You
	   * can't get closure info until you've unpacked the variable header,
	   * but you don't know how big it is until you've got closure info.
	   * So...we trust that the closure in the buffer is organized the
	   * same way as they will be in the heap...at least up through the
	   * end of the variable header.
	   */
	  ip = get_closure_info(bufptr, &size, &ptrs, &nonptrs, &vhs);
	  
	  /* Fill in the fixed header */
	  for (i = 0; i < FIXED_HS; i++)
	    graph[i] = *bufptr++;

	  if (INFO_TYPE(ip) == INFO_FETCHME_TYPE)
	    size = ptrs = nonptrs = vhs = 0;

	  /* Fill in the packed variable header */
	  for (i = 0; i < vhs; i++)
	    graph[FIXED_HS + i] = *bufptr++;

	  /* Pointers will be filled in later */

	  /* Fill in the packed non-pointers */
	  for (i = 0; i < nonptrs; i++)
	    graph[FIXED_HS + i + vhs + ptrs] = *bufptr++;
                
	  /* Indirections are never packed */
	  ASSERT(INFO_PTR(graph) != (W_) Ind_info);

	  /* Add to queue for processing */
	  QueueClosure(graph);
	
	  /*
	   * Common up the new closure with any existing closure having the same
	   * GA
	   */

	  if ((existing = GALAlookup(&ga)) == NULL) {
	    globalAddr *newGA;
	    /* Just keep the new object */
#ifdef PACK_DEBUG
	    fprintf(stderr, "Unpacking new (%x, %d, %x)\n", 
		    ga.loc.gc.gtid, ga.loc.gc.slot, ga.weight);
#endif
	    closure = graph;
	    newGA = setRemoteGA(graph, &ga, rtsTrue);
	    if (INFO_TYPE(ip) == INFO_FETCHME_TYPE)
	      FETCHME_GA(closure) = newGA;
	  } else {
	    /* Two closures, one global name.  Someone loses */
	    oldip = (P_) INFO_PTR(existing);

	    if ((INFO_TYPE(oldip) == INFO_FETCHME_TYPE || IS_BLACK_HOLE(oldip)) &&
		INFO_TYPE(ip) != INFO_FETCHME_TYPE) {

	      /* What we had wasn't worth keeping */
	      closure = graph;
	      CommonUp(existing, graph);
	    } else {

	      /*
	       * Either we already had something worthwhile by this name or
	       * the new thing is just another FetchMe.  However, the thing we
	       * just unpacked has to be left as-is, or the child unpacking
	       * code will fail.  Remember that the way pointer words are
	       * filled in depends on the info pointers of the parents being
	       * the same as when they were packed.
	       */
#ifdef PACK_DEBUG
	      fprintf(stderr, "Unpacking old (%x, %d, %x), keeping %#lx\n", 
		      ga.loc.gc.gtid, ga.loc.gc.slot, ga.weight, existing);
#endif
	      closure = existing;
	    }
	    /* Pool the total weight in the stored ga */
	    (void) addWeight(&ga);
	  }

	  /* Sort out the global address mapping */
	  if ((IS_THUNK(ip) && IS_UPDATABLE(ip)) || 
	      (IS_MUTABLE(ip) && INFO_TYPE(ip) != INFO_FETCHME_TYPE)) {
	    /* Make up new GAs for single-copy closures */
	    globalAddr *newGA = MakeGlobal(closure, rtsTrue);

	    ASSERT(closure == graph);

	    /* Create an old GA to new GA mapping */
	    *gaga++ = ga;
	    splitWeight(gaga, newGA);
	    ASSERT(gaga->weight == 1L << (BITS_IN(unsigned) - 1));
	    gaga++;
	  }
	  graph += FIXED_HS + (size < MIN_UPD_SIZE ? MIN_UPD_SIZE : size);
	}

	/*
	 * Set parent pointer to point to chosen closure.  If we're at the top of
	 * the graph (our parent is NULL), then we want to arrange to return the
	 * chosen closure to our caller (possibly in place of the allocated graph
	 * root.)
	 */
	if (parent == NULL)
	    graphroot = closure;
	else
	    parent[FIXED_HS + pvhs + pptr] = (W_) closure;

	/* Save closure pointer for resolving offsets */
	*slotptr = (W_) closure;

	/* Locate next parent pointer */
	pptr++;
	while (pptr + 1 > pptrs) {
	    parent = DeQueueClosure();

	    if (parent == NULL)
		break;
	    else {
		(void) get_closure_info(parent, &size, &pptrs, &nonptrs, &pvhs);
		pptr = 0;
	    }
	}
    } while (parent != NULL);

    ASSERT(bufsize == 0 || graph - 1 <= SAVE_Hp);

    *gamap = PendingGABuffer;
    *nGAs = (gaga - PendingGABuffer) / 2;

    /* ToDo: are we *certain* graphroot has been set??? WDP 95/07 */
    return (graphroot);
}
\end{code}

\begin{code}
#endif /* PAR -- whole file */
\end{code}