All --

> The missing code is going to look something like this:
> Next time I'm in front of my development machine, I'll give this a try.
> If this approach works, I'll work harder to produce a version of the
> solution that we can be proud of.

OK. I got it working. I'm attaching the compile.pl and Parrot/OpFunc.pm
files. Here's what I did to make it work:

  $ perl compile.pl t/test.pbc > t/test.c
  $ cc -c -O3 -I ./include -o t/test.o t/test.c
  $ cc -o t/test t/test.o strnative.o string.o register.o parrot.o \
  memory.o interpreter.o global_setup.o bytecode.o basic_opcodes.o -lm
  $ t/test

I got 150 Mop/s running this (vs. about 10 before!)

This code should be cleaned up along the lines of what Simon has been
doing lately to the assembler/disassembler and PackFile:: classes. I
can take that on later unless someone else is eager to do it.

BTW, maybe we should be using time_n instead of time_i in test.pasm?
I'm converting t/test.pasm to little_languages/mops.jako (I'll need
to add time() to jakoc to get it to work). When that works, I'll make
it use time_n instead of time_i. Then, I'll re-run stuff. Stay tuned.


Regards,

-- Gregor
 _____________________________________________________________________ 
/     perl -e 'srand(-2091643526); print chr rand 90 for (0..4)'      \

   Gregor N. Purdy                          [EMAIL PROTECTED]
   Focus Research, Inc.                http://www.focusresearch.com/
   8080 Beckett Center Drive #203                   513-860-3570 vox
   West Chester, OH 45069                           513-860-3579 fax
\_____________________________________________________________________/
#! /usr/bin/perl -w
#
# compile.pl
#
# Turn a parrot bytecode file into text.
#
# Copyright (C) 2001 The Parrot Team. All rights reserved.
# This program is free software. It is subject to the same license
# as the Parrot interpreter.
#
# $Id: $
#

use strict;

use Parrot::Opcode;
use Parrot::PackFile;
use Parrot::PackFile::ConstTable;
use Parrot::OpFunc;

Parrot::OpFunc->init('basic_opcodes.ops');

#
# GLOBAL VARIABLES:
#

my %unpack_type = (i => 'l',
		   I => 'l',
		   n => 'd',
		   N => 'l',
		   D => 'l',
		   S => 'l',
		   s => 'l',
                  );
my %unpack_size = (i => 4,
		   n => 8,
		   I => 4,
		   N => 4,
		   D => 4,
		   S => 4,
		   s => 4,
		   );

my %opcodes            = Parrot::Opcode::read_ops();
my $opcode_fingerprint = Parrot::Opcode::fingerprint();
my @opcodes;
for my $name (keys %opcodes) {
    $opcodes[$opcodes{$name}{CODE}] = { NAME => $name,
					%{$opcodes{$name}} };
}


#
# dump_const_table()
#

sub dump_const_table {
    my ($pf) = @_;

    my $count = $pf->const_table->const_count;

    if ($count < 1) {
	warn "Disassembling without opcode table fingerprint!";
	return;
    }

    die "Cannot compile (differing opcode table)!"
        if $pf->const_table->constant(0)->data ne $opcode_fingerprint;

    print "# Constants: $count entries\n";
    print "# ID  Flags    Encoding Type     Size     Data\n"; 

    my $constant_num = 0;

    foreach ($pf->const_table->constants) {
        printf("%04x: %08x %08x %08x %08x %s\n",
	    $constant_num, $_->flags, $_->encoding, $_->type,
            $_->size, $_->data);

	$constant_num++;
    }
}


#
# compile_byte_code()
#

