Let us think a bit about the implementation of lexical variables.

Assignement

  First, let us consider how to compile a variable assignement such
  as:
     $x = $y
  where both $x and $y are lexical variables.  At first, one may think
  that this can be compiled simply into a register assignment:
     P0 = P1
  where P0 and P1 are the PMC registers standing respectively for $x
  and $y.

  But, I we look at the example below, we see that this will not work
  in general.
      sub foo {
        my $x = 13;
        my $y = 17;
        return (sub { print "$x\n"; },
                sub { $x = $y; });
      }
      ($print, $assign) = foo();
      &$assign();
      &$print();
  Indeed, the returned subroutines will not have access to the registers
  nor to the stack frame of the subroutine "foo".  This implies that
  variables must be allocated in the heap.

  It turns out that PMCs provide the right operations to implement
  variables:  we can compile
       my $x = 13;
       my $y = 17;
       $x = $y
  into
       new P0, .PerlInt          # Create e new integer variable ($x)
       set P0, 13                # Set its value to 13
       new P1, .PerlInt          # Create e new integer variable ($y)
       set P1, 17                # Set its value to 17
       set_pmc P0, P1            # Assigns the value of $y to $x
  ("set" invokes the "set_integer_native" method;
   "set_pmc" is not implemented yet, but is mentioned in PDD02.)

  So, we should really view a PMC not as a Perl value, but
  rather as a Perl variable.

:= operator

  Actually, things are a bit more complicated if we take the :=
  operator into account.  There seems to be two ways to implement it:
  - use an "alias" PMC which forward all method calls to another PMC:
    the operation "$x := $y" would then turns $x into an "alias" PMC
    pointing to $y;
  - implement a variable not as a PMC, but as a heap-allocated pointer
    to a PMC.

  The first possibility is probably more efficient because we avoid
  some memory allocations and indirections, but it is more complex to
  implement.  So I will only consider the second one.

Scratchpads

  We need to allocate an area in the heap for each lexical variable.
  Instead of allocating this area one variable at a time, we can
  allocate a single "scratchpad" value for all variables of a block:
  this is more efficient.

  The compiler can keep track of the index of the variables in the
  scratchpad.  So, the scratchpad can be implemented as an array of
  PMCs. (We will probably need some faster opcodes to access the
  array: there is no need to perform a method call, nor to do any
  bound checking.)

MY

  To implement MY, we need to be able to access to a variable by its
  name.  For this, the compiler can generate statically a hash mapping
  the variable name to its index in the scratchpad.  Then,
  MY{"foo"} will look up the index of the variable "foo" in the
  hash and use the index to get the variable PMC in the scratchpad.

  To access the scratchpad of an outer block, we need a way to move
  from a scratchpad to its parent scratchpad.  This is trivial if we
  always set the field 0 of a scratchpad to point to its parent
  scratchpad.  Likewise, we need a statically generated linked-list of
  hashes, which describe each scratchpad.

Closures

  A subroutine must have access to the scratchpads of all the
  englobing blocks.  As the scratchpads are linked, it is sufficient
  to add a pointer to the immediately englobing scratchpads to the
  closure (Sub class).

  Then, the exemple

      sub foo {
        my $x = 13;
        my $y = 17;
        return (sub { print "$x\n"; },
                sub { $x = $y; });
      }

   would be compiled into

      foo:  # We assume the closure is in P0
            # We extract the parent scratchpad from the closure and put it
            # in P1
            get_pad P1, P0
            # We allocate a new scratchpad
            new P2, .Array
            # The first field of the scratchpad contain the parent scratchpad
            set P2[0], P1
            # We allocate the $x variable
            new P3, .Int
            set P3, 13
            set P2[1], P3
            # We allocate the $y variable
            new P4, .Int
            set P4, 13
            set P2[2], P4
            # We create the first closure
            new P5, .Sub
            set_code P5, sub1
            set_pad P5, P2
            # We create the second closure
            new P6, .Sub
            set_code P6, sub2
            set_pad P6, P2
            # We put them into an array
            new P0, .Perlarray
            set P0[0], P5
            set P0[1], P6
            # We return the array
            ret
      sub1: # We assume the closure is in P0
            # We extract the parent scratchpad from the closure and put it
            # in P1
            get_pad P1, P0
            # We get the $x variable and put it in P2
            set P2, P1[1]
            # ... we print the variable value
            ret
      sub1: # We assume the closure is in P0
            # We extract the parent scratchpad from the closure and put it
            # in P1
            get_pad P1, P0
            # We get the $x and $y variables
            set P2, P1[1]
            set P3, P1[2]
            # We assign the value of $y to $x
            set_pmc P2, P3
            ret

Conclusion

  It seems to me that to implement lexical variables, we only need to
  implement the set_pmc method and to extend the Sub class so that it
  contains both a code pointer and a scratchpad.

  In particular, we don't need any special support for scratchpads, at
  least for the moment.

  What do you think?

-- Jerome

Reply via email to