+ bci = BCO_NEXT;
+ /* We use the high 8 bits for flags, only the highest of which is
+ * currently allocated */
+ ASSERT((bci & 0xFF00) == (bci & 0x8000));
+
+ switch (bci & 0xFF) {
+
+ /* check for a breakpoint on the beginning of a let binding */
+ case bci_BRK_FUN:
+ {
+ int arg1_brk_array, arg2_array_index, arg3_freeVars;
+ StgArrWords *breakPoints;
+ int returning_from_break; // are we resuming execution from a breakpoint?
+ // if yes, then don't break this time around
+ StgClosure *ioAction; // the io action to run at a breakpoint
+
+ StgAP_STACK *new_aps; // a closure to save the top stack frame on the heap
+ int i;
+ int size_words;
+
+ arg1_brk_array = BCO_NEXT; // 1st arg of break instruction
+ arg2_array_index = BCO_NEXT; // 2nd arg of break instruction
+ arg3_freeVars = BCO_NEXT; // 3rd arg of break instruction
+
+ // check if we are returning from a breakpoint - this info
+ // is stored in the flags field of the current TSO
+ returning_from_break = cap->r.rCurrentTSO->flags & TSO_STOPPED_ON_BREAKPOINT;
+
+ // if we are returning from a break then skip this section
+ // and continue executing
+ if (!returning_from_break)
+ {
+ breakPoints = (StgArrWords *) BCO_PTR(arg1_brk_array);
+
+ // stop the current thread if either the
+ // "rts_stop_next_breakpoint" flag is true OR if the
+ // breakpoint flag for this particular expression is
+ // true
+ if (rts_stop_next_breakpoint == rtsTrue ||
+ breakPoints->payload[arg2_array_index] == rtsTrue)
+ {
+ // make sure we don't automatically stop at the
+ // next breakpoint
+ rts_stop_next_breakpoint = rtsFalse;
+
+ // allocate memory for a new AP_STACK, enough to
+ // store the top stack frame plus an
+ // stg_apply_interp_info pointer and a pointer to
+ // the BCO
+ size_words = BCO_BITMAP_SIZE(obj) + 2;
+ new_aps = (StgAP_STACK *) allocate(cap, AP_STACK_sizeW(size_words));
+ SET_HDR(new_aps,&stg_AP_STACK_info,CCS_SYSTEM);
+ new_aps->size = size_words;
+ new_aps->fun = &stg_dummy_ret_closure;
+
+ // fill in the payload of the AP_STACK
+ new_aps->payload[0] = (StgClosure *)&stg_apply_interp_info;
+ new_aps->payload[1] = (StgClosure *)obj;
+
+ // copy the contents of the top stack frame into the AP_STACK
+ for (i = 2; i < size_words; i++)
+ {
+ new_aps->payload[i] = (StgClosure *)Sp[i-2];
+ }
+
+ // prepare the stack so that we can call the
+ // rts_breakpoint_io_action and ensure that the stack is
+ // in a reasonable state for the GC and so that
+ // execution of this BCO can continue when we resume
+ ioAction = (StgClosure *) deRefStablePtr (rts_breakpoint_io_action);
+ Sp -= 9;
+ Sp[8] = (W_)obj;
+ Sp[7] = (W_)&stg_apply_interp_info;
+ Sp[6] = (W_)&stg_noforceIO_info; // see [unreg] below
+ Sp[5] = (W_)new_aps; // the AP_STACK
+ Sp[4] = (W_)BCO_PTR(arg3_freeVars); // the info about local vars of the breakpoint
+ Sp[3] = (W_)False_closure; // True <=> a breakpoint
+ Sp[2] = (W_)&stg_ap_pppv_info;
+ Sp[1] = (W_)ioAction; // apply the IO action to its two arguments above
+ Sp[0] = (W_)&stg_enter_info; // get ready to run the IO action
+ // Note [unreg]: in unregisterised mode, the return
+ // convention for IO is different. The
+ // stg_noForceIO_info stack frame is necessary to
+ // account for this difference.
+
+ // set the flag in the TSO to say that we are now
+ // stopping at a breakpoint so that when we resume
+ // we don't stop on the same breakpoint that we
+ // already stopped at just now
+ cap->r.rCurrentTSO->flags |= TSO_STOPPED_ON_BREAKPOINT;
+
+ // stop this thread and return to the scheduler -
+ // eventually we will come back and the IO action on
+ // the top of the stack will be executed
+ RETURN_TO_SCHEDULER_NO_PAUSE(ThreadRunGHC, ThreadYielding);
+ }
+ }
+ // record that this thread is not stopped at a breakpoint anymore
+ cap->r.rCurrentTSO->flags &= ~TSO_STOPPED_ON_BREAKPOINT;
+
+ // continue normal execution of the byte code instructions
+ goto nextInsn;
+ }