sub compile_byte_code {
    my ($pf) = @_;
    my $pc;
    my $new_pc = 1;
    my $op_size;

    my $nconst = $pf->const_table->const_count;

    print <<END_C;
#include "parrot/parrot.h"
#include "parrot/string.h"
#include <math.h>

int
main(int argc, char **argv) {
    int                    i;
    STRING *               constants[$nconst];
    struct Parrot_Interp * interpreter;

    init_world();
  
    interpreter = make_interpreter();
    Parrot_string_constants = constants;

END_C

    for(my $i = 0; $i < $nconst; $i++) {
      my $const    = $pf->const_table->constant($i);
      my $type     = $const->type;
      my $encoding = $const->encoding;
      my $size     = $const->size;
      my $flags    = $const->flags;
      my $data     = $const->data;

print <<END_C;
    constants[$i] = string_make("$data", $size, $encoding, $flags, $type);
END_C
    }




    my $cursor = 0;
    my $length = length($pf->byte_code);

    my $offset=0;

    while ($offset + 4 <= $length) {
        $pc = $new_pc;

	my $op_start = $offset;
	my $op_code = unpack "x$offset l", $pf->byte_code;
        my $op_name = $opcodes[$op_code]{NAME};

	printf "/* $opcodes[$op_code]{NAME} ";

	$offset += 4;
        $op_size = 1;

	my $arg_count = $opcodes[$op_code]{ARGS};
	my @args = ();
        my @comment_args = ();

        if ($arg_count) {
	  foreach (0 .. $arg_count - 1) {
	      my $type        = $opcodes[$op_code]{TYPES}[$_];
	      my $unpack_type = $unpack_type{$type};
	      my $unpack_size = $unpack_size{$type};
  
	      die "$0: Premature end of bytecode in argument.\n"
		  if ($offset + $unpack_size) > $length;
  
	      my $arg = unpack "x$offset $unpack_type", $pf->byte_code;
	      $offset += $unpack_size;
              $op_size += $unpack_size / 4;
  
	      if($type =~ m/^[INPS]$/) { # Register
		  push @args, $arg;
		  push @comment_args, $type . $arg;
	      } elsif($type eq "D") { # destination address
		  push @args, "$arg";
		  push @comment_args, $arg;
	      } elsif($type eq "s") { # string constant
		  push @args, $arg;
		  push @comment_args, "[String $arg]";
	      } else { # constant
		  push @args, $arg;
		  push @comment_args, $arg;
	      }
	  }
  
	  print join(", ", @comment_args);
        }

        print " */\n";

#        print "/* OP $op_name [$op_code] */\n";
        my $body = $op_body{$op_name};

        #
        # Map {{@N}} ==> $args[N - 1]
        #

	while ($body =~ m/{{@(\d+)}}/m) {
          my $rep = $args[$1 - 1];
	  $body =~ s/{{@(\d+)}}/$rep/m;
        }

        #
        # Map {{=}} ==> PC_$pc
        #

	while ($body =~ m/{{=}}/m) {
          my $rep = "PC_$pc";
	  $body =~ s/{{=}}/$rep/m;
        }

        #
        # Map {{+=N}} ==> PC_$new_pc (where $new_pc = $pc + N)
        #

	$body =~ s/\+=-/-=/mg;

	while ($body =~ m/{{([+-])=(\d+)}}/m) {
          my $rep = "PC_" . ($pc + "$1$2");
	  $body =~ s/{{[+-]=(\d+)}}/$rep/m;
        }

        #
        # Map {{N}} ==> PC_N
        #

	$body =~ s/\+=-/-=/mg;

	while ($body =~ m/{{(\d+)}}/m) {
          my $rep = "PC_$1";
	  $body =~ s/{{(\d+)}}/$rep/m;
        }

        print $body;

        $new_pc = $pc + $op_size;
    }

    print <<END_C;

PC_$new_pc:
PC_0: {
    exit(0);
}

    return 0;
}
END_C

    return 0;
}


#
# compile_file()
#

sub compile_file {
    my ($file_name) = @_;

    my $pf = Parrot::PackFile->new;
    $pf->unpack_file($file_name);

#    dump_const_table($pf);
    compile_byte_code($pf);
 
    undef $pf;

    return;
}


#
# MAIN PROGRAM:
#

@ARGV = qw(-) unless @ARGV;

foreach (@ARGV) {
    compile_file($_)
}

exit 0;

__END__

=head1 NAME

compile.pl - compile the byte code from Parrot Pack Files to C

=head1 SYNOPSIS

  perl compile.pl FILE

=head1 DESCRIPTION

Compile the Parrot Pack Files listed on the command line, or
from standard input if no file is named.

=head1 COPYRIGHT

Copyright (C) 2001 The Parrot Team. All rights reserved.

=head1 LICENSE

This program is free software. It is subject to the same license
as the Parrot interpreter.

#! perl -w
#
# OpFunc.pm
#
# Take a file of opcode functions and create real C code for them
#
# opcode functions are in the format:
#
#  AUTO_OP opname {
#
#   ... body of function ...
#
#  }
#
# Where the closing brace is on its own line. Alternately, for opcode
# functions that manage their own return values:
#
# MANUAL_OP opname {
#
#    ... body of function ...
#
#    RETURN(x);
#
# }
#
# There may be more than one RETURN
#
# The functions have the magic variables Pnnn for parameters 1 through
# X. (Parameter 0 is the opcode number) Types for each, and the size
# of the return offset, are taken from the opcode_table file
#

use strict;

package Parrot::OpFunc;

use Parrot::Opcode;
use Parrot::Config;

BEGIN {
    use Exporter;
    use vars qw(%op_body @EXPORT @ISA);
    @ISA = qw(Exporter);
    @EXPORT = qw(%op_body);
};


my $current_name = '';
my $current_body = '';

