On Thu, Sep 20, 2001 at 12:15:37AM -0400, Michael Fischer wrote:
> please see attached process_switch.pl
> notes inside.
I've been working on the same thing. For comparison, I'm attaching
generate.pl, my replacement for build_interp_starter.pl,
process_opfuncs.pl, and make_op_header.pl. (I agree that the
various generators should share more code; my solution was to unify
them.)
generate.pl takes a list of opcode definition files on the command
line. It writes the interp_guts.h and op.h headers, as well as .c
files for all opcode definition files. It takes an optional -d
argument specifying the type of dispatch to use: valid values are
"function" and "switch".
There are some rough spots, but it appears to work overall.
> Made it work, Gibbs has seen patch, but we wanted to
> defer to Dan/Simon because op.h has the knack of
> redefining every op as Parrot_op_foo. :-(
This is necessary to avoid namespace pollution. In particular, the
"end" op will cause everything to go pear-shaped on FreeBSD without
mangling. (Personally, I'd rather we didn't use the defines at all,
and just referred to the functions as Parrot_op_foo; after all, only
generated code should ever call these functions.)
> Gibbs and I thought that the build_interp_starter
> and process_opfuncs perl files ought to be able
> to share more code, particularly since this submission
> is practically a copy of process_opfuncs, and the
> enum is trivially done inside build_interp_starter.
> However, we have not come to any decision on _where_
> the shared code should go. Suggestions solicited.
Whether my generate.pl is used or not, I think that unifying
generation into a single program is the right approach.
> One bug: I can't quite deal gracefully
> with the #includes, I just nuke 'em and write them back,
> but the multi-line C-comment block at the top of
> basic_opcodes.ops is a pain. Eliminating it clobbers
> everything with a STRING *....
I'm not at all certain what to do with things outside the opcodes
themselves. The .ops => .c conversion was clearly originally
concieved as translating one file into another. In order to dispatch
ops via a switch, you need to pull out only the function contents;
this makes the .ops file into a definition of function code only...the
remainder of the file gets tossed by the wayside.
- Damien
#!/usr/bin/perl -w
use strict;
use Parrot::Opcode;
use Getopt::Std;
use Symbol;
####################
# Arguments.
sub usage {
print STDERR "Usage: $0 -d <function | switch> [<file> ...]\n";
exit 1;
}
my %opts = (d => "function");
getopts("d:", \%opts) or usage;
if ($opts{d} ne "function" && $opts{d} ne "switch") {
print STDERR "Supported dispatch modes: function, switch.\n";
exit 1;
}
####################
# Opcodes.
my $opcode_fingerprint = Parrot::Opcode::fingerprint();
my %opcodes = Parrot::Opcode::read_ops();
my @opcodes;
for my $name (keys %opcodes) {
my $op = $opcodes{$name};
push @opcodes, $op;
$op->{NAME} = $name;
}
@opcodes = sort { $a->{CODE} <=> $b->{CODE} } @opcodes;
my %files;
for my $f (@ARGV) {
$files{$f} = [ read_ops($f) ];
}
####################
# quoted() is used to simplify generation. The leading regex /\s*#/ is
# stripped from text, to allow here-docs to be set off from the surrounding
# Perl code. Lines beginning with /\s*\#\*/ are printed once for every
# opcode. Text surrounded in curly braces, like {THIS}, is replaced with
# the value of the appropriate field in the opcode definition.
sub quoted {
my $s = "";
for (split /\n/, $_[0]) {
if (s/^\s*\#\*//) {
for my $op (@opcodes) {
my $t = $_; $t =~ s/{(\w+)}/$op->{$1}/ge;
$s .= "$t\n";
}
}
elsif (s/^\s*\# ?//) {
$s .= "$_\n";
}
}
$s;
}
####################
# op.h
open OP_H, "> include/parrot/op.h"
or die "include/parrot/op.h: $!\n";
print OP_H quoted(<<END)
# /*
# * op.h
# * Opcode header.
# * This file is autogenerated by generate.pl -- DO NOT EDIT.
# */
#
# #if !defined(PARROT_OP_H_GUARD)
# #define PARROT_OP_H_GUARD
#
# typedef IV OP;
#
# #define DEFAULT_OPCODE_TABLE NULL
#
END
;
if ($opts{d} eq "function") {
print OP_H quoted(<<END)
#*#define {NAME} Parrot_op_{NAME}
#
#*opcode_t *{NAME}(opcode_t *, struct Parrot_Interp *);
END
;
}
print OP_H quoted(<<END)
#
# #endif
END
;
####################
# interp_guts.h
open INTERP, "> include/parrot/interp_guts.h"
or die "include/parrot/interp_guts.h: $!\n";
print INTERP quoted(<<END)
# /*
# * interp_guts.h
# *
# * This file is autogenerated by generate.pl -- DO NOT EDIT.
# */
#
# #define BUILD_TABLE(x) do { \\
END
;
for my $op (@opcodes) {
if ($opts{d} eq "function") {
print INTERP "\tx[$op->{CODE}] = (void*)$op->{NAME}; \\\n";
} else {
print INTERP "\tx[$op->{CODE}] = NULL; \\\n";
}
}
print INTERP quoted(<<END)
# } while (0);
#
# #define BUILD_NAME_TABLE(x) do { \\
#* x[{CODE}] = \"{NAME}\"; \\
# } while (0);
#
# #define BUILD_ARG_TABLE(x) do { \\
#* x[{CODE}] = {ARGS}; \\
# } while(0);
#
END
;
if ($opts{d} eq "function") {
print INTERP quoted(<<END)
# #define DO_OP(code, temp, func, interp) do { \\
# temp = (void *)interp->opcode_funcs; \\
# func = (opcode_t* (*)())temp[code->i]; \\
# code = (func)(code, interp); \\
# } while(0);
END
;
} elsif ($opts{d} eq "switch") {
print INTERP "#define DO_OP(cur_opcode, temp, func, interp) do { \\\n";
print INTERP " switch (cur_opcode->i) { \\\n";
for my $op (@opcodes) {
if (defined $op->{IMPL_BODY}) {
my $body = $op->{IMPL_BODY};
$body =~ s/RETVAL/return_offset/g;
$body =~ s/RETURN\(0\);/;/g;
$body =~ s/RETURN\((.*)\)/cur_opcode = cur_opcode + $1; break/g;
print INTERP " case $op->{CODE}: { \\\n";
for (split /\n/, $body) {
print INTERP "$_ \\\n";
}
print INTERP " cur_opcode += $op->{IMPL_RETURN_ADDR}; } \\\n";
print INTERP " break; \\\n";
}
}
print INTERP " default: \\\n";
print INTERP " exit(1); /* XXX: Better error trapping */ \\\n";
print INTERP " } } while(0)\n";
}
print INTERP quoted(<<END)
#
# #define OPCODE_FINGERPRINT "$opcode_fingerprint"
END
;
close INTERP;
####################
# Generate opcode files.
for my $f (@ARGV) {
my $output = $f;
$output =~ s/(\.ops)?$/.c/;
open OUTPUT, "> $output" or die "$output: $!\n";
print OUTPUT quoted(<<END)
# /*
# * !!!!!!! DO NOT EDIT THIS FILE !!!!!!!
# * This file is automatically generated from $f.
# * Edit it instead.
# */
#
# #include "parrot/parrot.h"
# #include <math.h>
#
END
;
if ($opts{d} ne "function") {
close OUTPUT;
next;
}
for my $name (@{$files{$f}}) {
my $op = $opcodes{$name};
my $body = $op->{IMPL_BODY};
$body =~ s/RETVAL/return_offset/g;
$body =~ s/RETURN\(0\);/return 0;/g;
$body =~ s/RETURN\((.*)\)/return cur_opcode + $1/g;
print OUTPUT "#line $op->{IMPL_LINE} \"$op->{IMPL_FILE}\"\n";
print OUTPUT ("opcode_t *$op->{FUNC}(".
"opcode_t cur_opcode[], ".
"struct Parrot_Interp *interpreter".
") {\n");
print OUTPUT $body;
print OUTPUT " return cur_opcode + $op->{IMPL_RETURN_ADDR};\n}\n\n";
}
close OUTPUT;
}
####################
# Read opcode function definitions.
#
# 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 ...
#
# RETVAL = x;
#
# }
#
# There may be more than one RETVAL
#
# 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
sub read_ops {
my($file) = @_;
open INPUT, $file or die "$file: $!\n";
my @ops;
my($name, $body, $footer, @param_sub);
while (<INPUT>) {
if (/^AUTO_OP/) {
$body = "";
($name, $footer) = auto_code($_);
}
if (/^MANUAL_OP/) {
$body = " IV return_offset = 1;\n";
($name, $footer) = manual_code($_);
}
if (/^(AUTO|MANUAL)_OP/) {
push @ops, $name;
if (defined $opcodes{$name}{IMPL_FILE}) {
print STDERR "Warning: $name implemented multiple times:\n";
print STDERR " $opcodes{$name}{IMPL_FILE}, ",
"line $opcodes{$name}{IMPL_LINE}\n";
print STDERR " $file, line $.\n";
}
$opcodes{$name}{IMPL_FILE} = $file;
$opcodes{$name}{IMPL_LINE} = $.;
my $count = 1;
@param_sub = ("",
map {if ($_ eq "n") {
my $temp = '*(NV *)&cur_opcode['.$count.']';
$count += 2;
$temp;
} else {
"cur_opcode[" . $count++ . "].i"
}
} @{$opcodes{$name}{TYPES}});
next;
}
s/\bP(\d+)\b/$param_sub[$1]/g;
if (/^\}/) {
$opcodes{$name}{IMPL_BODY} = $body;
$opcodes{$name}{IMPL_RETURN_ADDR} = $footer;
$name = undef;
$body = undef;
$footer = undef;
@param_sub = ();
}
if (defined $body) {
$body .= $_;
}
}
close INPUT;
return @ops;
}
my %psize;
BEGIN {
%psize = (i => 1,
n => 2,
I => 1,
N => 1,
D => 1,
S => 1,
s => 1,
);
}
sub auto_code {
my($line) = @_;
my($name) = $line =~ /^AUTO_OP\s+(\w+)/;
die "$name: unknown opcode\n" unless $opcodes{$name};
my $psize = 0;
foreach (@{$opcodes{$name}{TYPES}}) {
$psize+=$psize{$_};
}
my $return_offset = $psize + 1;
return($name, $return_offset);
}
sub manual_code {
my($line) = @_;
my($name) = $line =~ /^MANUAL_OP\s+(\w+)/;
die "$name: unknown opcode\n" unless $opcodes{$name};
return($name, "return_offset");
}