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");
}

Reply via email to