my %opcodes = Parrot::Opcode::read_ops();
my %opcode;
my $opcode;

my %psize = (i => 1,
             n => $PConfig{nvsize}/$PConfig{ivsize},
             I => 1,
             N => 1,
             D => 1,
             S => 1,
             s => 1,
             );


#
# init()
#

sub init
{
  my ($class, $file) = @_;

  die "Parrot::OpFunc::init(): No file specified!\n" unless defined $file;

open GUTS, "include/parrot/interp_guts.h"
  or die "Could not open include/parrot/interp_guts.h";
while (<GUTS>) {
    next unless /\tx\[(\d+)\] = ([a-z_]+);/;
    $opcode{$2}{OPNUM} = $1;
}



open OPCODE, "opcode_table" or die "Can't open opcode_table, $!/$^E";
while (<OPCODE>) {
    s/#.*//;
    s/^\s+//;
    chomp;
    next unless $_;

    my ($name, $params, @params) = split /\s+/;
    $opcode{$name}{PARAM_COUNT} = $params;
    $opcode{$name}{PARAM_ARRAY} = \@params;

    my $psize=0;
    foreach (@params) {
       $psize+=$psize{$_};
    }


    $opcode{$name}{RETURN_OFFSET} = 1 + $psize;
    my $count = 1;
    $opcode{$name}{PARAMETER_SUB} = ["", 
                                     map {if ($_ eq "n") { 
                                         my $temp = '*(NV *)(&cur_opcode[' . $count . 
'])';
                                         $count += 2;
                                         $temp;
                                     } else {
                                         "cur_opcode[" . $count++ . "]"
                                         }
                                      } @params];
}

my $orig = $file;
open INPUT, $file or die "Can't open $file, $!/$^E";
if (! ($file =~ s/\.ops$/.c/)) {
    $file .= ".c";
}


#
# Read through the file, generating C source code:
#

my($name, $footer, @param_sub);
while (<INPUT>) {
    my $op_size = 1;

    next if m|^\s*$|;            # Skip blank lines
    next if m|^\s*/\*.*\*/\s*$|; # Skip comment-only lines

    if (/^AUTO_OP/) {
        ($name, $footer, $op_size) = gen_auto_header($_);
        die unless defined $op_size;
    }

    if (/^MANUAL_OP/) {
        ($name, $footer, $op_size) = gen_manual_header($_);
        die unless defined $op_size;
    }

    if (/^(AUTO|MANUAL)_OP/) {
        my $count = 1;
        @param_sub = ("",
                      map {if ($_ eq "n") {
                          my $temp = '*(NV *)&{{\@$count}}';
                          $count += 2;
                          $temp;
                      } else {
                          "{{\@" . $count++ . "}}";
                      }
                       } @{$opcodes{$name}{TYPES}});
        next;
    }

    s/RETVAL/goto {{+=$op_size}}/;

    s/RETURN\(0\);/goto {{0}};/;

    s/RETURN\((.*)\)/goto {{+=$1}}/;

    s/\bP(\d+)\b/$param_sub[$1]/g;

    if (/^}/) {
        $current_body .= "$footer\n";
        next;
    }

    $current_body .= $_;
}

if ($current_name ne '') {
    $op_body{$current_name} = $current_body;
}

#print "OPS:\n";
#print join(', ', sort keys %op_body), "\n";

  return;
}


sub gen_auto_header {
    my ($line) = @_;
    my ($name) = $line =~ /AUTO_OP\s+(\w+)/;

    if ($current_name ne '') {
      $op_body{$current_name} = $current_body;
    }

    $current_name = $name;
    $current_body = '';

    my $psize=0;
    foreach (@{$opcodes{$name}{TYPES}}) {
       $psize+=$psize{$_};
    }
    my $return_offset = $psize + 1;

    $opcode{$name}{RETURN_OFFSET} = 1 + $psize;
    
    $current_body .= "{{=}}: { /* $name */\n";

    return($name, "  goto {{+=$return_offset}};\n}\n", $return_offset);
}

sub gen_manual_header {
    my ($line) = @_;
    my ($name) = $line =~ /MANUAL_OP\s+(\w+)/;

    if ($current_name ne '') {
      $op_body{$current_name} = $current_body;
    }

    $current_name = $name;
    $current_body = '';
    
    my $psize=0;
    foreach (@{$opcodes{$name}{TYPES}}) {
       $psize+=$psize{$_};
    }
    my $return_offset = $psize + 1;

    $opcode{$name}{RETURN_OFFSET} = 1 + $psize;
    
    $current_body .= "{{=}}: { /* $name */\n";

    return($name, "  goto {{+=$return_offset}};\n}\n", $return_offset);
}

1;

Reply via email to