This patch adds the as2jit.pl script, and a couple example files:
jit/i386/as/Parrot_mul_i_i_i.as
and
jit/i386/as/Parrot_time_n.as
This script takes an AT&T format assembly source file that can also
include jit instructions, and assembles it to the .jit file format.
It is not written to be x86 specific, but may well be as I haven't
tested it anywhere else so far.
If you run:
as2jit.pl jit/i386/as/*.as
You will get:
Parrot_mul_i_i_i: \xa1 &INT_REG[2] \x0f \xaf \x05 &INT_REG[3] \xa3
&INT_REG[1]
Parrot_time_n: SYSCALL(GETTIMEOFDAY, 2, V&TEMP_INT[0] V&TEMP_INT[2])
\xdb \x05 &TEMP_INT[0] \xdb \x05 &TEMP_INT[1] \xdd \x05 &CONST_FLOAT[0]
\xde \xf9\xde \xc1\xdd \x1d &NUM_REG[1]
Which is what you would see in jit/i386/core.jit.
This is of course very experimental.
I'll also put together something that goes in the other way, to dump
..jit files to assembly.
--
Rafael Kitover
diff -ruN -ruN parrot.orig/as2jit.pl parrot/as2jit.pl
--- parrot.orig/as2jit.pl Wed Dec 31 16:00:00 1969
+++ parrot/as2jit.pl Fri Dec 28 05:07:03 2001
@@ -0,0 +1,327 @@
+#!/usr/bin/perl
+
+=head1 NAME
+
+as2jit.pl -- Convert .as files into a .jit file for Parrot.
+
+=head1 SYNOPSIS
+
+as2jit.pl [-d] *.as > ops.jit
+
+=head1 DESCRIPTION
+
+Prototype Parrot .jit file assembler using "as" and "objdump" from GNU
+binutils (other versions might work too).
+
+Write a bunch of "foo.as" AT&T syntax assembly files, where "foo" is the
+fully qualified operation for Parrot you want to implement, then run
+C<as2jit.pl *.as E<gt> whatever.jit>.
+
+=head1 OPTIONS
+
+=over 4
+
+=item B<-d, --debug>
+
+Spew extra debugging info to STDERR.
+
+=item B<-h, --help>
+
+See this page.
+
+=back
+
+=cut
+
+$VERSION = "0.01";
+
+use strict;
+use warnings;
+use Getopt::Long;
+use IO::File;
+use Data::Dumper;
+use File::Basename;
+use Parrot::Config;
+
+my $debug = 0;
+BEGIN { GetOptions (
+ 'debug|d+' => \$debug,
+ 'h|help' => sub { exec "perldoc $0" }
+) }
+use constant DEBUG => $debug || 0;
+
+use constant TRUE => 1;
+use constant FALSE => 0;
+
+use constant TMP_OBJ => ".$$.o";
+use constant TMP_AS => ".$$.as";
+
+use constant INSTR_IDX => 0;
+use constant VALID_OPERAND => qr/^[\$\%(\d]/;
+use constant COMMENT => qr/^\s*[;!#|]/;
+
+use constant DUMMY_ARG => '($0x0)';
+use constant DUMMY_INSTR => 'nop';
+
+use constant OBJDUMP => "objdump -w -d";
+use constant AS => "as";
+
+sub parse($);
+sub assemble($$);
+sub write_as($$);
+sub disassemble($);
+sub jit_code($$$);
+sub usage;
+
+my @files = @ARGV;
+
+grep { -e } @files or usage, exit 2;
+
+for my $file (@files) {
+ my $operation = basename($file, ".as");
+
+ my $assembly = parse $file;
+
+ write_as $assembly->{dummy} => TMP_AS;
+
+ assemble TMP_AS, TMP_OBJ;
+
+ my $disassembly = disassemble TMP_OBJ;
+
+ print jit_code($operation, $assembly, $disassembly), "\n\n";
+}
+
+exit 0;
+
+###############################################################################
+
+sub assemble($$) {
+ my ($file, $obj) = @_;
+
+ print STDERR "Assembling:\n\n", (new IO::File $file)->getlines, "\n\n"
+ if DEBUG;
+
+ system AS." $file -o $obj";
+ die AS." $file failed" if (($? >> 8) != 0);
+}
+
+sub parse($) {
+ my $file = shift;
+ my %assembly;
+
+ my $as = new IO::File $file
+ or die "Could not open $file: $!";
+
+ my $line = 0;
+
+ while (<$as>) {
+ chomp;
+
+# Skip blanks, directives and comments.
+ next if /^\s*$/ || /^\s*\./ || /@{[ COMMENT ]}/;
+
+ my ($instr, @args,
+ $dummy_instr, @dummy_args);
+
+ my (@tokens) = split /(?:\s|,)+/, $_;
+
+ $instr = shift @tokens;
+
+# JIT function calls of the form F(function, arg1 arg2...), S(), CALL(), C()...
+ if ($instr =~ /\(/) {
+ $instr = $_;
+ $dummy_instr = DUMMY_INSTR;
+ }
+ else {
+ $dummy_instr = $instr;
+
+# Now process args.
+ while (my $token = shift @tokens) {
+# integer, register, constant value...
+ if ($token =~ /@{[ VALID_OPERAND ]}/) {
+ push @args, $token;
+ push @dummy_args, $token;
+ }
+ else {
+# Other JIT-specific values, such as &TEMP_INT[0].
+ push @args, $token;
+ push @dummy_args, DUMMY_ARG;
+ }
+ }
+ }
+
+ $assembly{src}[$line] = [ $instr, @args ];
+ $assembly{dummy}[$line] = [ $dummy_instr, @dummy_args ];
+
+ $line++;
+ }
+
+ $assembly{max_line} = $line - 1;
+
+ return wantarray ? %assembly : \%assembly;
+}
+
+sub write_as($$) {
+ my ($code, $target) = @_;
+
+ my $out = new IO::File "> $target"
+ or die "Could not write to $target: $!";
+
+ for my $line (@$code) {
+ my ($instr, @args) = @$line;
+
+ print $out "$instr ", join (', ', @args), "\n";
+ }
+}
+
+sub disassemble($) {
+ my $obj = shift;
+
+ my @result;
+
+ print STDERR "Disassembly:\n\n" if DEBUG;
+
+ my $objdump = new IO::File OBJDUMP." -d $obj |"
+ or die "Could not run ".OBJDUMP." -d $obj: $!";
+
+ while (<$objdump>) { last if /<\.text>:$/ }
+
+ while (<$objdump>) {
+ my ($opcodes, $instr, $args) =
+ /^\s* \w+: \s+ ( (?:\w\w\s)+ ) \s+ (\w+) \s+ (.+)/x;
+
+ my %instruction = (
+ opcodes => [ split /\s+/, $opcodes ],
+ instr => $instr,
+ args => [ split /,/, $args ]
+ );
+
+ push @result, \%instruction;
+
+ print STDERR $_ if DEBUG;
+ print STDERR Dumper(\%instruction) if DEBUG > 1;
+ }
+
+# The last instruction from objdump is bogus, for whatever reason...
+ pop @result;
+
+ print STDERR "\n\n" if DEBUG;
+
+ return wantarray ? @result : \@result;
+}
+
+sub jit_code($$$) {
+ my ($operation, $assembly, $code) = @_;
+
+# FIXME Does this make sense?
+ my $word_size = $PConfig{opcode_t_size};
+
+ my $result = "$operation: ";
+ my $src = $assembly->{src};
+ my $dummy = $assembly->{dummy};
+ my $line = 0;
+
+instr: for my $opcodes (map { $_->{opcodes} } @$code) {
+ my $src_instr = $src->[$line][INSTR_IDX];
+ my $src_ops = $src->[$line];
+ $line++;
+
+# Special instructions.
+ if ($src_instr =~ /\(/) {
+ $result .= $src_instr." ";
+ next;
+ }
+
+
+# Check if the last operand is an immediate zero. We only work with word-size
+# operands, and I don't know if all architectures have immediates as the last
+# word in the instruction.
+ if (exists $opcodes->[-$word_size]) {
+ my $is_zero = TRUE;
+
+is_zero: for my $byte (@$opcodes[-$word_size .. -1]) {
+ if ($byte != 0) {
+ $is_zero = FALSE; last IS_ZERO;
+ }
+ }
+
+ if ($is_zero) {
+ $result .= join ' ', (map { "\\x$_" }
+ @$opcodes[0 .. (@$opcodes - $word_size - 1)]);
+
+# Find the special arg, if it exists.
+ for my $src_op (@$src_ops[1 .. (@$src_ops - 1)]) {
+ if ($src_op !~ /@{[ VALID_OPERAND ]}/) {
+ $result .= " $src_op ";
+ next instr;
+ }
+ }
+
+# Otherwise it really is a zero immediate.
+ $result .= (' \x0' x $word_size).' ';
+ }
+ }
+ else {
+ $result .= join ' ', (map { "\\x$_" } @$opcodes);
+ }
+ }
+
+ return $result;
+}
+
+sub usage {
+ print STDERR <<EOF;
+Usage: $0 [OPTIONS] FILE.as ...
+Assemble files into JIT instructions.
+
+ Options:
+ -d,--debug Debugging output. If used multiple times will increase debug level.
+ -h,--help Help.
+
+EOF
+}
+
+END {
+ for (TMP_AS, TMP_OBJ) {
+ do { unlink $_
+ or die "Could not delete file $_: $!"
+ } if -e $_;
+ }
+}
+
+1;
+
+=head1 ASSUMPTIONS
+
+Assuming that an immediate operand is the last word in a machine code
+instruction, with word size being determined from
+C<$Parrot::Config::PConfig{opcode_t_size}>.
+
+All funny parameters such as C<&NUM_REG[1]> get replaced with a memory access
+of C<($0x0)> for the dummy assembly code.
+
+Calls such as C<F(...)>, C<S(...)> and such get replaced with a C<nop> for the
+dummy assembly code.
+
+The regex that reads objdump output in disassemble may not always work.
+
+When using objdump -d to disassemble small files compiled with as, the last
+instruction seems to be erroneously disassembled and is therefore ignored, this
+may be a bug in objdump.
+
+Use the -d option to see what is going on in the assembly/disassembly stages.
+
+=head1 LICENSE
+
+This program is free software; you can redistribute it and/or modify it under
+the same terms as Parrot.
+
+=head1 AUTHOR
+
+Rafael Kitover (c) 2000, 2001
+
+=head1 SEE ALSO
+
+The stuff in the parrot/docs directory, particularly jit.pod.
+
+=cut
diff -ruN -ruN parrot.orig/jit/i386/as/Parrot_mul_i_i_i.as
parrot/jit/i386/as/Parrot_mul_i_i_i.as
--- parrot.orig/jit/i386/as/Parrot_mul_i_i_i.as Wed Dec 31 16:00:00 1969
+++ parrot/jit/i386/as/Parrot_mul_i_i_i.as Fri Dec 28 05:07:25 2001
@@ -0,0 +1,3 @@
+mov &INT_REG[2],%eax
+imul &INT_REG[3],%eax
+mov %eax,&INT_REG[1]
diff -ruN -ruN parrot.orig/jit/i386/as/Parrot_time_n.as
parrot/jit/i386/as/Parrot_time_n.as
--- parrot.orig/jit/i386/as/Parrot_time_n.as Wed Dec 31 16:00:00 1969
+++ parrot/jit/i386/as/Parrot_time_n.as Fri Dec 28 05:07:25 2001
@@ -0,0 +1,7 @@
+SYSCALL(GETTIMEOFDAY, 2, V&TEMP_INT[0] V&TEMP_INT[2])
+fildl &TEMP_INT[0]
+fildl &TEMP_INT[1]
+fldl &CONST_FLOAT[0]
+fdivrp %st,%st(1)
+faddp %st,%st(1)
+fstpl &NUM_REG[1]