All -- Here's the actual patch...
> I've updated the patch for the bytecode --> C compiler to work with > the latest code out of CVS. I've modified Makefile so that when you > build, it automatically assembles t/test1.pasm to t/test1.pbc, and > automatically runs the bytecode compiler "pbcc" to produce t/test1.c, > compiles and links the C file so that you can run t/test1. I get a > HUGE speedup (as would be expected) on my machine. > > BTW, I renamed t/test.pasm to t/test1.pasm because I wanted to name > the final output program the same as the base name of the source file, > and naming a program 'test' is a no-no. 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 \_____________________________________________________________________/
? pbcc.patch Index: MANIFEST =================================================================== RCS file: /home/perlcvs/parrot/MANIFEST,v retrieving revision 1.23 diff -a -u -r1.23 MANIFEST --- MANIFEST 2001/09/30 20:25:22 1.23 +++ MANIFEST 2001/10/03 12:17:19 @@ -4,6 +4,7 @@ Makefile.in Parrot/Assembler.pm Parrot/Opcode.pm +Parrot/OpFunc.pm Parrot/PackFile.pm Parrot/PackFile/ConstTable.pm Parrot/PackFile/Constant.pm @@ -53,6 +54,7 @@ opcode_table packfile.c parrot.c +pbcc pdump.c process_opfunc.pl register.c @@ -71,7 +73,7 @@ t/op/string.t t/op/time.t t/op/trans.t -t/test.pasm +t/test1.pasm t/test2.pasm t/test3.pasm test_c.in Index: Makefile.in =================================================================== RCS file: /home/perlcvs/parrot/Makefile.in,v retrieving revision 1.15 diff -a -u -r1.15 Makefile.in --- Makefile.in 2001/10/01 22:00:23 1.15 +++ Makefile.in 2001/10/03 12:17:19 @@ -22,7 +22,7 @@ .c$(O): $(CC) $(CFLAGS) -o $@ -c $< -all : $(TEST_PROG) $(PDUMP) +all : $(TEST_PROG) $(PDUMP) t/test1${exe} #XXX This target is not portable to Win32 shared: libparrot.so @@ -35,6 +35,18 @@ $(PDUMP): pdump$(O) $(O_FILES) $(CC) $(CFLAGS) -o $(PDUMP) $(O_FILES) pdump$(O) $(C_LIBS) +t/test1.pbc: t/test1.pasm + cd t; make test1.pbc + +t/test1.c: t/test1.pbc + pbcc t/test1.pbc > t/test1.c + +t/test1$(O): t/test1.c + $(CC) -o t/test1$(O) -c -I ./include t/test1.c + +t/test1${exe}: t/test1$(O) $(O_FILES) + $(CC) $(CFLAGS) -o t/test1${exe} $(O_FILES) t/test1$(O) $(C_LIBS) + test_main$(O): $(H_FILES) global_setup$(O): $(H_FILES) @@ -75,3 +87,4 @@ update: cvs -q update -dP + Index: pbcc =================================================================== RCS file: pbcc diff -N pbcc --- /dev/null Wed Oct 3 03:04:34 2001 +++ pbcc Wed Oct 3 05:17:19 2001 @@ -0,0 +1,327 @@ +#! /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; + +use Data::Dumper; +$Data::Dumper::Useqq = 1; +$Data::Dumper::Terse = 1; +$Data::Dumper::Indent = 0; + +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; + struct Parrot_Interp * interpreter; + struct PackFile_Constant * c; + struct PackFile * pf; + + init_world(); + + interpreter = make_interpreter(); + pf = PackFile_new(); + + interpreter->code = pf; + +END_C + + for(my $i = 0; $i < $nconst; $i++) { + my $const = $pf->const_table->constant($i); + + if ($const->type eq Parrot::PackFile::Constant::type_code('PFC_INTEGER')) +{ # TODO: Don't hardocde these codes. + print <<END_C; + c = PackFile_Constant_new_integer($const->value); +END_C + } elsif ($const->type eq Parrot::PackFile::Constant::type_code('PFC_NUMBER')) { +# TODO: Don't hardocde these codes. + print <<END_C; + c = PackFile_Constant_new_number($const->value); +END_C + } elsif ($const->type eq Parrot::PackFile::Constant::type_code('PFC_STRING')) { +# TODO: Don't hardocde these codes. + my $type = $const->value->type; + my $encoding = $const->value->encoding; + my $size = $const->value->size; + my $flags = $const->value->flags; + my $data = Dumper($const->value->data); + + $data = '"' . $data . '"' unless $data =~ m/^"/; + + print <<END_C; + c = PackFile_Constant_new_string(string_make($data, $size, $encoding, $flags, +$type)); +END_C + } else { + die; + } + + print <<END_C; + PackFile_ConstTable_push_constant(pf->const_table, c); + +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. + Index: Parrot/OpFunc.pm =================================================================== RCS file: OpFunc.pm diff -N OpFunc.pm --- /dev/null Wed Oct 3 03:04:34 2001 +++ OpFunc.pm Wed Oct 3 05:17:20 2001 @@ -0,0 +1,226 @@ +#! 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 => 1, + 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 { "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 { "{{\@" . $count++ . "}}"; } @{$opcodes{$name}{TYPES}}); + next; + } + + s/RETVAL/goto {{+=$op_size}}/; + + s/RETURN\(0\);/goto {{0}};/; + + s/RETURN\((.*)\)/goto {{+=$1}}/; + + s/INT_REG\(([^)]+)\)/interpreter->int_reg->registers[$1]/g; + s/STR_REG\(([^)]+)\)/interpreter->string_reg->registers[$1]/g; + s/PMC_REG\(([^)]+)\)/interpreter->pmc_reg->registers[$1]/g; + s/NUM_REG\(([^)]+)\)/interpreter->num_reg->registers[$1]/g; + + s/NUM_CONST\(([^)]+)\)/interpreter->code->const_table->constants[$1]->number/g; + s/STR_CONST\(([^)]+)\)/interpreter->code->const_table->constants[$1]->string/g; + s/INT_CONST\(([^)]+)\)/interpreter->code->const_table->constants[$1]->string/g; + + 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; + Index: Parrot/Opcode.pm =================================================================== RCS file: /home/perlcvs/parrot/Parrot/Opcode.pm,v retrieving revision 1.9 diff -a -u -r1.9 Opcode.pm --- Parrot/Opcode.pm 2001/10/02 14:01:30 1.9 +++ Parrot/Opcode.pm 2001/10/03 12:17:20 @@ -115,89 +115,4 @@ a version of parrot supporting the appropriate opcodes. =cut -package Parrot::Opcode; -use strict; -use Symbol; - -sub read_ops { - my $file = @_ ? shift : "opcode_table"; - - my $fh = gensym; - open $fh, $file or die "$file: $!\n"; - - my %opcode; - my $count = 1; - while (<$fh>) { - s/#.*//; - s/^\s+//; - chomp; - next unless $_; - - my($name, @params) = split /\s+/; - if (@params && $params[0] =~ /^\d+$/) { - my $count = shift @params; - die "$file, line $.: opcode $name parameters don't match count\n" - if ($count != @params); - } - - warn "$file, line $.: opcode $name redefined\n" if $opcode{$name}; - - $opcode{$name}{ARGS} = @params; - $opcode{$name}{TYPES} = \@params; - $opcode{$name}{CODE} = ($name eq "end") ? 0 : $count++; - $opcode{$name}{FUNC} = "Parrot_op_$name"; - - my $num_i = () = grep {/i/} @params; - my $num_n = () = grep {/n/} @params; - $opcode{$name}{RETURN_OFFSET} = 1 + $num_i + $num_n * $nvivsize; - } - - return %opcode; -} - -1; - - -__END__ - -=head1 NAME - -Parrot::Opcode - Read opcode definitions - -=head1 SYNOPSIS - - use Parrot::Opcode; - - %opcodes = Parrot::Opcode::read_ops(); - -=head1 DESCRIPTION - -The read_ops() function parses the Parrot opcode_table file, and -returns the contents as a hash. The hash key is the opcode name; -values are hashrefs containing the following fields: - -=over - -=item CODE - -The opcode number. - -=item ARGS - -The opcode argument count. - -=item TYPES - -The opcode argument types, as an arrayref. - -=item FUNC - -The name of the C function implementing this op. - -=back - -read_ops() takes an optional argument: the file to read the opcode table -from. - -=cut Index: t/.cvsignore =================================================================== RCS file: /home/perlcvs/parrot/t/.cvsignore,v retrieving revision 1.1 diff -a -u -r1.1 .cvsignore --- t/.cvsignore 2001/09/13 07:23:59 1.1 +++ t/.cvsignore 2001/10/03 12:17:20 @@ -1,2 +1,5 @@ *.pbc *.out +test1.c +test1.o +test1 Index: t/test.pasm =================================================================== RCS file: test.pasm diff -N test.pasm --- /tmp/cvsnQ6eRsJocC Wed Oct 3 05:17:20 2001 +++ /dev/null Wed Oct 3 03:04:34 2001 @@ -1,55 +0,0 @@ - time_i I1 - set_i_ic I2, 0 - set_i_ic I3, 1 - set_i_ic I4, 100000000 - set_s_sc S0, "\nIterations: " - print_s S0 - print_i I4 -REDO: eq_i_ic I2, I4, DONE - add_i I2, I2, I3 - branch_ic REDO -DONE: time_i I5 - set_s_sc S0, "\nStart time: " - print_s S0 - print_i I1 - set_s_sc S0, "\nEnd time: " - print_s S0 - print_i I5 - - set_s_sc S0, "\nCount: " - print_s S0 - print_i I2 - - set_s_sc S0, "\nElapsed time:" - print_s S0 - sub_i I2, I5, I1 - print_i I2 - - set_i_ic I1, 3 - mul_i I4, I4, I1 - iton_n_i N1, I4 - iton_n_i N2, I2 - set_s_sc S0, "\nEstimated ops:" - print_s S0 - print_i I4 - - set_s_sc S0, "\nEstimated ops (numerically):" - print_s S0 - print_n N1 - - set_s_sc S0, "\nElapsed time:" - print_s S0 - print_i I2 - - set_s_sc S0, "\nElapsed time:" - print_s S0 - print_n N2 - - div_n N1, N1, N2 - set_s_sc S0, "\nOps/sec:" - print_s S0 - print_n N1 - - set_s_sc S0, "\n" - print_s S0 - end Index: t/test1.pasm =================================================================== RCS file: test1.pasm diff -N test1.pasm --- /dev/null Wed Oct 3 03:04:34 2001 +++ test1.pasm Wed Oct 3 05:17:20 2001 @@ -0,0 +1,55 @@ + time_i I1 + set_i_ic I2, 0 + set_i_ic I3, 1 + set_i_ic I4, 100000000 + set_s_sc S0, "\nIterations: " + print_s S0 + print_i I4 +REDO: eq_i_ic I2, I4, DONE + add_i I2, I2, I3 + branch_ic REDO +DONE: time_i I5 + set_s_sc S0, "\nStart time: " + print_s S0 + print_i I1 + set_s_sc S0, "\nEnd time: " + print_s S0 + print_i I5 + + set_s_sc S0, "\nCount: " + print_s S0 + print_i I2 + + set_s_sc S0, "\nElapsed time:" + print_s S0 + sub_i I2, I5, I1 + print_i I2 + + set_i_ic I1, 3 + mul_i I4, I4, I1 + iton_n_i N1, I4 + iton_n_i N2, I2 + set_s_sc S0, "\nEstimated ops:" + print_s S0 + print_i I4 + + set_s_sc S0, "\nEstimated ops (numerically):" + print_s S0 + print_n N1 + + set_s_sc S0, "\nElapsed time:" + print_s S0 + print_i I2 + + set_s_sc S0, "\nElapsed time:" + print_s S0 + print_n N2 + + div_n N1, N1, N2 + set_s_sc S0, "\nOps/sec:" + print_s S0 + print_n N1 + + set_s_sc S0, "\n" + print_s S0 + end