From: Stephen Dolan <mu@netsoc.tcd.ie> Date: Sun, 25 Nov 2012 23:07:43 +0000 (+0000) Subject: Minor refactor of function call setup to allow for multiple arguments. X-Git-Tag: jq-1.2~42 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f19ba32fdde2bfea89e6c10a9183d130b6de99aa;p=jq Minor refactor of function call setup to allow for multiple arguments. --- diff --git a/compile.c b/compile.c index fe50c70..e0f675f 100644 --- a/compile.c +++ b/compile.c @@ -30,7 +30,9 @@ struct inst { // are bound, and even then only for instructions which bind. struct inst* bound_by; char* symbol; + block subfn; + block arglist; // This instruction is compiled as part of which function? // (only used during block_compile) @@ -47,6 +49,7 @@ static inst* inst_new(opcode op) { i->bound_by = 0; i->symbol = 0; i->subfn = gen_noop(); + i->arglist = gen_noop(); i->source = UNKNOWN_LOCATION; return i; } @@ -56,6 +59,9 @@ static void inst_free(struct inst* i) { if (opcode_describe(i->op)->flags & OP_HAS_BLOCK) { block_free(i->subfn); } + if (opcode_describe(i->op)->flags & OP_HAS_VARIABLE_LENGTH_ARGLIST) { + block_free(i->arglist); + } if (opcode_describe(i->op)->flags & OP_HAS_CONSTANT) { jv_free(i->imm.constant); } @@ -186,30 +192,11 @@ block gen_op_block_bound(opcode op, block binder) { } block gen_op_call(opcode op, block arglist) { + assert(op == CALL_1_1); assert(opcode_describe(op)->flags & OP_HAS_VARIABLE_LENGTH_ARGLIST); inst* i = inst_new(op); - block prelude = gen_noop(); - block call = inst_block(i); - int nargs = 0; - inst* curr = 0; - while ((curr = block_take(&arglist))) { - assert(opcode_describe(curr->op)->flags & OP_IS_CALL_PSEUDO); - block bcurr = inst_block(curr); - switch (curr->op) { - default: assert(0 && "Unknown type of parameter"); break; - case CLOSURE_REF: - block_append(&call, bcurr); - break; - case CLOSURE_CREATE: - block_append(&prelude, bcurr); - block_append(&call, gen_op_block_bound(CLOSURE_REF, bcurr)); - break; - } - nargs++; - } - assert(nargs < 100); //FIXME - i->imm.intval = nargs; - return block_join(prelude, call); + i->arglist = arglist; + return block_join(inst_block(i), inst_block(inst_new(CALLSEQ_END))); } static void inst_join(inst* a, inst* b) { @@ -220,6 +207,32 @@ static void inst_join(inst* a, inst* b) { b->prev = a; } +static void block_insert_after(inst* i, block b) { + if (b.first) { + assert(b.last); + if (i->next) { + inst* j = i->next; + i->next = 0; + j->prev = 0; + inst_join(b.last, j); + } + inst_join(i, b.first); + } +} + +static void block_insert_before(inst* i, block b) { + if (b.first) { + assert(b.last); + if (i->prev) { + inst* j = i->prev; + i->prev = 0; + j->next = 0; + inst_join(j, b.first); + } + inst_join(b.last, i); + } +} + void block_append(block* b, block b2) { if (b2.first) { if (b->last) { @@ -266,6 +279,10 @@ static void block_bind_subblock(block binder, block body, int bindflags) { // binding recurses into closures block_bind_subblock(binder, i->subfn, bindflags); } + if (flags & OP_HAS_VARIABLE_LENGTH_ARGLIST) { + // binding recurses into argument list + block_bind_subblock(binder, i->arglist, bindflags); + } } } @@ -457,12 +474,70 @@ static int count_cfunctions(block b) { return n; } + +// Expands call instructions into a calling sequence +// Checking for argument count compatibility happens later +static void expand_call_arglist(struct bytecode* bc, block b) { + for (inst* curr = b.first; curr; curr = curr->next) { + if (opcode_describe(curr->op)->flags & OP_HAS_VARIABLE_LENGTH_ARGLIST) { + assert(curr->op == CALL_1_1); + assert(curr->next && curr->next->op == CALLSEQ_END); + // We expand the argument list as a series of instructions + block arglist = curr->arglist; + curr->arglist = gen_noop(); + assert(arglist.first && "zeroth argument (function to call) must be present"); + inst* function = arglist.first->bound_by; + assert(function); + + switch (function->op) { + default: assert(0 && "Unknown parameter type"); break; + case CLOSURE_CREATE: + case CLOSURE_PARAM: { + block prelude = gen_noop(); + block callargs = gen_noop(); + int nargs = 0; + for (inst* i; (i = block_take(&arglist));) { + assert(opcode_describe(i->op)->flags & OP_IS_CALL_PSEUDO); + block b = inst_block(i); + switch (i->op) { + default: assert(0 && "Unknown type of parameter"); break; + case CLOSURE_REF: + block_append(&callargs, b); + break; + case CLOSURE_CREATE: + block_append(&prelude, b); + block_append(&callargs, gen_op_block_bound(CLOSURE_REF, b)); + break; + } + nargs++; + } + + assert(!arglist.first); + block_insert_before(curr, prelude); + block_insert_after(curr, callargs); + curr->imm.intval = nargs; + break; + } + + case CLOSURE_CREATE_C: + // Arguments to C functions not yet supported + assert(block_is_single(arglist)); + assert(arglist.first->op == CLOSURE_REF); + block_insert_after(curr, arglist); + curr->imm.intval = 1; + break; + } + } + } +} + static int compile(struct locfile* locations, struct bytecode* bc, block b) { int errors = 0; int pos = 0; int var_frame_idx = 0; bc->nsubfunctions = 0; bc->nclosures = 0; + expand_call_arglist(bc, b); for (inst* curr = b.first; curr; curr = curr->next) { if (!curr->next) assert(curr == b.last); pos += opcode_length(curr->op); @@ -484,6 +559,7 @@ static int compile(struct locfile* locations, struct bytecode* bc, block b) { assert(curr->bound_by == curr); curr->imm.intval = bc->nsubfunctions++; } + if (curr->op == CLOSURE_PARAM) { assert(curr->bound_by == curr); curr->imm.intval = bc->nclosures++; @@ -524,9 +600,11 @@ static int compile(struct locfile* locations, struct bytecode* bc, block b) { int opflags = op->flags; assert(!(op->flags & OP_IS_CALL_PSEUDO)); if (opflags & OP_HAS_VARIABLE_LENGTH_ARGLIST) { + assert(curr->op == CALL_1_1); int nargs = curr->imm.intval; assert(nargs > 0); code[pos++] = (uint16_t)nargs; + int desired_params = 0; for (int i=0; i<nargs; i++) { curr = curr->next; diff --git a/opcode.c b/opcode.c index bc259c2..6e85e70 100644 --- a/opcode.c +++ b/opcode.c @@ -7,6 +7,7 @@ #define BRANCH OP_HAS_BRANCH, 2 #define CFUNC (OP_HAS_SYMBOL | OP_HAS_CFUNC | OP_HAS_VARIABLE_LENGTH_ARGLIST), 2 #define UFUNC (OP_HAS_UFUNC | OP_HAS_VARIABLE_LENGTH_ARGLIST), 2 +#define CALLSEQ_END_IMM (OP_IS_CALL_PSEUDO), 0 #define CLOSURE_PARAM_IMM (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 0 #define CLOSURE_CREATE_IMM (OP_HAS_BLOCK | OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 0 #define CLOSURE_CREATE_C_IMM (OP_IS_CALL_PSEUDO | OP_HAS_BINDING), 0 diff --git a/opcode_list.h b/opcode_list.h index 306373f..00817a1 100644 --- a/opcode_list.h +++ b/opcode_list.h @@ -24,6 +24,7 @@ OP(CALL_BUILTIN_3_1, CFUNC, 3, 1) OP(CALL_1_1, UFUNC, 1, 1) OP(RET, NONE, 1, 1) +OP(CALLSEQ_END, CALLSEQ_END_IMM, 0, 0) OP(CLOSURE_PARAM, CLOSURE_PARAM_IMM, 0, 0) OP(CLOSURE_REF, CLOSURE_REF_IMM, 0, 0) OP(CLOSURE_CREATE, CLOSURE_CREATE_IMM, 0, 0)