ikptr ik_exec_code(ikpcb* pcb, ikptr code_ptr, ikptr argcount, ikptr cp) { ikptr argc = ik_asm_enter(pcb, code_ptr+off_code_data, argcount, cp); ikptr next_k = pcb->next_k; while(next_k) { cont* k = (cont*)(long)(next_k - vector_tag); if (k->tag == system_continuation_tag) { break; } ikptr top = k->top; ikptr rp = ref(top, 0); long int framesize = (long int) ref(rp, disp_frame_size); #ifdef DEBUG_EXEC fprintf(stderr, "exec framesize=0x%016lx ksize=%ld rp=0x%016lx\n", framesize, k->size, rp); #endif if(framesize == 0) { framesize = ref(top, wordsize); } if(framesize <= 0) { fprintf(stderr, "invalid framesize %ld\n", framesize); exit(-10); } if(framesize < k->size) { cont* nk = (cont*)(long)ik_unsafe_alloc(pcb, sizeof(cont)); nk->tag = k->tag; nk->next = k->next; nk->top = top + framesize; nk->size = k->size - framesize; k->size = framesize; k->next = vector_tag + (ikptr)(long)nk; /* record side effect */ unsigned long int idx = ((unsigned long int)(&k->next)) >> pageshift; ((unsigned int*)(long)(pcb->dirty_vector))[idx] = -1; } else if (framesize > k->size) {
ikptr_t ik_exec_code (ikpcb_t * pcb, ikptr_t s_code, ikptr_t s_argcount, ikptr_t s_closure) /* Execute Scheme code and all its continuations until no more continuations are stored in the PCB or a system continuation is found in the continuations linked list. S_CODE is a tagged memory pointer referencing the code object implementing S_CLOSURE, if any. S_ARGCOUNT is a fixnum representing the negated number of Scheme arguments. S_CLOSURE is a reference to the closure object to execute; it can be the fixnum zero if there is no closure to execute, as when we enter a loaded FASL file. Return the return value of the last executed continuation. */ { /* A fixnum representing the negated number of returned Scheme values. It can be zero. */ ikptr_t s_retval_count; /* Reference to the continuation object representing the C or Scheme continuation we want to go back to. */ ikptr_t s_kont; if (0 || DEBUG_EXEC) { ik_debug_message_no_newline("%s: enter closure 0x%016lx, code 0x%016lx, annotation: ", __func__, (long)s_closure, (long) s_code); ik_fprint(stderr, IK_REF(s_code, off_code_annotation)); fprintf(stderr, "\n"); } /* Enter compiled Scheme code. Before and after we assert that the frame pointer equals the frame base; this constraint on the Scheme stack is needed by the assembly routine "ik_asm_enter". It is responsibility of the caller of "ik_exec_code()" to set the Scheme stack appropriately. */ { assert(pcb->frame_base == pcb->frame_pointer); s_retval_count = ik_asm_enter(pcb, IK_CODE_ENTRY_POINT(s_code), s_argcount, s_closure); assert(pcb->frame_base == pcb->frame_pointer); } /* Loop until there are continuations to be reinstated. */ for (s_kont = pcb->next_k; s_kont; s_kont = pcb->next_k) { #ifndef NDEBUG { /* Assert that the situation on the Scheme stack is: * * high memory * | | <- pcb->frame_pointer = pcb->frame_base * |----------------------| * | ik_underflow_handler | <- pcb->frame_pointer - wordsize * |----------------------| * | return value 0 | * |----------------------| * | return value 1 | * |----------------------| * | return value 2 | * |----------------------| * | | * low memory * * Of course we cannot check for the presence of return values. */ ikptr_t underflow_handler; assert(pcb->frame_base == pcb->frame_pointer); underflow_handler = *(ikptr_t *)(pcb->frame_pointer - wordsize); assert(IK_UNDERFLOW_HANDLER == underflow_handler); } #endif assert(IK_IS_ANY_CONTINUATION(s_kont)); if (0 || DEBUG_EXEC) { ik_debug_message("%s: resuming process continuation s_kont=0x%016lx", __func__, (long)s_kont); } ikcont_t * kont = IK_CONTINUATION_STRUCT(s_kont); /* System continuations are created by the FFI to save the current C execution contest just before calling back a Scheme function. So if S_KONT is a system continuation: we have no Scheme code to go back to, we just return to the caller of this C function. It is responsibility of such caller to reinstate the continuation to C code. */ if (system_continuation_tag == kont->tag) break; assert(continuation_tag == kont->tag); /* RETURN_ADDRESS is a raw memory address being the entry point in machine code we have to jump back to. */ ikptr_t return_address = IK_REF(kont->top, 0); /* FRAMESIZE is stack frame size of the function we have to return to. This value was computed at compile time and stored in binary code just before the "call" instruction. */ iksword_t framesize = IK_CALLTABLE_FRAMESIZE(return_address); if (0 || DEBUG_EXEC) { ik_debug_message("%s: framesize=%ld kont->size=%ld return_address=0x%016lx", __func__, framesize, kont->size, return_address); } /* A continuation object can never have the underflow handler as return address of the top stack frame; if it has it: it is a wrongly generated continuation object. Wrong continuation generation is the problem of issue #35, so we react specially here by logging the state. */ if (IK_UNDERFLOW_HANDLER == return_address) { ik_exec_code_log_and_abort(pcb, s_kont); } /* Zero framesize means that we are returning to a continuation having as topmost stack frame a frame whose size could not be computed at compile time. In such cases the framesize field in the call table is set to zero and the actual stack frame size is computed at runtime and pushed on the stack frame itself before performing a "call" assembly instruction. */ if (0 == framesize) { framesize = IK_REF(kont->top, wordsize); } /* Perform some framesize validations. If these happen it means that there is a bug in Vicare. */ { if (framesize <= 0) { ik_abort("invalid caller function framesize %ld\n", framesize); } if (framesize > kont->size) { ik_exec_code_log_and_abort(pcb, s_kont); } } if (framesize < kont->size) { /* The process continuation we have to reinstate references 2 or more freezed frames. Mutate S_KONT to reference only the topmost freezed frame and create a new continuation object referencing the rest of the freezed frames. Register the "rest" continuation as "next process continuation". */ ikcont_t * rest_kont = (ikcont_t*)ik_unsafe_alloc(pcb, IK_ALIGN(continuation_size)); ikptr_t s_rest_kont = (ikptr_t)(((ikuword_t)rest_kont) | continuation_primary_tag); rest_kont->tag = continuation_tag; rest_kont->next = kont->next; rest_kont->top = kont->top + framesize; rest_kont->size = kont->size - framesize; kont->size = framesize; kont->next = s_rest_kont; /* FIXME Is it required to signal dirt for both the fields? Or it always happens that a continuation object's memory block is fully in a single page? In the original Ikarus code only the "kont->next" dirt was registered, but debugging of Issue #35 is making me paranoid. (Marco Maggi; Wed Mar 27, 2013) */ IK_SIGNAL_DIRT_IN_PAGE_OF_POINTER(pcb, &(kont->size)); IK_SIGNAL_DIRT_IN_PAGE_OF_POINTER(pcb, &(kont->next)); { /* Special validations to ease debugging of issue #35. */ if (0 == kont->size) { ik_debug_message("%s: next continuation with zero size 0x%016lx,\n\ \tframe return address=0x%016lx", __func__, s_kont, IK_REF(kont->top, 0)); } if (0 == rest_kont->size) { ik_debug_message("%s: rest continuation with zero size 0x%016lx,\n\ \ttop frame return address=0x%016lx", __func__, s_rest_kont, IK_REF(rest_kont->top, 0)); } }