From 1204e328dfebb46c6389a9b50a353ce0d5eb10a7 Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Mon, 30 Jun 2014 19:40:56 -0500 Subject: Add much commentary about CALL_JQ and call frames --- execute.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/execute.c b/execute.c index a1284f98..5b583e50 100644 --- a/execute.c +++ b/execute.c @@ -36,22 +36,23 @@ struct jq_state { }; struct closure { - struct bytecode* bc; - stack_ptr env; + struct bytecode* bc; // jq bytecode + stack_ptr env; // jq stack address of closed frame }; +// locals for any function called: either a closure or a local variable union frame_entry { struct closure closure; jv localvar; }; +// jq function call frame struct frame { - struct bytecode* bc; - stack_ptr env; - stack_ptr retdata; - uint16_t* retaddr; - /* bc->nclosures closures followed by bc->nlocals local variables */ - union frame_entry entries[0]; + struct bytecode* bc; // jq bytecode for callee + stack_ptr env; // jq stack address of frame to return to + stack_ptr retdata; // jq stack address to unwind to on RET + uint16_t* retaddr; // jq bytecode return address + union frame_entry entries[0]; // nclosures + nlocals }; static int frame_size(struct bytecode* bc) { @@ -652,23 +653,59 @@ jv jq_next(jq_state *jq) { } case CALL_JQ: { + /* + * Bytecode layout here: + * + * CALL_JQ + * (i.e., number of call arguments) + * (what we're calling) + * (frame reference + code pointer) + * + * + * + * Each closure consists of two uint16_t values: a "level" + * identifying the frame to be closed over, and an index. + * + * The level is a relative number of call frames reachable from + * the currently one; 0 -> current frame, 1 -> previous frame, and + * so on. + * + * The index is either an index of the closed frame's subfunctions + * or of the closed frame's parameter closures. If the latter, + * that closure will be passed, else the closed frame's pointer + * and the subfunction's code will form the closure to be passed. + * + * See make_closure() for more information. + */ jv input = stack_pop(jq); uint16_t nclosures = *pc++; - uint16_t* retaddr; + uint16_t* retaddr = pc + 2 + nclosures*2; + stack_ptr retdata = jq->stk_top; struct frame* new_frame; struct closure cl = make_closure(jq, pc); - if (nclosures == 0 && *(pc + 2) == RET && *pc == 1) { - // TCO + if (nclosures == 0 && *(pc + 2) == RET && *pc >= 1) { + /* + * TCO: Tail call with no references left to the current frame. + * + * The *pc >= 1 portion of the conditional checks that the + * callee does not close over this frame. The nclosures == 0 + * potion checks that there are no arguments (which could close + * over this frame). + * + * TODO: nclosures > 0 can still allow TCO if none of those + * closures refers to the current frame. + * + * TODO: chase JUMP chains to see if they end in RET. + * + * These additions will make us want to memoize here or in an + * optimizer pass preceding interpretation. + */ retaddr = frame_current(jq)->retaddr; - stack_ptr retdata = frame_current(jq)->retdata; + retdata = frame_current(jq)->retdata; frame_pop(jq); - new_frame = frame_push(jq, cl, pc + 2, 0); - new_frame->retdata = retdata; - } else { - retaddr = pc + 2 + nclosures*2; - new_frame = frame_push(jq, cl, pc + 2, nclosures); - new_frame->retdata = jq->stk_top; } + new_frame = frame_push(jq, cl, pc + 2, nclosures); + new_frame->retdata = retdata; new_frame->retaddr = retaddr; pc = new_frame->bc->code; stack_push(jq, input); -- cgit v1.2.3