X-Git-Url: http://git.megacz.com/?a=blobdiff_plain;f=ghc%2Frts%2FInterpreter.c;h=95ddc48c2e144367b4dcb6a6a9c2f1346b0ef287;hb=49c120b93b0688863d46582eee6b20bfbed6c077;hp=fb474ea8588c032a8a77947d4d6faa25fb015ea4;hpb=730e2dd8168e27c0aa98a9556df2802b8e92aa7c;p=ghc-hetmet.git diff --git a/ghc/rts/Interpreter.c b/ghc/rts/Interpreter.c index fb474ea..95ddc48 100644 --- a/ghc/rts/Interpreter.c +++ b/ghc/rts/Interpreter.c @@ -5,12 +5,16 @@ * Copyright (c) 1994-2000. * * $RCSfile: Interpreter.c,v $ - * $Revision: 1.11 $ - * $Date: 2001/01/12 12:06:24 $ + * $Revision: 1.34 $ + * $Date: 2002/02/15 22:15:08 $ * ---------------------------------------------------------------------------*/ -#ifdef GHCI - +#if !defined(SMP) +#include "PosixSource.h" +#else +/* Hack and slash.. */ +#include "Stg.h" +#endif #include "Rts.h" #include "RtsAPI.h" #include "RtsUtils.h" @@ -31,7 +35,24 @@ * The new bytecode interpreter * ------------------------------------------------------------------------*/ -/* Sp points to the lowest live word on the stack. */ +/* The interpreter can be compiled so it just interprets BCOs and + hands literally everything else to the scheduler. This gives a + "reference interpreter" which is correct but slow -- useful for + debugging. By default, we handle certain closures specially so as + to dramatically cut down on the number of deferrals to the + scheduler. Ie normally you don't want REFERENCE_INTERPRETER to be + defined. */ + +/* #define REFERENCE_INTERPRETER */ + +/* Gather stats about entry, opcode, opcode-pair frequencies. For + tuning the interpreter. */ + +/* #define INTERP_STATS */ + + + +/* iSp points to the lowest live word on the stack. */ #define StackWord(n) iSp[n] #define BCO_NEXT instrs[bciPtr++] @@ -39,35 +60,111 @@ #define BCO_LIT(n) (W_)literals[n] #define BCO_ITBL(n) itbls[n] -#define LOAD_STACK_POINTERS \ - iSp = cap->rCurrentTSO->sp; iSu = cap->rCurrentTSO->su; +#define LOAD_STACK_POINTERS \ + iSp = cap->r.rCurrentTSO->sp; \ + iSu = cap->r.rCurrentTSO->su; \ + /* We don't change this ... */ \ + iSpLim = cap->r.rCurrentTSO->stack + RESERVED_STACK_WORDS; + -#define SAVE_STACK_POINTERS \ - cap->rCurrentTSO->sp = iSp; cap->rCurrentTSO->su = iSu; +#define SAVE_STACK_POINTERS \ + cap->r.rCurrentTSO->sp = iSp; \ + cap->r.rCurrentTSO->su = iSu; -#define RETURN(retcode) \ +#define RETURN(retcode) \ SAVE_STACK_POINTERS; return retcode; static __inline__ StgPtr allocate_UPD ( int n_words ) { - //fprintf(stderr, "alloc_UPD %d -> ", n_words ); if (n_words - sizeofW(StgHeader) < MIN_UPD_SIZE) n_words = MIN_UPD_SIZE + sizeofW(StgHeader); - //fprintf(stderr, "%d\n", n_words ); return allocate(n_words); } static __inline__ StgPtr allocate_NONUPD ( int n_words ) { - //fprintf(stderr, "alloc_NONUPD %d -> ", n_words ); if (n_words - sizeofW(StgHeader) < MIN_NONUPD_SIZE) n_words = MIN_NONUPD_SIZE + sizeofW(StgHeader); - //fprintf(stderr, "%d\n", n_words ); return allocate(n_words); } +#ifdef INTERP_STATS +/* Hacky stats, for tuning the interpreter ... */ +int it_unknown_entries[N_CLOSURE_TYPES]; +int it_total_unknown_entries; +int it_total_entries; + +int it_retto_BCO; +int it_retto_UPDATE; +int it_retto_other; + +int it_slides; +int it_insns; +int it_BCO_entries; + +int it_ofreq[27]; +int it_oofreq[27][27]; +int it_lastopc; + +void interp_startup ( void ) +{ + int i, j; + it_retto_BCO = it_retto_UPDATE = it_retto_other = 0; + it_total_entries = it_total_unknown_entries = 0; + for (i = 0; i < N_CLOSURE_TYPES; i++) + it_unknown_entries[i] = 0; + it_slides = it_insns = it_BCO_entries = 0; + for (i = 0; i < 27; i++) it_ofreq[i] = 0; + for (i = 0; i < 27; i++) + for (j = 0; j < 27; j++) + it_oofreq[i][j] = 0; + it_lastopc = 0; +} + +void interp_shutdown ( void ) +{ + int i, j, k, o_max, i_max, j_max; + fprintf(stderr, "%d constrs entered -> (%d BCO, %d UPD, %d ??? )\n", + it_retto_BCO + it_retto_UPDATE + it_retto_other, + it_retto_BCO, it_retto_UPDATE, it_retto_other ); + fprintf(stderr, "%d total entries, %d unknown entries \n", + it_total_entries, it_total_unknown_entries); + for (i = 0; i < N_CLOSURE_TYPES; i++) { + if (it_unknown_entries[i] == 0) continue; + fprintf(stderr, " type %2d: unknown entries (%4.1f%%) == %d\n", + i, 100.0 * ((double)it_unknown_entries[i]) / + ((double)it_total_unknown_entries), + it_unknown_entries[i]); + } + fprintf(stderr, "%d insns, %d slides, %d BCO_entries\n", + it_insns, it_slides, it_BCO_entries); + for (i = 0; i < 27; i++) + fprintf(stderr, "opcode %2d got %d\n", i, it_ofreq[i] ); + + for (k = 1; k < 20; k++) { + o_max = 0; + i_max = j_max = 0; + for (i = 0; i < 27; i++) { + for (j = 0; j < 27; j++) { + if (it_oofreq[i][j] > o_max) { + o_max = it_oofreq[i][j]; + i_max = i; j_max = j; + } + } + } + + fprintf ( stderr, "%d: count (%4.1f%%) %6d is %d then %d\n", + k, ((double)o_max) * 100.0 / ((double)it_insns), o_max, + i_max, j_max ); + it_oofreq[i_max][j_max] = 0; + + } +} +#endif + + StgThreadReturnCode interpretBCO ( Capability* cap ) { /* On entry, the closure to interpret is on the top of the @@ -83,14 +180,18 @@ StgThreadReturnCode interpretBCO ( Capability* cap ) LOAD_STACK_POINTERS; - iSpLim = cap->rCurrentTSO->stack + RESERVED_STACK_WORDS; - /* Main object-entering loop. Object to be entered is on top of stack. */ nextEnter: obj = (StgClosure*)StackWord(0); iSp++; + nextEnter_obj: + +# ifdef INTERP_STATS + it_total_entries++; +# endif + IF_DEBUG(evaluator, fprintf(stderr, "\n---------------------------------------------------------------\n"); @@ -100,29 +201,92 @@ StgThreadReturnCode interpretBCO ( Capability* cap ) // checkSanity(1); // iSp--; StackWord(0) = obj; - // checkStack(iSp,cap->rCurrentTSO->stack+cap->rCurrentTSO->stack_size,iSu); + // checkStack(iSp,cap->r.rCurrentTSO->stack+cap->r.rCurrentTSO->stack_size,iSu); // iSp++; - printStack(iSp,cap->rCurrentTSO->stack+cap->rCurrentTSO->stack_size,iSu); + printStack(iSp,cap->r.rCurrentTSO->stack+cap->r.rCurrentTSO->stack_size,iSu); fprintf(stderr, "\n\n"); ); + + switch ( get_itbl(obj)->type ) { + case INVALID_OBJECT: barf("Invalid object %p",(StgPtr)obj); -#if 0 +# ifndef REFERENCE_INTERPRETER + + case IND: + case IND_OLDGEN: + case IND_PERM: + case IND_OLDGEN_PERM: + case IND_STATIC: + { + obj = ((StgInd*)obj)->indirectee; + goto nextEnter_obj; + } + + case CONSTR: + case CONSTR_1_0: + case CONSTR_0_1: + case CONSTR_2_0: + case CONSTR_1_1: + case CONSTR_0_2: + case CONSTR_INTLIKE: + case CONSTR_CHARLIKE: + case CONSTR_STATIC: + case CONSTR_NOCAF_STATIC: + nextEnter_obj_CONSTR: + { + StgInfoTable* ret_itbl = (StgInfoTable*)StackWord(0); + if (ret_itbl == (StgInfoTable*)&stg_ctoi_ret_R1p_info) { +# ifdef INTERP_STATS + it_retto_BCO++; +# endif + /* Returning this constr to a BCO. Push the constr on + the stack and enter the return continuation BCO, which + is immediately underneath ret_itbl. */ + StackWord(-1) = (W_)obj; + obj = (StgClosure*)StackWord(1); + iSp --; + if (get_itbl(obj)->type == BCO) + goto nextEnter_obj_BCO; /* fast-track common case */ + else + goto nextEnter_obj; /* a safe fallback */ + } else + if (ret_itbl == (StgInfoTable*)&stg_upd_frame_info) { +# ifdef INTERP_STATS + it_retto_UPDATE++; +# endif + /* Returning this constr to an update frame. Do the + update and re-enter the constr. */ + ASSERT((W_*)iSu == iSp); + UPD_IND(iSu->updatee, obj); + iSu = iSu->link; + iSp += sizeofW(StgUpdateFrame); + goto nextEnter_obj_CONSTR; + } +# ifdef INTERP_STATS + else it_retto_other++; +# endif + goto defer_to_sched; + } + case AP_UPD: - { nat Words; - nat i; + /* Copied from stg_AP_UPD_entry. */ + { + nat i, words; StgAP_UPD *ap = (StgAP_UPD*)obj; - Words = ap->n_args; + words = ap->n_args; - /* WARNING: do a stack overflow check here ! - This code (copied from stg_AP_UPD_entry) is not correct without it. */ + /* Stack check. If a stack overflow might occur, don't enter + the closure; let the scheduler handle it instead. */ + if (iSp - (words+sizeofW(StgUpdateFrame)) < iSpLim) + goto defer_to_sched; + /* Ok; we're safe. Party on. Push an update frame. */ iSp -= sizeofW(StgUpdateFrame); - { StgUpdateFrame *__frame; __frame = (StgUpdateFrame *)iSp; @@ -132,21 +296,75 @@ StgThreadReturnCode interpretBCO ( Capability* cap ) iSu = __frame; } - iSp -= Words; - /* Reload the stack */ - for (i=0; ipayload[i]; + iSp -= words; + for (i=0; i < words; i++) StackWord(i) = (W_)ap->payload[i]; - iSp--; StackWord(0) = (W_)ap->fun; - goto nextEnter; - } -#endif + obj = (StgClosure*)ap->fun; + goto nextEnter_obj; + } - case BCO: + case PAP: + /* Copied from stg_PAP_entry. */ + { + nat words, i; + StgPAP* pap = (StgPAP *)obj; + + /* + * remove any update frames on the top of the stack, by just + * performing the update here. + */ + while ((W_)iSu - (W_)iSp == 0) { + + switch (get_itbl(iSu)->type) { + + case UPDATE_FRAME: + /* We're sitting on top of an update frame, so let's + do the business. */ + UPD_IND(iSu->updatee, pap); + iSu = iSu->link; + iSp += sizeofW(StgUpdateFrame); + continue; + + case SEQ_FRAME: + /* Too complicated ... adopt the Usual Solution. */ + /* fprintf(stderr, "!!! SEQ frame in PAP update\n"); */ + goto defer_to_sched; + + case CATCH_FRAME: + /* can't happen, see stg_update_PAP */ + barf("interpretBCO: PAP_entry: CATCH_FRAME"); + + default: + barf("interpretBCO: PAP_entry: strange activation record"); + } + } + + words = pap->n_args; + /* Stack check. If a stack overflow might occur, don't enter + the closure; let the scheduler handle it instead. */ + if (iSp - words < iSpLim) + goto defer_to_sched; + + /* Ok; safe. */ + iSp -= words; + for (i=0; i < words; i++) StackWord(i) = (W_)pap->payload[i]; + + obj = (StgClosure*)pap->fun; + goto nextEnter_obj; + } + +# endif /* ndef REFERENCE_INTERPRETER */ + + case BCO: /* ---------------------------------------------------- */ /* Start of the bytecode interpreter */ /* ---------------------------------------------------- */ + nextEnter_obj_BCO: +# ifdef INTERP_STATS + it_BCO_entries++; +# endif { int do_print_stack = 1; register int bciPtr = 1; /* instruction pointer */ @@ -157,18 +375,41 @@ StgThreadReturnCode interpretBCO ( Capability* cap ) register StgInfoTable** itbls = (StgInfoTable**) (&bco->itbls->payload[0]); + /* Heap check */ if (doYouWantToGC()) { iSp--; StackWord(0) = (W_)bco; + cap->r.rCurrentTSO->what_next = ThreadEnterInterp; RETURN(HeapOverflow); } + /* "Standard" stack check */ + if (iSp - (INTERP_STACK_CHECK_THRESH+1) < iSpLim) { + iSp--; + StackWord(0) = (W_)obj; + cap->r.rCurrentTSO->what_next = ThreadEnterInterp; + RETURN(StackOverflow); + } + + /* Context-switch check */ + if (context_switch) { + iSp--; + StackWord(0) = (W_)obj; + cap->r.rCurrentTSO->what_next = ThreadEnterInterp; + RETURN(ThreadYielding); + } + + +# ifdef INTERP_STATS + it_lastopc = 0; /* no opcode */ +# endif + nextInsn: ASSERT(bciPtr <= instrs[0]); IF_DEBUG(evaluator, //if (do_print_stack) { //fprintf(stderr, "\n-- BEGIN stack\n"); - //printStack(iSp,cap->rCurrentTSO->stack+cap->rCurrentTSO->stack_size,iSu); + //printStack(iSp,cap->r.rCurrentTSO->stack+cap->r.rCurrentTSO->stack_size,iSu); //fprintf(stderr, "-- END stack\n\n"); //} do_print_stack = 1; @@ -180,27 +421,62 @@ StgThreadReturnCode interpretBCO ( Capability* cap ) fprintf(stderr, "%d %p\n", i, (StgPtr)(*(iSp+i))); fprintf(stderr,"\n"); } - //if (do_print_stack) checkStack(iSp,cap->rCurrentTSO->stack+cap->rCurrentTSO->stack_size,iSu); + //if (do_print_stack) checkStack(iSp,cap->r.rCurrentTSO->stack+cap->r.rCurrentTSO->stack_size,iSu); ); +# ifdef INTERP_STATS + it_insns++; + ASSERT( (int)instrs[bciPtr] >= 0 && (int)instrs[bciPtr] < 27 ); + it_ofreq[ (int)instrs[bciPtr] ] ++; + it_oofreq[ it_lastopc ][ (int)instrs[bciPtr] ] ++; + it_lastopc = (int)instrs[bciPtr]; +# endif switch (BCO_NEXT) { + case bci_STKCHECK: { + /* An explicit stack check; we hope these will be + rare. */ + int stk_words_reqd = BCO_NEXT + 1; + if (iSp - stk_words_reqd < iSpLim) { + iSp--; + StackWord(0) = (W_)obj; + cap->r.rCurrentTSO->what_next = ThreadEnterInterp; + RETURN(StackOverflow); + } + goto nextInsn; + } case bci_ARGCHECK: { int i; StgPAP* pap; int arg_words_reqd = BCO_NEXT; int arg_words_avail = ((W_*)iSu) - ((W_*)iSp); if (arg_words_avail >= arg_words_reqd) goto nextInsn; - /* Handle arg check failure. Copy the spare args - into a PAP frame. */ - /* fprintf(stderr, "arg check fail %d %d\n", arg_words_reqd, arg_words_avail ); */ + +# ifndef REFERENCE_INTERPRETER + + /* Optimisation: if there are no args avail and the + t-o-s is an update frame, do the update, and + re-enter the object. */ + if (arg_words_avail == 0 + && get_itbl(iSu)->type == UPDATE_FRAME) { + UPD_IND(iSu->updatee, obj); + iSu = iSu->link; + iSp += sizeofW(StgUpdateFrame); + goto nextEnter_obj_BCO; + } + +# endif /* ndef REFERENCE_INTERPRETER */ + + /* Handle arg check failure. General case: copy the + spare args into a PAP frame. */ pap = (StgPAP*)allocate_UPD(PAP_sizeW(arg_words_avail)); SET_HDR(pap,&stg_PAP_info,CCS_SYSTEM/*ToDo*/); pap->n_args = arg_words_avail; pap->fun = obj; for (i = 0; i < arg_words_avail; i++) pap->payload[i] = (StgClosure*)StackWord(i); + /* Push on the stack and defer to the scheduler. */ iSp = (StgPtr)iSu; iSp --; @@ -209,7 +485,8 @@ StgThreadReturnCode interpretBCO ( Capability* cap ) fprintf(stderr,"\tBuilt "); printObj((StgClosure*)pap); ); - RETURN(ThreadEnterGHC); + cap->r.rCurrentTSO->what_next = ThreadEnterGHC; + RETURN(ThreadYielding); } case bci_PUSH_L: { int o1 = BCO_NEXT; @@ -222,6 +499,8 @@ StgThreadReturnCode interpretBCO ( Capability* cap ) case bci_PUSH_LL: { int o1 = BCO_NEXT; int o2 = BCO_NEXT; + ASSERT((W_*)iSp+o1 < (W_*)iSu); + ASSERT((W_*)iSp+o2 < (W_*)iSu); StackWord(-1) = StackWord(o1); StackWord(-2) = StackWord(o2); iSp -= 2; @@ -231,6 +510,9 @@ StgThreadReturnCode interpretBCO ( Capability* cap ) int o1 = BCO_NEXT; int o2 = BCO_NEXT; int o3 = BCO_NEXT; + ASSERT((W_*)iSp+o1 < (W_*)iSu); + ASSERT((W_*)iSp+o2 < (W_*)iSu); + ASSERT((W_*)iSp+o3 < (W_*)iSu); StackWord(-1) = StackWord(o1); StackWord(-2) = StackWord(o2); StackWord(-3) = StackWord(o3); @@ -276,6 +558,9 @@ StgThreadReturnCode interpretBCO ( Capability* cap ) StackWord(n+by) = StackWord(n); } iSp += by; +# ifdef INTERP_STATS + it_slides++; +# endif goto nextInsn; } case bci_ALLOC: { @@ -285,7 +570,7 @@ StgThreadReturnCode interpretBCO ( Capability* cap ) ap = (StgAP_UPD*)allocate_UPD(request); StackWord(-1) = (W_)ap; ap->n_args = n_payload; - SET_HDR(ap, &stg_AP_UPD_info, ??) + SET_HDR(ap, &stg_AP_UPD_info, CCS_SYSTEM/*ToDo*/) iSp --; goto nextInsn; } @@ -341,9 +626,7 @@ StgThreadReturnCode interpretBCO ( Capability* cap ) int request = CONSTR_sizeW( itbl->layout.payload.ptrs, itbl->layout.payload.nptrs ); StgClosure* con = (StgClosure*)allocate_NONUPD(request); - //fprintf(stderr, "---PACK p %d, np %d\n", - // (int) itbl->layout.payload.ptrs, - // (int) itbl->layout.payload.nptrs ); + ASSERT( itbl->layout.payload.ptrs + itbl->layout.payload.nptrs > 0); SET_HDR(con, BCO_ITBL(o_itbl), CCS_SYSTEM/*ToDo*/); for (i = 0; i < n_words; i++) con->payload[i] = (StgClosure*)StackWord(i); @@ -416,6 +699,30 @@ StgThreadReturnCode interpretBCO ( Capability* cap ) bciPtr = failto; goto nextInsn; } + case bci_TESTLT_F: { + /* The top thing on the stack should be a tagged float. */ + int discr = BCO_NEXT; + int failto = BCO_NEXT; + StgFloat stackFlt, discrFlt; + ASSERT(sizeofW(StgFloat) == StackWord(0)); + stackFlt = PK_FLT( & StackWord(1) ); + discrFlt = PK_FLT( & BCO_LIT(discr) ); + if (stackFlt >= discrFlt) + bciPtr = failto; + goto nextInsn; + } + case bci_TESTEQ_F: { + /* The top thing on the stack should be a tagged float. */ + int discr = BCO_NEXT; + int failto = BCO_NEXT; + StgFloat stackFlt, discrFlt; + ASSERT(sizeofW(StgFloat) == StackWord(0)); + stackFlt = PK_FLT( & StackWord(1) ); + discrFlt = PK_FLT( & BCO_LIT(discr) ); + if (stackFlt != discrFlt) + bciPtr = failto; + goto nextInsn; + } /* Control-flow ish things */ case bci_ENTER: { @@ -428,9 +735,11 @@ StgThreadReturnCode interpretBCO ( Capability* cap ) int tag = StackWord(0); StgInfoTable* ret_itbl = (StgInfoTable*)StackWord(tag +1); ASSERT(tag <= 2); /* say ... */ - if (ret_itbl == (StgInfoTable*)&stg_ctoi_ret_R1_info + if (ret_itbl == (StgInfoTable*)&stg_ctoi_ret_R1p_info + || ret_itbl == (StgInfoTable*)&stg_ctoi_ret_R1n_info || ret_itbl == (StgInfoTable*)&stg_ctoi_ret_F1_info - || ret_itbl == (StgInfoTable*)&stg_ctoi_ret_D1_info) { + || ret_itbl == (StgInfoTable*)&stg_ctoi_ret_D1_info + || ret_itbl == (StgInfoTable*)&stg_ctoi_ret_V_info) { /* Returning to interpreted code. Interpret the BCO immediately underneath the itbl. */ StgBCO* ret_bco = (StgBCO*)StackWord(tag +1+1); @@ -444,18 +753,48 @@ StgThreadReturnCode interpretBCO ( Capability* cap ) the TOS value into R1/F1/D1 and do a standard compiled-code return. */ StgInfoTable* magic_itbl = BCO_ITBL(o_itoc_itbl); - StackWord(0) = (W_)magic_itbl; - RETURN(ThreadRunGHC); + if (magic_itbl != NULL) { + StackWord(0) = (W_)magic_itbl; + cap->r.rCurrentTSO->what_next = ThreadRunGHC; + RETURN(ThreadYielding); + } else { + /* Special case -- returning a VoidRep to + compiled code. T.O.S is the VoidRep tag, + and underneath is the return itbl. Zap the + tag and enter the itbl. */ + ASSERT(StackWord(0) == (W_)NULL); + iSp ++; + cap->r.rCurrentTSO->what_next = ThreadRunGHC; + RETURN(ThreadYielding); + } } } - + case bci_SWIZZLE: { + int stkoff = BCO_NEXT; + signed short n = (signed short)(BCO_NEXT); + StackWord(stkoff) += (W_)n; + goto nextInsn; + } + case bci_CCALL: { + StgInt tok; + int o_itbl = BCO_NEXT; + void(*marshall_fn)(void*) = (void (*)(void*))BCO_LIT(o_itbl); + SAVE_STACK_POINTERS; + tok = suspendThread(&cap->r,rtsFalse); + marshall_fn ( (void*)(& StackWord(0) ) ); + cap = (Capability *)((void *)resumeThread(tok,rtsFalse) - sizeof(StgFunTable)); + LOAD_STACK_POINTERS; + goto nextInsn; + } + case bci_JMP: { + /* BCO_NEXT modifies bciPtr, so be conservative. */ + int nextpc = BCO_NEXT; + bciPtr = nextpc; + goto nextInsn; + } case bci_CASEFAIL: barf("interpretBCO: hit a CASEFAIL"); - /* As yet unimplemented */ - case bci_TESTLT_F: - case bci_TESTEQ_F: - /* Errors */ default: barf("interpretBCO: unknown or unimplemented opcode"); @@ -469,19 +808,26 @@ StgThreadReturnCode interpretBCO ( Capability* cap ) /* End of the bytecode interpreter */ /* ---------------------------------------------------- */ + defer_to_sched: default: { +# ifdef INTERP_STATS + { int j = get_itbl(obj)->type; + ASSERT(j >= 0 && j < N_CLOSURE_TYPES); + it_unknown_entries[j]++; + it_total_unknown_entries++; + } +# endif + /* Can't handle this object; yield to sched. */ IF_DEBUG(evaluator, fprintf(stderr, "entering unknown closure -- yielding to sched\n"); printObj(obj); - ) - cap->rCurrentTSO->what_next = ThreadEnterGHC; + ); iSp--; StackWord(0) = (W_)obj; + cap->r.rCurrentTSO->what_next = ThreadEnterGHC; RETURN(ThreadYielding); } } /* switch on object kind */ barf("fallen off end of object-type switch in interpretBCO()"); } - -#endif /* GHCI */