1 \section[stk-overflow]{Stack overflow routine}
3 %************************************************************************
5 \subsection[arity-error]{Arity error has nothing to do with stack overflow}
7 %************************************************************************
13 void PrintTickyInfo(STG_NO_ARGS);
15 #ifdef __DO_ARITY_CHKS__
23 fprintf(stderr, "Arity error: called with %ld args, should have been %ld\n",
26 #if defined(TICKY_TICKY)
27 if (RTSflags.TickyFlags.showTickyStats) PrintTickyInfo();
33 #endif /* __DO_ARITY_CHECKS__ */
36 %************************************************************************
38 \subsection[stk-oflow-seq]{Boring sequential stack overflow}
40 %************************************************************************
46 StackOverflow(STG_NO_ARGS)
49 StackOverflowHook(RTSflags.GcFlags.stksSize * sizeof(W_)); /*msg*/
51 #if defined(TICKY_TICKY)
52 if (RTSflags.TickyFlags.showTickyStats) PrintTickyInfo();
60 %************************************************************************
62 \subsection[stk-squeeze]{Code for squeezing out update frames}
64 %************************************************************************
66 Code for squeezing out vacuous update frames. Updatees of squeezed frames
67 are turned into indirections to the common black hole (or blocking queue).
71 SqueezeUpdateFrames(bottom, top, frame)
77 P_ next_frame = NULL; /* Temporally next */
78 P_ prev_frame; /* Temporally previous */
81 * If we have no update frames, there is nothing to do.
87 if ((prev_frame = GRAB_SuB(frame)) <= bottom) {
88 #if !defined(CONCURRENT)
89 if ( RTSflags.GcFlags.lazyBlackHoling )
90 UPD_BH(GRAB_UPDATEE(frame), BH_UPD_info);
96 * Walk down the stack, reversing the SuB pointers so that we can walk back up
97 * as we squeeze from the bottom. Note that next_frame and prev_frame refer to
98 * next and previous as they were added to the stack, rather than the way we see
99 * them in this walk. (It makes the next loop less confusing.)
102 while (prev_frame > bottom) {
103 PUSH_SuB(frame, next_frame);
106 prev_frame = GRAB_SuB(frame);
110 * Now, we're at the bottom. Frame points to the lowest update
111 * frame on the stack, and its saved SuB actually points to the
112 * frame above. We have to walk back up the stack, squeezing out
113 * empty update frames and turning the pointers back around on the
118 * The bottom-most frame has not been altered, and we never want
119 * to eliminate it anyway. Just black hole the updatee and walk
120 * one step up before starting to squeeze. When you get to the
121 * topmost frame, remember that there are still some words above
122 * it that might have to be moved.
125 #if !defined(CONCURRENT)
126 if ( RTSflags.GcFlags.lazyBlackHoling )
127 UPD_BH(GRAB_UPDATEE(frame), BH_UPD_info);
133 * Loop through all of the middle frames (everything except the
134 * very bottom and the very top).
136 while ((next_frame = GRAB_SuB(frame)) != NULL) {
138 P_ frame_bottom = frame + BREL(STD_UF_SIZE);
140 /* Check to see if the current frame is empty (both A and B) */
141 if (prev_frame == frame_bottom + BREL(displacement) &&
142 GRAB_SuA(next_frame) == GRAB_SuA(frame)) {
144 /* Now squeeze out the current frame */
145 P_ updatee_keep = GRAB_UPDATEE(prev_frame);
146 P_ updatee_bypass = GRAB_UPDATEE(frame);
149 fprintf(stderr, "squeezing frame at %lx, ret %lx\n", frame,
154 /* Check for a blocking queue on the node that's going away */
155 if (INFO_PTR(updatee_bypass) == (W_) BQ_info) {
156 /* Sigh. It has one. Don't lose those threads! */
157 if (INFO_PTR(updatee_keep) == (W_) BQ_info) {
158 /* Urgh. Two queues. Merge them. */
159 P_ tso = (P_) BQ_ENTRIES(updatee_keep);
161 while (TSO_LINK(tso) != Nil_closure)
164 TSO_LINK(tso) = (P_) BQ_ENTRIES(updatee_bypass);
166 /* For simplicity, just swap the BQ for the BH */
167 P_ temp = updatee_keep;
169 updatee_keep = updatee_bypass;
170 updatee_bypass = temp;
172 /* Record the swap in the kept frame (below) */
173 PUSH_UPDATEE(prev_frame, updatee_keep);
178 UPD_SQUEEZED(); /* ticky stuff (NB: nothing for spat-profiling) */
179 UPD_IND(updatee_bypass, updatee_keep);
181 sp = frame - BREL(1); /* Toss the current frame */
182 displacement += STD_UF_SIZE;
185 #if !defined(CONCURRENT)
186 if ( RTSflags.GcFlags.lazyBlackHoling )
187 UPD_BH(GRAB_UPDATEE(frame), BH_UPD_info);
190 /* No squeeze for this frame */
191 sp = frame_bottom - BREL(1); /* Keep the current frame */
193 /* Fix the SuB in the current frame (should point to the frame below) */
194 PUSH_SuB(frame, prev_frame);
197 /* Now slide all words from sp up to the next frame */
199 if (displacement > 0) {
200 P_ next_frame_bottom = next_frame + BREL(STD_UF_SIZE);
203 fprintf(stderr, "sliding [%lx, %lx] by %d\n", sp, next_frame_bottom,
207 while (sp <= next_frame_bottom) {
208 sp[BREL(displacement)] = *sp;
212 prev_frame = frame + BREL(displacement);
217 * Now handle the topmost frame. Patch SuB, black hole the
218 * updatee, and slide down.
221 PUSH_SuB(frame, prev_frame);
223 #if !defined(CONCURRENT)
224 if ( RTSflags.GcFlags.lazyBlackHoling )
225 UPD_BH(GRAB_UPDATEE(frame), BH_UPD_info);
228 if (displacement > 0) {
229 P_ sp = frame + BREL(STD_UF_SIZE) - BREL(1);
232 fprintf(stderr, "sliding [%lx, %lx] by %d\n", sp, top, displacement);
236 sp[BREL(displacement)] = *sp;
244 %************************************************************************
246 \subsection[stk-ouflow-par]{Rather exciting parallel stack overflow and underflow}
248 %************************************************************************
254 StackOverflow: called inside a nice ``callwrapper'' when stack
255 overflow occurs. The state is already saved in the TSO, and the stack
256 is in a tidy saved state.
259 EXTDATA_RO(StkO_info); /* boring extern decl */
260 EXTFUN(EnterNodeCode); /* For reentering node after potential GC */
263 EXTDATA_RO(FetchMe_info);
267 StackOverflow(args1, args2)
272 P_ old_stko, new_stko;
273 W_ headroom = STACK_OVERFLOW_HEADROOM(args1, args2);
277 W_ is_prim_return = STACK_OVERFLOW_PRIM_RETURN(args1, args2);
279 W_ reenter = STACK_OVERFLOW_REENTER(args1, args2);
280 W_ words_of_a = STACK_OVERFLOW_AWORDS(args1, args2);
281 W_ words_of_b = STACK_OVERFLOW_BWORDS(args1, args2);
282 W_ liveness = STACK_OVERFLOW_LIVENESS(args1, args2);
283 I_ really_reenter_node = 0;
285 SET_TASK_ACTIVITY(ST_OVERHEAD);
289 fprintf(stderr,"StackOverflow:liveness=%lx,a=%lx,b=%lx\n",
290 liveness,words_of_a,words_of_b);
293 old_stko = SAVE_StkO;
296 fprintf(stderr, "stko: %lx SpA %lx SuA %lx SpB %lx SuB %lx\n",
297 old_stko, STKO_SpA(old_stko),
298 STKO_SuA(old_stko), STKO_SpB(old_stko), STKO_SuB(old_stko));
301 if (RTSflags.GcFlags.squeezeUpdFrames) {
303 i = SqueezeUpdateFrames(STKO_BSTK_BOT(old_stko), STKO_SpB(old_stko),
306 STKO_SuB(old_stko) += BREL(i);
307 STKO_SpB(old_stko) += BREL(i);
309 /*?/ fprintf(stderr, "Just squeezed; now: SpB %lx SuB %lx retval %d\n", STKO_SpB(old_stko), STKO_SuB(old_stko), i); /?*/
311 if ((P_) STKO_SpA(old_stko) - AREL(headroom) > STKO_SpB(old_stko)) {
314 fprintf(stderr, "Squeezed; now: SpA %lx SpB %lx headroom %d\n", STKO_SpA(old_stko),
315 STKO_SpB(old_stko), headroom);
318 /* We saved enough space to continue on the old StkO */
322 SAVE_Liveness = liveness;
324 ASSERT(sanityChk_StkO(old_stko));
326 /* Double the stack chunk size each time we grow the stack */
327 /*?/ fprintf(stderr, "Stko %lx: about to double stk-chk size from %d...\n", old_stko, STKO_CLOSURE_CTS_SIZE(old_stko)); /?*/
328 cts_size = STKO_CLOSURE_CTS_SIZE(old_stko) * 2;
330 if (SAVE_Hp + STKO_HS + cts_size > SAVE_HpLim) {
333 * Even in the uniprocessor world, we may have to reenter node in case
334 * node is a selector shorted out by GC.
336 ASSERT(liveness & LIVENESS_R1);
337 TSO_PC2(CurrentTSO) = EnterNodeCode;
338 really_reenter_node = 1;
340 /*?/ fprintf(stderr, "StkO %lx: stk-chk GC: size %d...\n", old_stko, STKO_HS + cts_size);/?*/
341 ReallyPerformThreadGC(STKO_HS + cts_size, rtsFalse);
343 now, GC semantics promise to have left SAVE_Hp with
344 the requested space *behind* it; as we will bump
345 SAVE_Hp just below, we had better first put it back.
346 (PS: Finding this was a two-day bug-hunting trip...)
349 SAVE_Hp -= STKO_HS + cts_size;
351 old_stko = SAVE_StkO;
353 ALLOC_STK(STKO_HS, cts_size, 0);
354 new_stko = SAVE_Hp + 1;
355 SAVE_Hp += STKO_HS + cts_size;
356 SET_STKO_HDR(new_stko, StkO_info, CCC);
358 /*?/ fprintf(stderr, "New StkO now %lx...\n", new_stko); /?*/
360 /* Initialize the StkO, as in NewThread */
361 STKO_SIZE(new_stko) = cts_size + STKO_VHS;
362 STKO_SpB(new_stko) = STKO_SuB(new_stko) = STKO_BSTK_BOT(new_stko) + BREL(1);
363 STKO_SpA(new_stko) = STKO_SuA(new_stko) = STKO_ASTK_BOT(new_stko) + AREL(1);
364 STKO_LINK(new_stko) = old_stko;
366 /*?/ fprintf(stderr, "New StkO SpA = %lx...\n", STKO_SpA(new_stko) ); /?*/
368 STKO_RETURN(new_stko) = SAVE_Ret;
373 * When we fall off of the top stack segment, we will either be
374 * returning an algebraic data type, in which case R2 holds a
375 * valid info ptr, or we will be returning a primitive
376 * (e.g. Int#), in which case R2 is garbage. If we need to perform
377 * GC to pull in the lower stack segment (this should only happen
378 * because of task migration), then we need to know the register
379 * liveness for the algebraic returns. We get the liveness out of
380 * the info table. Now, we could set up the primitive returns
381 * with a bogus infoptr, which has a NO_LIVENESS field in the info
382 * table, but that would involve a lot more overhead than the
383 * current approach. At present, we set up RetReg to point to
384 * *either* a polymorphic algebraic return point, or a primitive
388 SAVE_Ret = is_prim_return ? (P_) PrimUnderflow : (P_) vtbl_Underflow;
390 SAVE_Ret = (P_) vtbl_Underflow;
393 STKO_SpA(old_stko) += AREL(words_of_a);
394 STKO_SpB(old_stko) += BREL(words_of_b);
397 /* Record the stack depths in chunks below the new stack object */
399 STKO_ADEP(new_stko) = STKO_ADEP(old_stko) +
400 AREL((I_) STKO_ASTK_BOT(old_stko) - (I_) STKO_SpA(old_stko));
401 STKO_BDEP(new_stko) = STKO_BDEP(old_stko) +
402 BREL((I_) STKO_BSTK_BOT(old_stko) - (I_) STKO_SpB(old_stko));
405 if (STKO_SpB(old_stko) < STKO_BSTK_BOT(old_stko)) {
407 * This _should_ only happen if PAP_entry fails a stack check
408 * and there is no update frame on the current stack. We can
409 * deal with this by storing a function's argument
410 * requirements in its info table, peering into the PAP (it
411 * had better be in R1) for the function pointer and taking
412 * only the necessary number of arguments, but this would be
413 * hard, so we haven't done it.
416 fprintf(stderr, "StackOverflow too deep (SpB=%lx, Bstk bot=%lx). Probably a PAP with no update frame.\n", STKO_SpB(old_stko), STKO_BSTK_BOT(old_stko));
417 abort(); /* an 'abort' may be overkill WDP 95/04 */
419 /* Move A stack words from old StkO to new StkO */
420 for (i = 1; i <= words_of_a; i++) {
421 STKO_SpA(new_stko)[-AREL(i)] = STKO_SpA(old_stko)[-AREL(i)];
423 STKO_SpA(new_stko) -= AREL(words_of_a);
425 /* Move B stack words from old StkO to new StkO */
426 for (i = 1; i <= words_of_b; i++) {
427 STKO_SpB(new_stko)[-BREL(i)] = STKO_SpB(old_stko)[-BREL(i)];
429 STKO_SpB(new_stko) -= BREL(words_of_b);
431 /* Now, handle movement of a single update frame */
432 /* ToDo: Make this more efficient. (JSM) */
433 if (STKO_SpB(old_stko) < STKO_SuB(old_stko)) {
434 /* Yikes! PAP_entry stole an update frame. Fix the world! */
435 P_ frame = STKO_SuB(new_stko) - BREL(STD_UF_SIZE);
438 fprintf(stderr, "Stolen update frame: (old %lx, new %lx) SuA %lx, SuB
439 %lx, return %lx\n", old_stko, new_stko, GRAB_SuA(frame), GRAB_SuB(frame),
443 STKO_SuA(old_stko) = GRAB_SuA(frame);
444 STKO_SuB(old_stko) = GRAB_SuB(frame);
446 SAVE_Ret = STKO_RETURN(new_stko);
447 STKO_RETURN(new_stko) = GRAB_RET(frame);
449 PUSH_SuA(frame, STKO_SuA(new_stko));
450 PUSH_SuB(frame, STKO_SuB(new_stko));
451 PUSH_RET(frame, vtbl_Underflow);
453 STKO_SuB(new_stko) = frame;
456 ASSERT(sanityChk_StkO(new_stko));
458 SAVE_StkO = new_stko;
460 return really_reenter_node;
464 Underflow things are all done in the threaded world. The code is in
468 #endif /* parallel */