]> granicus.if.org Git - jq/commitdiff
Minor refactor of function call setup to allow for multiple arguments.
authorStephen Dolan <mu@netsoc.tcd.ie>
Sun, 25 Nov 2012 23:07:43 +0000 (23:07 +0000)
committerStephen Dolan <mu@netsoc.tcd.ie>
Sun, 25 Nov 2012 23:07:43 +0000 (23:07 +0000)
compile.c
opcode.c
opcode_list.h

index fe50c70ab364f318fe17cd9d9631dbf3a06fc512..e0f675f85cf407d92c6f7204c2cf64b16af91d55 100644 (file)
--- 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;
index bc259c2285215e30b81ea87f4d9fe135878c9278..6e85e7093cd29b335d72ea3c785b540a57a832ad 100644 (file)
--- 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
index 306373fbf06eab8d306ac499b8592b1af1081044..00817a1387fce8d388c1f104e731f0fc0d98c97d 100644 (file)
@@ -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)