Tuesday, November 25, 2014

Fun with opcodes

"If I were to implement a language now, I'd write a very minimal core suitable for bootstrapping. ... Think of a handful of ops. Think very low level. (Think something a little higher than the universal Turing machine and the lambda calculus and maybe a little bit more VMmy than a good Forth implementation, and you have it.) If you've come up with something that can replace XS, stop. You're there. Do not continue. That's what you need." (Chromatic, January 2013) 

In September or October I started thinking about that, and began to play with a tiny interpreter written in Perl that processed a rather loose version of Lua opcodes.

Lua includes a register-based virtual machine (VM) and has a uniquely light-and-performant driven view of interpreters.  Using Perl to implement a sort-of Lua VM opcode interpreter let me play with the concepts without having to really write very much, because Perl is nothing if not excellent at tokenizing strings and tearing apart text.

Here's the core loop.


my %R   = (); # registers
my %K   = (); # constants
my %U   = (); # "upvars"
my $PC  = 0;  # program counter
my $FPF = 0; # ???

my @prg = <DATA>;

while( $PC < @prg )
{
   my $line = $prg[ $PC ];
   my ($opcode, $a, $b, $c, $d) = split ' ', $line;

   $PC++;
  
   next unless $opcode;
  
   given( lc $opcode )
   {
      when (/ffd2/)         { print $R{$a}; print " " if $b eq '\s';  print "\n" if $b eq '\n' }
      when (/nl/)            { print "\n" }
     
      when (/clr/)          { $R{$_} = '' for $a .. $b }
      when (/move/)          { $R{$a} = $R{$b} }
      when (/loadk/)         { $R{$a} = $b     }
      when (/loadbool/)        { $R{$a} = $b? 1 : 0; $PC++ if $c }
      when (/loadnil/)        { $R{$_} = 0 for $a..$b }
      when (/getupval/)     { $R{$a} = $U{$b} }
     
      when (/gettabup/)     { $R{$a} = $U{$b}->{ $c } }
      when (/gettable/)     { $R{$a} = $R{$b}->{ $c } }
     
      when (/settabup/)     { $U{$a}->{ $b } = $R{$c} }
      when (/setupval/)     { $U{$b} = $R{$a} }
      when (/settable/)     { $R{$a}->{$b} = $c }
     
      when (/newtable/)     { $R{$a} = {} } # size = B,C
     
      when (/self/)         { $R{$a+1} = $R{$b};
                              $R{$a} = $R{$b}->{$c} }
                       
      when (/inc/)            { $R{$a}++ }
      when (/dec/)            { $R{$a}-- }
      when (/bz/)           { $PC += $b unless $R{$a} }
      when (/bnz/)          { $PC += $b if     $R{$a} }

      when (/add/)          { $R{$a} = $b + $c }
      when (/sub/)          { $R{$a} = $b - $c }
      when (/mul/)          { $R{$a} = $b * $c }
      when (/div/)             { $R{$a} = $b / $c }
      when (/mod/)          { $R{$a} = $b % $c }
      when (/pow/)            { $R{$a} = $b ** $c }
      when (/unm/)          { $R{$a} = -$R{$b} }
      when (/not/)          { $R{$a} = ~$R{$b} }         # ?
      when (/len/)          { $R{$a} = length $R{$b} }  # ...if scalar
                                     # $#R{$b}          # ...if hash
                                   
      when (/concat|cat|join/)    { my $sp = $R{$d}? ' ' : ''; $R{$a} = join( $sp, @R{$b..$c} ) }
           
      when (/ju?mp/)        { $PC += $a; closeAllUpvalues($R{$a}+1) if $a }
      when (/j?eq/)            { $PC++ if ( $b == $c ) != $a }
      when (/j?lt/)            { $PC++ if ( $b < $c ) != $a }
      when (/j?le/)             { $PC++ if ( $b <= $c ) != $a }
     
      when (/test/)         { $PC++ unless $R{$b} <=> $c }
      when (/testset/)        { ( $R{$b} <=> $c )? $R{$a} = $R{$b} : $PC++ }
 
      when (/call/)         { @R{$a..$a+$c-2} = call( @R{$a..$a+$b-1} ) }
     
      when (/tailcall/)     { call(@R{$a..$a+$b-1}) }
     
      when (/return/)       { @R{$a..$a+$b-2} }
     
      when (/forloop/)        { $R{$a} += $R{$a+2};
                              $PC += $b if $R{$a} <= $R{$a+1} }
     
      when (/forprep/)        { $R{$a} -= $R{$a+2}; $PC += $b }
     
      when (/tforcall/)        { @R{$a+3..$a+2+$c} = call( $R{$a..$a+2} ) }
     
      when (/tforloop/)        { if ( $R{$a+1} ) {
                                $R{$a} = $R{$a+1};
                                $PC += $b
                            }}
     
      when (/setlist/)        { $R{$a}->{ ($c-1) * $FPF + $_ } = $R{$a+1}
                                    for 1..$b;
                            }
     
      when (/closure/)        { $R{$a} = closure(proto($b)) }
      when (/vararg/)        { $R{$_} = 'vararg' for $R{$a} .. $R{$a+$b-2} }                   
     
   }; 
}





No comments:

Post a Comment