# New Ticket Created by  Kenneth A Graves 
# Please include the string:  [perl #23186]
# in the subject line of all future correspondence about this issue. 
# <URL: http://rt.perl.org/rt2/Ticket/Display.html?id=23186 >


The .pcc_* directives are working for me in terms of implementing
function calls.  I want to do something similar for iterator calls. 
I've decided to implement iterators using coroutines.

The initial call to the iterator (sending in the parameters) is
identical to a function call, except P0 is a Coroutine PMC instead of a
Sub, and there is an implicit Continuation argument to mark where the
caller wants control to go after the iterator is complete.

Subsequent calls to the iterator are just savetop, invoke, loop around
to the return_from_subroutine label.

Inside the iterator, there are three differences compared to a function:
1) Every register in use needs to be saved to the user stack.  (Anyone
have a clean way to do this?)
2) Returning control to the caller is via "invoke", not "invoke P1".
3) When finished, invoke the implicit Continuation mentioned above.

To get this to work with IMCC, the only change I need is to get (2) to
happen.  So I added ".pcc_begin_yield" and ".pcc_end_yield", which do
the exact same thing as ".pcc_begin_return" and ".pcc_end_return",
except leaving off the " P1".  (For now.  Moving the save/restore of (1)
sub into the yield would be nice if I can figure out a way to do it.)

Example code and patch (relative to languages/imcc) attached.  If I'm
off in left field compared to how coroutines/iterators were planned to
be supported, someone please say so.

--kag





-- attachment  1 ------------------------------------------------------
url: http://rt.perl.org/rt2/attach/62028/45719/dee7e8/pcc_yield.patch

-- attachment  2 ------------------------------------------------------
url: http://rt.perl.org/rt2/attach/62028/45720/422adf/foo.pir

--- imcc.l.~1.40.~      2003-07-30 11:00:29.000000000 -0400
+++ imcc.l      2003-07-31 12:38:42.000000000 -0400
@@ -153,6 +153,8 @@
 ".pcc_sub"      return(PCC_SUB);
 ".pcc_begin_return"    return(PCC_BEGIN_RETURN);
 ".pcc_end_return"      return(PCC_END_RETURN);
+".pcc_begin_yield"    return(PCC_BEGIN_YIELD);
+".pcc_end_yield"      return(PCC_END_YIELD);
 "prototyped"    return PROTOTYPED;
 "non_prototyped"    return NON_PROTOTYPED;
 
--- imcc.y.~1.71.~      2003-07-30 19:00:18.000000000 -0400
+++ imcc.y      2003-07-31 13:01:50.000000000 -0400
@@ -409,13 +409,14 @@
 %token <t> GLOBAL ADDR CLONE RESULT RETURN POW SHIFT_RIGHT_U LOG_AND LOG_OR
 %token <t> COMMA ESUB
 %token <t> PCC_BEGIN PCC_END PCC_CALL PCC_SUB PCC_BEGIN_RETURN PCC_END_RETURN
+%token <t> PCC_BEGIN_YIELD PCC_END_YIELD
 %token <t> PROTOTYPED NON_PROTOTYPED
 %token <s> LABEL
 %token <t> EMIT EOM
 %token <s> IREG NREG SREG PREG IDENTIFIER STRINGC INTC FLOATC REG MACRO ENDM
 %token <s> PARROT_OP
 %type <t> type
-%type <i> program sub sub_start emit nsub pcc_sub sub_body pcc_ret
+%type <i> program sub sub_start emit nsub pcc_sub sub_body pcc_ret pcc_yield
 %type <s> classname relop
 %type <i> labels _labels label statements statement
 %type <i> pcc_sub_call
@@ -582,6 +583,22 @@
         PCC_END_RETURN '\n'             { $$ = 0; }
     ;
 
+pcc_yield: PCC_BEGIN_YIELD '\n' {
+                Instruction *i, *ins = instructions;
+                char name[128];
+                if (!ins || !ins->r[1] || ins->r[1]->type != VT_PCC_SUB)
+                    fataly(EX_SOFTWARE, "pcc_yield", line,
+                        "pcc_yield not inside pcc subroutine\n");
+                $<sr>$ = ins->r[1];
+                sprintf(name, "#pcc_sub_yield_%d:", line - 1);
+                i = _mk_instruction("", name, NULL, 0);
+                i = emitb(i);
+                i->type = ITPCCSUB | ITLABEL | ITPCCYIELD;
+        }
+        pcc_returns
+        PCC_END_YIELD '\n'             { $$ = 0; }
+    ;
+
 pcc_returns: /* empty */                { $$ = 0; }
     |       pcc_returns '\n'            { if($1) add_pcc_return($<sr>0, $1); }
     | pcc_returns pcc_return '\n'       { if($2) add_pcc_return($<sr>0, $2); }
@@ -599,6 +616,7 @@
         | MACRO '\n'                  { $$ = 0; }
         | pcc_sub_call                { $$ = 0; }
         | pcc_ret
+        | pcc_yield
     ;
 
 labels:        /* none */         { $$ = NULL; }
--- instructions.h.~1.24.~      2003-07-29 11:00:15.000000000 -0400
+++ instructions.h      2003-07-31 12:53:29.000000000 -0400
@@ -11,7 +11,8 @@
     ITSPILL = 0x400000, /*   set P31,x ; set x, p31 spilling */
     ITEXT   = 0x800000, /*   instruction is extcall in JIT */
     ITSAVES = 0x1000000,  /*   saveall/restoreall in a bsr */
-    ITPCCSUB = 0x2000000  /*  PCC sub call */
+    ITPCCSUB = 0x2000000,  /*  PCC sub call */
+    ITPCCYIELD = 0x4000000 /* yield from PCC call instead of return */
 };
 
 
--- pcc.c.~1.1.~        2003-07-30 09:42:48.000000000 -0400
+++ pcc.c       2003-07-31 13:22:15.000000000 -0400
@@ -48,9 +48,10 @@
 expand_pcc_sub_ret(Parrot_Interp interpreter, Instruction *ins)
 {
     SymReg *arg, *sub, *reg, *regs[IMCC_MAX_REGS];
-    int next[4], i, j, n;
+    int next[4], i, j, n, arg_count;
     char types[] = "INSP";
 
+    arg_count = ins->type & ITPCCYIELD ? 0 : 1;
     for (i = 0; i < 4; i++)
         next[i] = 5;
     /* the first ins holds the sub SymReg */
@@ -94,7 +95,7 @@
      */
     reg = mk_pasm_reg(str_dup("P1"));
     regs[0] = reg;
-    ins = insINS(interpreter, ins, "invoke", NULL, regs, 1, 0);
+    ins = insINS(interpreter, ins, "invoke", NULL, regs, arg_count, 0);
 }
 
 void
# pseudo source code:
#       main () {
#         int i=5;
#         foreach addtwo(i) {
#           print $_, "\n";
#         }
#         print "done in main\n";
#       }
#
#       addtwo (int a) {
#         int i;
#         for (i=0; i<10; i++) {
#           yield a+i;
#         }
#         print "done in coroutine\n";
#       }

.sub _main
  .local int i
  i=5
  newsub $P0, .Coroutine, _addtwo
  newsub $P1, .Continuation, after_loop
  .pcc_begin prototyped
  .arg $P1
  .arg i
  .pcc_call $P0
 ret_addr:
  .result $I2
  .pcc_end
    print $I2
    print "\n"
    savetop
    invoke
    goto ret_addr
 after_loop:
  print "done in main\n"
  end
.end

.pcc_sub _addtwo
  .param Continuation when_done
  .param int a
  .local int i
  i = 0
 loop:
    if i >= 10 goto done
    $I5 = a+i
    save i
    save a
    .pcc_begin_yield
    .return $I5
    .pcc_end_yield
    restore a
    restore i
    i = i + 1
    goto loop
 done:
  print "done in coroutine\n"
  invoke when_done
  end
.end

Reply via email to