This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Initial check-in of perl compiler.
authorMalcolm Beattie <mbeattie@sable.ox.ac.uk>
Sat, 3 May 1997 14:47:06 +0000 (14:47 +0000)
committerMalcolm Beattie <mbeattie@sable.ox.ac.uk>
Sat, 3 May 1997 14:47:06 +0000 (14:47 +0000)
p4raw-id: //depot/perlext/Compiler@10

61 files changed:
Artistic [new file with mode: 0644]
B.pm [new file with mode: 0644]
B.xs [new file with mode: 0644]
B/Asmdata.pm [new file with mode: 0644]
B/Assembler.pm [new file with mode: 0644]
B/Bblock.pm [new file with mode: 0644]
B/Bytecode.pm [new file with mode: 0644]
B/C.pm [new file with mode: 0644]
B/CC.pm [new file with mode: 0644]
B/Debug.pm [new file with mode: 0644]
B/Disassembler.pm [new file with mode: 0644]
B/Showlex.pm [new file with mode: 0644]
B/Stackobj.pm [new file with mode: 0644]
B/Terse.pm [new file with mode: 0644]
B/Xref.pm [new file with mode: 0644]
Copying [new file with mode: 0644]
Makefile.PL [new file with mode: 0644]
NOTES [new file with mode: 0644]
O.pm [new file with mode: 0644]
README [new file with mode: 0644]
TESTS [new file with mode: 0644]
TESTS.alpha2 [new file with mode: 0644]
Todo [new file with mode: 0644]
assemble [new file with mode: 0755]
bytecode.h [new file with mode: 0644]
bytecode.pl [new file with mode: 0644]
byteperl.c [new file with mode: 0644]
byterun.c [new file with mode: 0644]
byterun.h [new file with mode: 0644]
cc_harness [new file with mode: 0644]
cc_runtime.h [new file with mode: 0644]
ccop.c [new file with mode: 0644]
ccop.h [new file with mode: 0644]
disassemble [new file with mode: 0755]
old/README.feb11 [new file with mode: 0644]
old/TESTS.mar11 [new file with mode: 0644]
old/TESTS.mar20 [new file with mode: 0644]
old/TESTS.may11 [new file with mode: 0644]
old/TESTS.pre-jul27 [new file with mode: 0644]
op.patch [new file with mode: 0644]
ramblings/cc.notes [new file with mode: 0644]
ramblings/curcop.runtime [new file with mode: 0644]
ramblings/dontparse.c [new file with mode: 0644]
ramblings/flip-flop [new file with mode: 0644]
ramblings/foo.bench [new file with mode: 0644]
ramblings/foo2.bench [new file with mode: 0644]
ramblings/foo3.bench [new file with mode: 0644]
ramblings/magic [new file with mode: 0644]
ramblings/pp_i_add [new file with mode: 0644]
ramblings/reg.alloc [new file with mode: 0644]
ramblings/runtime.porting [new file with mode: 0644]
ramblings/sort.notes [new file with mode: 0644]
ramblings/sub.call [new file with mode: 0644]
ramblings/subst.notes [new file with mode: 0644]
run_bytecode_test [new file with mode: 0755]
run_cc_test [new file with mode: 0755]
run_test [new file with mode: 0755]
test_harness [new file with mode: 0755]
test_harness_bytecode [new file with mode: 0755]
test_harness_cc [new file with mode: 0755]
typemap [new file with mode: 0644]

diff --git a/Artistic b/Artistic
new file mode 100644 (file)
index 0000000..11f4d82
--- /dev/null
+++ b/Artistic
@@ -0,0 +1,131 @@
+
+
+
+
+                        The "Artistic License"
+
+                               Preamble
+
+The intent of this document is to state the conditions under which a
+Package may be copied, such that the Copyright Holder maintains some
+semblance of artistic control over the development of the package,
+while giving the users of the package the right to use and distribute
+the Package in a more-or-less customary fashion, plus the right to make
+reasonable modifications.
+
+Definitions:
+
+       "Package" refers to the collection of files distributed by the
+       Copyright Holder, and derivatives of that collection of files
+       created through textual modification.
+
+       "Standard Version" refers to such a Package if it has not been
+       modified, or has been modified in accordance with the wishes
+       of the Copyright Holder as specified below.
+
+       "Copyright Holder" is whoever is named in the copyright or
+       copyrights for the package.
+
+       "You" is you, if you're thinking about copying or distributing
+       this Package.
+
+       "Reasonable copying fee" is whatever you can justify on the
+       basis of media cost, duplication charges, time of people involved,
+       and so on.  (You will not be required to justify it to the
+       Copyright Holder, but only to the computing community at large
+       as a market that must bear the fee.)
+
+       "Freely Available" means that no fee is charged for the item
+       itself, though there may be fees involved in handling the item.
+       It also means that recipients of the item may redistribute it
+       under the same conditions they received it.
+
+1. You may make and give away verbatim copies of the source form of the
+Standard Version of this Package without restriction, provided that you
+duplicate all of the original copyright notices and associated disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications
+derived from the Public Domain or from the Copyright Holder.  A Package
+modified in such a way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided
+that you insert a prominent notice in each changed file stating how and
+when you changed that file, and provided that you do at least ONE of the
+following:
+
+    a) place your modifications in the Public Domain or otherwise make them
+    Freely Available, such as by posting said modifications to Usenet or
+    an equivalent medium, or placing the modifications on a major archive
+    site such as uunet.uu.net, or by allowing the Copyright Holder to include
+    your modifications in the Standard Version of the Package.
+
+    b) use the modified Package only within your corporation or organization.
+
+    c) rename any non-standard executables so the names do not conflict
+    with standard executables, which must also be provided, and provide
+    a separate manual page for each non-standard executable that clearly
+    documents how it differs from the Standard Version.
+
+    d) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or
+executable form, provided that you do at least ONE of the following:
+
+    a) distribute a Standard Version of the executables and library files,
+    together with instructions (in the manual page or equivalent) on where
+    to get the Standard Version.
+
+    b) accompany the distribution with the machine-readable source of
+    the Package with your modifications.
+
+    c) give non-standard executables non-standard names, and clearly
+    document the differences in manual pages (or equivalent), together
+    with instructions on where to get the Standard Version.
+
+    d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this
+Package.  You may charge any fee you choose for support of this
+Package.  You may not charge a fee for this Package itself.  However,
+you may distribute this Package in aggregate with other (possibly
+commercial) programs as part of a larger (possibly commercial) software
+distribution provided that you do not advertise this Package as a
+product of your own.  You may embed this Package's interpreter within
+an executable of yours (by linking); this shall be construed as a mere
+form of aggregation, provided that the complete Standard Version of the
+interpreter is so embedded.
+
+6. The scripts and library files supplied as input to or produced as
+output from the programs of this Package do not automatically fall
+under the copyright of this Package, but belong to whomever generated
+them, and may be sold commercially, and may be aggregated with this
+Package.  If such scripts or library files are aggregated with this
+Package via the so-called "undump" or "unexec" methods of producing a
+binary executable image, then distribution of such an image shall
+neither be construed as a distribution of this Package nor shall it
+fall under the restrictions of Paragraphs 3 and 4, provided that you do
+not represent such an executable image as a Standard Version of this
+Package.
+
+7. C subroutines (or comparably compiled subroutines in other
+languages) supplied by you and linked into this Package in order to
+emulate subroutines and variables of the language defined by this
+Package shall not be considered part of this Package, but are the
+equivalent of input as in Paragraph 6, provided these subroutines do
+not change the language in any way that would cause it to fail the
+regression tests for the language.
+
+8. Aggregation of this Package with a commercial distribution is always
+permitted provided that the use of this Package is embedded; that is,
+when no overt attempt is made to make this Package's interfaces visible
+to the end user of the commercial distribution.  Such use shall not be
+construed as a distribution of this Package.
+
+9. The name of the Copyright Holder may not be used to endorse or promote
+products derived from this software without specific prior written permission.
+
+10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+                               The End
diff --git a/B.pm b/B.pm
new file mode 100644 (file)
index 0000000..4a9a202
--- /dev/null
+++ b/B.pm
@@ -0,0 +1,203 @@
+#      B.pm
+#
+#      Copyright (c) 1996 Malcolm Beattie
+#
+#      You may distribute under the terms of either the GNU General Public
+#      License or the Artistic License, as specified in the README file.
+#
+package B;
+require DynaLoader;
+require Exporter;
+@ISA = qw(Exporter DynaLoader);
+@EXPORT_OK = qw(byteload_fh byteload_string minus_c ppname
+               class peekop cast_I32 ad cstring cchar hash
+               main_root main_start main_cv svref_2object
+               walkoptree walkoptree_exec walksymtable
+               comppadlist sv_undef compile_stats timing_info);
+
+use strict;
+@B::SV::ISA = 'B::OBJECT';
+@B::NULL::ISA = 'B::SV';
+@B::PV::ISA = 'B::SV';
+@B::IV::ISA = 'B::SV';
+@B::NV::ISA = 'B::IV';
+@B::RV::ISA = 'B::SV';
+@B::PVIV::ISA = qw(B::PV B::IV);
+@B::PVNV::ISA = qw(B::PV B::NV);
+@B::PVMG::ISA = 'B::PVNV';
+@B::PVLV::ISA = 'B::PVMG';
+@B::BM::ISA = 'B::PVMG';
+@B::AV::ISA = 'B::PVMG';
+@B::GV::ISA = 'B::PVMG';
+@B::HV::ISA = 'B::PVMG';
+@B::CV::ISA = 'B::PVMG';
+@B::IO::ISA = 'B::CV';
+
+@B::OP::ISA = 'B::OBJECT';
+@B::UNOP::ISA = 'B::OP';
+@B::BINOP::ISA = 'B::UNOP';
+@B::LOGOP::ISA = 'B::UNOP';
+@B::CONDOP::ISA = 'B::UNOP';
+@B::LISTOP::ISA = 'B::BINOP';
+@B::SVOP::ISA = 'B::OP';
+@B::GVOP::ISA = 'B::OP';
+@B::PVOP::ISA = 'B::OP';
+@B::CVOP::ISA = 'B::OP';
+@B::LOOP::ISA = 'B::LISTOP';
+@B::PMOP::ISA = 'B::LISTOP';
+@B::COP::ISA = 'B::OP';
+
+@B::SPECIAL::ISA = 'B::OBJECT';
+
+{
+    # Stop "-w" from complaining about the lack of a real B::OBJECT class
+    package B::OBJECT;
+}
+
+my $debug;
+my $op_count = 0;
+
+sub debug {
+    my ($class, $value) = @_;
+    $debug = $value;
+}
+
+# sub OPf_KIDS;
+# add to .xs for perl5.002
+sub OPf_KIDS () { 4 }
+
+sub ad {
+    my $obj = shift;
+    return $$obj;
+}
+
+sub class {
+    my $obj = shift;
+    my $name = ref $obj;
+    $name =~ s/^.*:://;
+    return $name;
+}
+
+# For debugging
+sub peekop {
+    my $op = shift;
+    return sprintf("%s (0x%x) %s", class($op), $$op, $op->ppaddr);
+}
+
+sub walkoptree {
+    my($op, $method, $level) = @_;
+    $op_count++; # just for statistics
+    $level ||= 0;
+    warn(sprintf("walkoptree: %d. %s\n", $level, peekop($op))) if $debug;
+    $op->$method($level);
+    if (ad($op) && ($op->flags & OPf_KIDS)) {
+       my $kid;
+       for ($kid = $op->first; $$kid; $kid = $kid->sibling) {
+           walkoptree($kid, $method, $level + 1);
+       }
+    }
+}
+
+sub compile_stats {
+    return "Total number of OPs processed: $op_count\n";
+}
+
+sub timing_info {
+    my ($sec, $min, $hr) = localtime;
+    my ($user, $sys) = times;
+    sprintf("%02d:%02d:%02d user=$user sys=$sys",
+           $hr, $min, $sec, $user, $sys);
+}
+
+my %symtable;
+sub savesym {
+    my ($obj, $value) = @_;
+#    warn(sprintf("savesym: sym_%x => %s\n", ad($obj), $value)); # debug
+    $symtable{sprintf("sym_%x", ad($obj))} = $value;
+}
+
+sub objsym {
+    my $obj = shift;
+    return $symtable{sprintf("sym_%x", ad($obj))};
+}
+
+sub walkoptree_exec {
+    my ($op, $method, $level) = @_;
+    my ($sym, $ppname);
+    my $prefix = "    " x $level;
+    for (; $$op; $op = $op->next) {
+       $sym = objsym($op);
+       if (defined($sym)) {
+           print $prefix, "goto $sym\n";
+           return;
+       }
+       savesym($op, sprintf("%s (0x%lx)", class($op), ad($op)));
+       $op->$method($level);
+       $ppname = $op->ppaddr;
+       if ($ppname =~ /^pp_(or|and|mapwhile|grepwhile|entertry)$/) {
+           print $prefix, uc($1), " => {\n";
+           walkoptree_exec($op->other, $method, $level + 1);
+           print $prefix, "}\n";
+       } elsif ($ppname eq "pp_match" || $ppname eq "pp_subst") {
+           my $pmreplstart = $op->pmreplstart;
+           if (ad($pmreplstart)) {
+               print $prefix, "PMREPLSTART => {\n";
+               walkoptree_exec($pmreplstart, $method, $level + 1);
+               print $prefix, "}\n";
+           }
+       } elsif ($ppname eq "pp_substcont") {
+           print $prefix, "SUBSTCONT => {\n";
+           walkoptree_exec($op->other->pmreplstart, $method, $level + 1);
+           print $prefix, "}\n";
+           $op = $op->other;
+       } elsif ($ppname eq "pp_cond_expr") {
+           # pp_cond_expr never returns op_next
+           print $prefix, "TRUE => {\n";
+           walkoptree_exec($op->true, $method, $level + 1);
+           print $prefix, "}\n";
+           $op = $op->false;
+           redo;
+       } elsif ($ppname eq "pp_range") {
+           print $prefix, "TRUE => {\n";
+           walkoptree_exec($op->true, $method, $level + 1);
+           print $prefix, "}\n", $prefix, "FALSE => {\n";
+           walkoptree_exec($op->false, $method, $level + 1);
+           print $prefix, "}\n";
+       } elsif ($ppname eq "pp_enterloop") {
+           print $prefix, "REDO => {\n";
+           walkoptree_exec($op->redoop, $method, $level + 1);
+           print $prefix, "}\n", $prefix, "NEXT => {\n";
+           walkoptree_exec($op->nextop, $method, $level + 1);
+           print $prefix, "}\n", $prefix, "LAST => {\n";
+           walkoptree_exec($op->lastop,  $method, $level + 1);
+           print $prefix, "}\n";
+       } elsif ($ppname eq "pp_subst") {
+           my $replstart = $op->pmreplstart;
+           if (ad($replstart)) {
+               print $prefix, "SUBST => {\n";
+               walkoptree_exec($replstart, $method, $level + 1);
+               print $prefix, "}\n";
+           }
+       }
+    }
+}
+
+sub walksymtable {
+    my ($symref, $method, $recurse) = @_;
+    my $sym;
+    no strict 'vars';
+    local(*glob);
+    while (($sym, *glob) = each %$symref) {
+       if ($sym =~ /::$/) {
+           if ($sym ne "main::" && &$recurse($sym)) {
+               walksymtable(\%glob, $method, $recurse);
+           }
+       } else {
+           svref_2object(\*glob)->EGV->$method();
+       }
+    }
+}
+
+bootstrap B;
+
+1;
diff --git a/B.xs b/B.xs
new file mode 100644 (file)
index 0000000..a89b530
--- /dev/null
+++ b/B.xs
@@ -0,0 +1,1028 @@
+/*     B.xs
+ *
+ *     Copyright (c) 1996 Malcolm Beattie
+ *
+ *     You may distribute under the terms of either the GNU General Public
+ *     License or the Artistic License, as specified in the README file.
+ *
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+#include "INTERN.h"
+#include "bytecode.h"
+#include "byterun.h"
+#include "ccop.h"
+
+static char *svclassnames[] = {
+    "B::NULL",
+    "B::IV",
+    "B::NV",
+    "B::RV",
+    "B::PV",
+    "B::PVIV",
+    "B::PVNV",
+    "B::PVMG",
+    "B::BM",
+    "B::PVLV",
+    "B::AV",
+    "B::HV",
+    "B::CV",
+    "B::GV",
+    "B::FM",
+    "B::IO",
+};
+
+static SV *
+make_sv_object(arg, sv)
+SV *arg;
+SV *sv;
+{
+    char *type = 0;
+    IV iv;
+    
+    for (iv = 0; iv < sizeof(specialsv_list)/sizeof(SV*); iv++) {
+       if (sv == specialsv_list[iv]) {
+           type = "B::SPECIAL";
+           break;
+       }
+    }
+    if (!type) {
+       type = svclassnames[SvTYPE(sv)];
+       iv = (IV)sv;
+    }
+    sv_setiv(newSVrv(arg, type), iv);
+    return arg;
+}
+
+static SV *
+make_mg_object(arg, mg)
+SV *arg;
+MAGIC *mg;
+{
+    sv_setiv(newSVrv(arg, "B::MAGIC"), (IV)mg);
+    return arg;
+}
+
+static SV *
+cstring(sv)
+SV *sv;
+{
+    SV *sstr = newSVpv("", 0);
+    STRLEN len;
+    char *s;
+
+    if (!SvOK(sv))
+       sv_setpvn(sstr, "0", 1);
+    else
+    {
+       /* XXX Optimise? */
+       s = SvPV(sv, len);
+       sv_catpv(sstr, "\"");
+       for (; len; len--, s++)
+       {
+           /* At least try a little for readability */
+           if (*s == '"')
+               sv_catpv(sstr, "\\\"");
+           else if (*s == '\\')
+               sv_catpv(sstr, "\\\\");
+           else if (*s >= ' ' && *s < 127) /* XXX not portable */
+               sv_catpvn(sstr, s, 1);
+           else if (*s == '\n')
+               sv_catpv(sstr, "\\n");
+           else if (*s == '\r')
+               sv_catpv(sstr, "\\r");
+           else if (*s == '\t')
+               sv_catpv(sstr, "\\t");
+           else if (*s == '\a')
+               sv_catpv(sstr, "\\a");
+           else if (*s == '\b')
+               sv_catpv(sstr, "\\b");
+           else if (*s == '\f')
+               sv_catpv(sstr, "\\f");
+           else if (*s == '\v')
+               sv_catpv(sstr, "\\v");
+           else
+           {
+               /* no trigraph support */
+               char escbuff[5]; /* to fit backslash, 3 octals + trailing \0 */
+               /* Don't want promotion of a signed -1 char in sprintf args */
+               unsigned char c = (unsigned char) *s;
+               sprintf(escbuff, "\\%03o", c);
+               sv_catpv(sstr, escbuff);
+           }
+           /* XXX Add line breaks if string is long */
+       }
+       sv_catpv(sstr, "\"");
+    }
+    return sstr;
+}
+
+static SV *
+cchar(sv)
+SV *sv;
+{
+    SV *sstr = newSVpv("'", 0);
+    char *s = SvPV(sv, na);
+
+    if (*s == '\'')
+       sv_catpv(sstr, "\\'");
+    else if (*s == '\\')
+       sv_catpv(sstr, "\\\\");
+    else if (*s >= ' ' && *s < 127) /* XXX not portable */
+       sv_catpvn(sstr, s, 1);
+    else if (*s == '\n')
+       sv_catpv(sstr, "\\n");
+    else if (*s == '\r')
+       sv_catpv(sstr, "\\r");
+    else if (*s == '\t')
+       sv_catpv(sstr, "\\t");
+    else if (*s == '\a')
+       sv_catpv(sstr, "\\a");
+    else if (*s == '\b')
+       sv_catpv(sstr, "\\b");
+    else if (*s == '\f')
+       sv_catpv(sstr, "\\f");
+    else if (*s == '\v')
+       sv_catpv(sstr, "\\v");
+    else
+    {
+       /* no trigraph support */
+       char escbuff[5]; /* to fit backslash, 3 octals + trailing \0 */
+       /* Don't want promotion of a signed -1 char in sprintf args */
+       unsigned char c = (unsigned char) *s;
+       sprintf(escbuff, "\\%03o", c);
+       sv_catpv(sstr, escbuff);
+    }
+    sv_catpv(sstr, "'");
+    return sstr;
+}
+
+void *
+bset_obj_store(obj, ix)
+void *obj;
+I32 ix;
+{
+    if (ix > obj_list_fill) {
+       if (obj_list_fill == -1)
+           New(666, obj_list, ix + 1, void*);
+       else
+           Renew(obj_list, ix + 1, void*);
+       obj_list_fill = ix;
+    }
+    obj_list[ix] = obj;
+    return obj;
+}
+
+#ifdef INDIRECT_BGET_MACROS
+void freadpv(len, data)
+U32 len;
+void *data;
+{
+    New(666, pv.xpv_pv, len, char);
+    fread(pv.xpv_pv, 1, len, (FILE*)data);
+    pv.xpv_len = len;
+    pv.xpv_cur = len - 1;
+}
+
+void byteload_fh(fp)
+FILE *fp;
+{
+    struct bytestream bs;
+    bs.data = fp;
+    bs.fgetc = (int(*) _((void*)))fgetc;
+    bs.fread = (int(*) _((char*,size_t,size_t,void*)))fread;
+    bs.freadpv = freadpv;
+    byterun(bs);
+}
+
+static int fgetc_fromstring(data)
+void *data;
+{
+    char **strp = (char **)data;
+    return *(*strp)++;
+}
+
+static int fread_fromstring(argp, elemsize, nelem, data)
+char *argp;
+size_t elemsize;
+size_t nelem;
+void *data;
+{
+    char **strp = (char **)data;
+    size_t len = elemsize * nelem;
+    
+    memcpy(argp, *strp, len);
+    *strp += len;
+    return (int)len;
+}
+
+static void freadpv_fromstring(len, data)
+U32 len;
+void *data;
+{
+    char **strp = (char **)data;
+    
+    New(666, pv.xpv_pv, len, char);
+    memcpy(pv.xpv_pv, *strp, len);
+    pv.xpv_len = len;
+    pv.xpv_cur = len - 1;
+    *strp += len;
+}    
+
+void byteload_string(str)
+char *str;
+{
+    struct bytestream bs;
+    bs.data = &str;
+    bs.fgetc = fgetc_fromstring;
+    bs.fread = fread_fromstring;
+    bs.freadpv = freadpv_fromstring;
+    byterun(bs);
+}
+#else
+void byteload_fh(fp)
+FILE *fp;
+{
+    byterun(fp);
+}
+
+void byteload_string(str)
+char *str;
+{
+    croak("Must compile with -DINDIRECT_BGET_MACROS for byteload_string");
+}    
+#endif /* INDIRECT_BGET_MACROS */
+
+
+typedef OP     *B__OP;
+typedef UNOP   *B__UNOP;
+typedef BINOP  *B__BINOP;
+typedef LOGOP  *B__LOGOP;
+typedef CONDOP *B__CONDOP;
+typedef LISTOP *B__LISTOP;
+typedef PMOP   *B__PMOP;
+typedef SVOP   *B__SVOP;
+typedef GVOP   *B__GVOP;
+typedef PVOP   *B__PVOP;
+typedef LOOP   *B__LOOP;
+typedef COP    *B__COP;
+
+typedef SV     *B__SV;
+typedef SV     *B__IV;
+typedef SV     *B__PV;
+typedef SV     *B__NV;
+typedef SV     *B__PVMG;
+typedef SV     *B__PVLV;
+typedef SV     *B__BM;
+typedef SV     *B__RV;
+typedef AV     *B__AV;
+typedef HV     *B__HV;
+typedef CV     *B__CV;
+typedef GV     *B__GV;
+typedef IO     *B__IO;
+
+typedef MAGIC  *B__MAGIC;
+
+MODULE = B     PACKAGE = B     PREFIX = B_
+
+PROTOTYPES: DISABLE
+
+#define B_main_cv()    main_cv
+#define B_main_root()  main_root
+#define B_main_start() main_start
+#define B_comppadlist()        (main_cv ? CvPADLIST(main_cv) : CvPADLIST(compcv))
+#define B_sv_undef()   &sv_undef
+#define B_sv_yes()     &sv_yes
+#define B_sv_no()      &sv_no
+
+B::CV
+B_main_cv()
+
+B::OP
+B_main_root()
+
+B::OP
+B_main_start()
+
+B::AV
+B_comppadlist()
+
+B::SV
+B_sv_undef()
+
+B::SV
+B_sv_yes()
+
+B::SV
+B_sv_no()
+
+MODULE = B     PACKAGE = B
+
+
+void
+byteload_fh(fp)
+       FILE *  fp
+
+void
+byteload_string(str)
+       char *  str
+
+#define address(sv) (IV)sv
+
+IV
+address(sv)
+       SV *    sv
+
+B::SV
+svref_2object(sv)
+       SV *    sv
+    CODE:
+       if (!SvROK(sv))
+           croak("argument is not a reference");
+       RETVAL = (SV*)SvRV(sv);
+    OUTPUT:
+       RETVAL
+
+void
+ppname(opnum)
+       int     opnum
+    CODE:
+       ST(0) = sv_newmortal();
+       if (opnum >= 0 && opnum < sizeof(ppnames)/sizeof(char*))
+           sv_setpv(ST(0), ppnames[opnum]);
+
+void
+hash(sv)
+       SV *    sv
+    CODE:
+       char *s;
+       STRLEN len;
+       U32 hash = 0;
+       char hexhash[11]; /* must fit "0xffffffff" plus trailing \0 */
+       s = SvPV(sv, len);
+       while (len--)
+           hash = hash * 33 + *s++;
+       sprintf(hexhash, "0x%x", hash);
+       ST(0) = sv_2mortal(newSVpv(hexhash, 0));
+
+#define cast_I32(foo) (I32)foo
+IV
+cast_I32(i)
+       IV      i
+
+void
+minus_c()
+    CODE:
+       minus_c = TRUE;
+
+SV *
+cstring(sv)
+       SV *    sv
+
+SV *
+cchar(sv)
+       SV *    sv
+
+#define OP_next(o)     o->op_next
+#define OP_sibling(o)  o->op_sibling
+#define OP_ppaddr(o)   ppnames[o->op_type]
+#define OP_targ(o)     o->op_targ
+#define OP_type(o)     o->op_type
+#define OP_seq(o)      o->op_seq
+#define OP_flags(o)    o->op_flags
+#define OP_private(o)  o->op_private
+
+MODULE = B     PACKAGE = B::OP         PREFIX = OP_
+
+B::OP
+OP_next(o)
+       B::OP           o
+
+B::OP
+OP_sibling(o)
+       B::OP           o
+
+char *
+OP_ppaddr(o)
+       B::OP           o
+
+U16
+OP_targ(o)
+       B::OP           o
+
+U16
+OP_type(o)
+       B::OP           o
+
+U16
+OP_seq(o)
+       B::OP           o
+
+U8
+OP_flags(o)
+       B::OP           o
+
+U8
+OP_private(o)
+       B::OP           o
+
+#define UNOP_first(o)  o->op_first
+
+MODULE = B     PACKAGE = B::UNOP               PREFIX = UNOP_
+
+B::OP 
+UNOP_first(o)
+       B::UNOP o
+
+#define BINOP_last(o)  o->op_last
+
+MODULE = B     PACKAGE = B::BINOP              PREFIX = BINOP_
+
+B::OP
+BINOP_last(o)
+       B::BINOP        o
+
+#define LOGOP_other(o) o->op_other
+
+MODULE = B     PACKAGE = B::LOGOP              PREFIX = LOGOP_
+
+B::OP
+LOGOP_other(o)
+       B::LOGOP        o
+
+#define CONDOP_true(o) o->op_true
+#define CONDOP_false(o)        o->op_false
+
+MODULE = B     PACKAGE = B::CONDOP             PREFIX = CONDOP_
+
+B::OP
+CONDOP_true(o)
+       B::CONDOP       o
+
+B::OP
+CONDOP_false(o)
+       B::CONDOP       o
+
+#define LISTOP_children(o)     o->op_children
+
+MODULE = B     PACKAGE = B::LISTOP             PREFIX = LISTOP_
+
+U32
+LISTOP_children(o)
+       B::LISTOP       o
+
+#define PMOP_pmreplroot(o)     o->op_pmreplroot
+#define PMOP_pmreplstart(o)    o->op_pmreplstart
+#define PMOP_pmnext(o)         o->op_pmnext
+#define PMOP_pmregexp(o)       o->op_pmregexp
+#define PMOP_pmshort(o)                o->op_pmshort
+#define PMOP_pmflags(o)                o->op_pmflags
+#define PMOP_pmpermflags(o)    o->op_pmpermflags
+#define PMOP_pmslen(o)         o->op_pmslen
+
+MODULE = B     PACKAGE = B::PMOP               PREFIX = PMOP_
+
+void
+PMOP_pmreplroot(o)
+       B::PMOP         o
+       OP *            root = NO_INIT
+    CODE:
+       ST(0) = sv_newmortal();
+       root = o->op_pmreplroot;
+       /* OP_PUSHRE stores an SV* instead of an OP* in op_pmreplroot */
+       if (o->op_type == OP_PUSHRE) {
+           sv_setiv(newSVrv(ST(0), root ?
+                            svclassnames[SvTYPE((SV*)root)] : "B::SV"),
+                    (IV)root);
+       }
+       else {
+           sv_setiv(newSVrv(ST(0), cc_opclassname(root)), (IV)root);
+       }
+
+B::OP
+PMOP_pmreplstart(o)
+       B::PMOP         o
+
+B::PMOP
+PMOP_pmnext(o)
+       B::PMOP         o
+
+B::SV
+PMOP_pmshort(o)
+       B::PMOP         o
+
+U16
+PMOP_pmflags(o)
+       B::PMOP         o
+
+U16
+PMOP_pmpermflags(o)
+       B::PMOP         o
+
+U8
+PMOP_pmslen(o)
+       B::PMOP         o
+
+void
+PMOP_precomp(o)
+       B::PMOP         o
+       REGEXP *        rx = NO_INIT
+    CODE:
+       ST(0) = sv_newmortal();
+       rx = o->op_pmregexp;
+       if (rx)
+           sv_setpvn(ST(0), rx->precomp, rx->prelen);
+
+#define SVOP_sv(o)     o->op_sv
+
+MODULE = B     PACKAGE = B::SVOP               PREFIX = SVOP_
+
+
+B::SV
+SVOP_sv(o)
+       B::SVOP o
+
+#define GVOP_gv(o)     o->op_gv
+
+MODULE = B     PACKAGE = B::GVOP               PREFIX = GVOP_
+
+
+B::GV
+GVOP_gv(o)
+       B::GVOP o
+
+MODULE = B     PACKAGE = B::PVOP               PREFIX = PVOP_
+
+void
+PVOP_pv(o)
+       B::PVOP o
+    CODE:
+       /*
+        * OP_TRANS uses op_pv to point to a table of 256 shorts
+        * whereas other PVOPs point to a null terminated string.
+        */
+       ST(0) = sv_2mortal(newSVpv(o->op_pv, (o->op_type == OP_TRANS) ?
+                                  256 * sizeof(short) : 0));
+
+#define LOOP_redoop(o) o->op_redoop
+#define LOOP_nextop(o) o->op_nextop
+#define LOOP_lastop(o) o->op_lastop
+
+MODULE = B     PACKAGE = B::LOOP               PREFIX = LOOP_
+
+
+B::OP
+LOOP_redoop(o)
+       B::LOOP o
+
+B::OP
+LOOP_nextop(o)
+       B::LOOP o
+
+B::OP
+LOOP_lastop(o)
+       B::LOOP o
+
+#define COP_label(o)   o->cop_label
+#define COP_stash(o)   o->cop_stash
+#define COP_filegv(o)  o->cop_filegv
+#define COP_cop_seq(o) o->cop_seq
+#define COP_arybase(o) o->cop_arybase
+#define COP_line(o)    o->cop_line
+
+MODULE = B     PACKAGE = B::COP                PREFIX = COP_
+
+char *
+COP_label(o)
+       B::COP  o
+
+B::HV
+COP_stash(o)
+       B::COP  o
+
+B::GV
+COP_filegv(o)
+       B::COP  o
+
+U32
+COP_cop_seq(o)
+       B::COP  o
+
+I32
+COP_arybase(o)
+       B::COP  o
+
+U16
+COP_line(o)
+       B::COP  o
+
+MODULE = B     PACKAGE = B::SV         PREFIX = Sv
+
+U32
+SvREFCNT(sv)
+       B::SV   sv
+
+U32
+SvFLAGS(sv)
+       B::SV   sv
+
+MODULE = B     PACKAGE = B::IV         PREFIX = Sv
+
+IV
+SvIV(sv)
+       B::IV   sv
+
+IV
+SvIVX(sv)
+       B::IV   sv
+
+MODULE = B     PACKAGE = B::IV
+
+#define needs64bits(sv) ((I32)SvIVX(sv) != SvIVX(sv))
+
+int
+needs64bits(sv)
+       B::IV   sv
+
+void
+packiv(sv)
+       B::IV   sv
+    CODE:
+       if (sizeof(IV) == 8) {
+           U32 wp[2];
+           IV iv = SvIVX(sv);
+           /*
+            * The following way of spelling 32 is to stop compilers on
+            * 32-bit architectures from moaning about the shift count
+            * being >= the width of the type. Such architectures don't
+            * reach this code anyway (unless sizeof(IV) > 8 but then
+            * everything else breaks too so I'm not fussed at the moment).
+            */
+           wp[0] = htonl(((U32)iv) >> (sizeof(IV)*4));
+           wp[1] = htonl(iv & 0xffffffff);
+           ST(0) = sv_2mortal(newSVpv((char *)wp, 8));
+       } else {
+           U32 w = htonl((U32)SvIVX(sv));
+           ST(0) = sv_2mortal(newSVpv((char *)&w, 4));
+       }
+
+MODULE = B     PACKAGE = B::NV         PREFIX = Sv
+
+double
+SvNV(sv)
+       B::NV   sv
+
+double
+SvNVX(sv)
+       B::NV   sv
+
+MODULE = B     PACKAGE = B::RV         PREFIX = Sv
+
+B::SV
+SvRV(sv)
+       B::RV   sv
+
+MODULE = B     PACKAGE = B::PV         PREFIX = Sv
+
+void
+SvPV(sv)
+       B::PV   sv
+    CODE:
+       ST(0) = sv_newmortal();
+       sv_setpvn(ST(0), SvPVX(sv), SvCUR(sv));
+
+MODULE = B     PACKAGE = B::PVMG       PREFIX = Sv
+
+void
+SvMAGIC(sv)
+       B::PVMG sv
+       MAGIC * mg = NO_INIT
+    PPCODE:
+       for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic)
+           XPUSHs(make_mg_object(sv_newmortal(), mg));
+
+MODULE = B     PACKAGE = B::PVMG
+
+B::HV
+SvSTASH(sv)
+       B::PVMG sv
+
+#define MgMOREMAGIC(mg) mg->mg_moremagic
+#define MgPRIVATE(mg) mg->mg_private
+#define MgTYPE(mg) mg->mg_type
+#define MgFLAGS(mg) mg->mg_flags
+#define MgOBJ(mg) mg->mg_obj
+
+MODULE = B     PACKAGE = B::MAGIC      PREFIX = Mg     
+
+B::MAGIC
+MgMOREMAGIC(mg)
+       B::MAGIC        mg
+
+U16
+MgPRIVATE(mg)
+       B::MAGIC        mg
+
+char
+MgTYPE(mg)
+       B::MAGIC        mg
+
+U8
+MgFLAGS(mg)
+       B::MAGIC        mg
+
+B::SV
+MgOBJ(mg)
+       B::MAGIC        mg
+
+void
+MgPTR(mg)
+       B::MAGIC        mg
+    CODE:
+       ST(0) = sv_newmortal();
+       if (mg->mg_ptr)
+           sv_setpvn(ST(0), mg->mg_ptr, mg->mg_len);
+
+MODULE = B     PACKAGE = B::PVLV       PREFIX = Lv
+
+U32
+LvTARGOFF(sv)
+       B::PVLV sv
+
+U32
+LvTARGLEN(sv)
+       B::PVLV sv
+
+char
+LvTYPE(sv)
+       B::PVLV sv
+
+B::SV
+LvTARG(sv)
+       B::PVLV sv
+
+MODULE = B     PACKAGE = B::BM         PREFIX = Bm
+
+I32
+BmUSEFUL(sv)
+       B::BM   sv
+
+U16
+BmPREVIOUS(sv)
+       B::BM   sv
+
+U8
+BmRARE(sv)
+       B::BM   sv
+
+void
+BmTABLE(sv)
+       B::BM   sv
+       STRLEN  len = NO_INIT
+       char *  str = NO_INIT
+    CODE:
+       str = SvPV(sv, len);
+       /* Boyer-Moore table is just after string and its safety-margin \0 */
+       ST(0) = sv_2mortal(newSVpv(str + len + 1, 256));
+
+MODULE = B     PACKAGE = B::GV         PREFIX = Gv
+
+void
+GvNAME(gv)
+       B::GV   gv
+    CODE:
+       ST(0) = sv_2mortal(newSVpv(GvNAME(gv), GvNAMELEN(gv)));
+
+B::HV
+GvSTASH(gv)
+       B::GV   gv
+
+B::SV
+GvSV(gv)
+       B::GV   gv
+
+B::IO
+GvIO(gv)
+       B::GV   gv
+
+B::CV
+GvFORM(gv)
+       B::GV   gv
+
+B::AV
+GvAV(gv)
+       B::GV   gv
+
+B::HV
+GvHV(gv)
+       B::GV   gv
+
+B::GV
+GvEGV(gv)
+       B::GV   gv
+
+B::CV
+GvCV(gv)
+       B::GV   gv
+
+U32
+GvCVGEN(gv)
+       B::GV   gv
+
+U16
+GvLINE(gv)
+       B::GV   gv
+
+B::GV
+GvFILEGV(gv)
+       B::GV   gv
+
+MODULE = B     PACKAGE = B::GV
+
+U32
+GvREFCNT(gv)
+       B::GV   gv
+
+U8
+GvFLAGS(gv)
+       B::GV   gv
+
+MODULE = B     PACKAGE = B::IO         PREFIX = Io
+
+long
+IoLINES(io)
+       B::IO   io
+
+long
+IoPAGE(io)
+       B::IO   io
+
+long
+IoPAGE_LEN(io)
+       B::IO   io
+
+long
+IoLINES_LEFT(io)
+       B::IO   io
+
+char *
+IoTOP_NAME(io)
+       B::IO   io
+
+B::GV
+IoTOP_GV(io)
+       B::IO   io
+
+char *
+IoFMT_NAME(io)
+       B::IO   io
+
+B::GV
+IoFMT_GV(io)
+       B::IO   io
+
+char *
+IoBOTTOM_NAME(io)
+       B::IO   io
+
+B::GV
+IoBOTTOM_GV(io)
+       B::IO   io
+
+short
+IoSUBPROCESS(io)
+       B::IO   io
+
+MODULE = B     PACKAGE = B::IO
+
+char
+IoTYPE(io)
+       B::IO   io
+
+U8
+IoFLAGS(io)
+       B::IO   io
+
+MODULE = B     PACKAGE = B::AV         PREFIX = Av
+
+SSize_t
+AvFILL(av)
+       B::AV   av
+
+SSize_t
+AvMAX(av)
+       B::AV   av
+
+#define AvOFF(av) ((XPVAV*)SvANY(av))->xof_off
+
+IV
+AvOFF(av)
+       B::AV   av
+
+void
+AvARRAY(av)
+       B::AV   av
+    PPCODE:
+       if (AvFILL(av) >= 0) {
+           SV **svp = AvARRAY(av);
+           I32 i;
+           for (i = 0; i <= AvFILL(av); i++)
+               XPUSHs(make_sv_object(sv_newmortal(), svp[i]));
+       }
+
+MODULE = B     PACKAGE = B::AV
+
+U8
+AvFLAGS(av)
+       B::AV   av
+
+MODULE = B     PACKAGE = B::CV         PREFIX = Cv
+
+B::HV
+CvSTASH(cv)
+       B::CV   cv
+
+B::OP
+CvSTART(cv)
+       B::CV   cv
+
+B::OP
+CvROOT(cv)
+       B::CV   cv
+
+B::GV
+CvGV(cv)
+       B::CV   cv
+
+B::GV
+CvFILEGV(cv)
+       B::CV   cv
+
+long
+CvDEPTH(cv)
+       B::CV   cv
+
+B::AV
+CvPADLIST(cv)
+       B::CV   cv
+
+B::CV
+CvOUTSIDE(cv)
+       B::CV   cv
+
+void
+CvXSUB(cv)
+       B::CV   cv
+    CODE:
+       ST(0) = sv_2mortal(newSViv((IV)CvXSUB(cv)));
+
+
+void
+CvXSUBANY(cv)
+       B::CV   cv
+    CODE:
+       ST(0) = sv_2mortal(newSViv(CvXSUBANY(cv).any_iv));
+
+MODULE = B     PACKAGE = B::HV         PREFIX = Hv
+
+STRLEN
+HvFILL(hv)
+       B::HV   hv
+
+STRLEN
+HvMAX(hv)
+       B::HV   hv
+
+I32
+HvKEYS(hv)
+       B::HV   hv
+
+I32
+HvRITER(hv)
+       B::HV   hv
+
+char *
+HvNAME(hv)
+       B::HV   hv
+
+B::PMOP
+HvPMROOT(hv)
+       B::HV   hv
+
+void
+HvARRAY(hv)
+       B::HV   hv
+    PPCODE:
+       if (HvKEYS(hv) > 0) {
+           SV *sv;
+           char *key;
+           I32 len;
+           (void)hv_iterinit(hv);
+           EXTEND(sp, HvKEYS(hv) * 2);
+           while (sv = hv_iternextsv(hv, &key, &len)) {
+               PUSHs(newSVpv(key, len));
+               PUSHs(make_sv_object(sv_newmortal(), sv));
+           }
+       }
diff --git a/B/Asmdata.pm b/B/Asmdata.pm
new file mode 100644 (file)
index 0000000..bcfa0cc
--- /dev/null
@@ -0,0 +1,152 @@
+#
+#      Copyright (c) 1996 Malcolm Beattie
+#
+#      You may distribute under the terms of either the GNU General Public
+#      License or the Artistic License, as specified in the README file.
+#
+#
+#
+# This file is autogenerated from bytecode.pl. Changes made here will be lost.
+#
+package B::Asmdata;
+use Exporter;
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(%insn_data @insn_name @optype @specialsv_name);
+use vars qw(%insn_data @insn_name @optype @specialsv_name);
+
+@optype = qw(OP UNOP BINOP LOGOP CONDOP LISTOP PMOP SVOP GVOP PVOP LOOP COP);
+@specialsv_name = qw(Nullsv &sv_undef &sv_yes &sv_no);
+
+# XXX insn_data is initialised this way because with a large
+# %insn_data = (foo => [...], bar => [...], ...) initialiser
+# I get a hard-to-track-down stack underflow and segfault.
+$insn_data{comment} = [35, \&PUT_comment, "GET_comment"];
+$insn_data{nop} = [10, \&PUT_none, "GET_none"];
+$insn_data{ret} = [0, \&PUT_none, "GET_none"];
+$insn_data{ldsv} = [1, \&PUT_objindex, "GET_objindex"];
+$insn_data{ldop} = [2, \&PUT_objindex, "GET_objindex"];
+$insn_data{stsv} = [3, \&PUT_U32, "GET_U32"];
+$insn_data{stop} = [4, \&PUT_U32, "GET_U32"];
+$insn_data{ldspecsv} = [5, \&PUT_U8, "GET_U8"];
+$insn_data{newsv} = [6, \&PUT_U8, "GET_U8"];
+$insn_data{newop} = [7, \&PUT_U8, "GET_U8"];
+$insn_data{newopn} = [8, \&PUT_U8, "GET_U8"];
+$insn_data{newpv} = [9, \&PUT_PV, "GET_PV"];
+$insn_data{pv_cur} = [11, \&PUT_U32, "GET_U32"];
+$insn_data{pv_free} = [12, \&PUT_none, "GET_none"];
+$insn_data{sv_upgrade} = [13, \&PUT_U8, "GET_U8"];
+$insn_data{sv_refcnt} = [14, \&PUT_U32, "GET_U32"];
+$insn_data{sv_refcnt_add} = [15, \&PUT_I32, "GET_I32"];
+$insn_data{sv_flags} = [16, \&PUT_U32, "GET_U32"];
+$insn_data{xrv} = [17, \&PUT_objindex, "GET_objindex"];
+$insn_data{xpv} = [18, \&PUT_none, "GET_none"];
+$insn_data{xiv32} = [19, \&PUT_I32, "GET_I32"];
+$insn_data{xiv64} = [20, \&PUT_IV64, "GET_IV64"];
+$insn_data{xnv} = [21, \&PUT_double, "GET_double"];
+$insn_data{xlv_targoff} = [22, \&PUT_U32, "GET_U32"];
+$insn_data{xlv_targlen} = [23, \&PUT_U32, "GET_U32"];
+$insn_data{xlv_targ} = [24, \&PUT_objindex, "GET_objindex"];
+$insn_data{xlv_type} = [25, \&PUT_U8, "GET_U8"];
+$insn_data{xbm_useful} = [26, \&PUT_I32, "GET_I32"];
+$insn_data{xbm_previous} = [27, \&PUT_U16, "GET_U16"];
+$insn_data{xbm_rare} = [28, \&PUT_U8, "GET_U8"];
+$insn_data{xfm_lines} = [29, \&PUT_I32, "GET_I32"];
+$insn_data{xio_lines} = [30, \&PUT_I32, "GET_I32"];
+$insn_data{xio_page} = [31, \&PUT_I32, "GET_I32"];
+$insn_data{xio_page_len} = [32, \&PUT_I32, "GET_I32"];
+$insn_data{xio_lines_left} = [33, \&PUT_I32, "GET_I32"];
+$insn_data{xio_top_name} = [34, \&PUT_pvcontents, "GET_pvcontents"];
+$insn_data{xio_top_gv} = [36, \&PUT_objindex, "GET_objindex"];
+$insn_data{xio_fmt_name} = [37, \&PUT_pvcontents, "GET_pvcontents"];
+$insn_data{xio_fmt_gv} = [38, \&PUT_objindex, "GET_objindex"];
+$insn_data{xio_bottom_name} = [39, \&PUT_pvcontents, "GET_pvcontents"];
+$insn_data{xio_bottom_gv} = [40, \&PUT_objindex, "GET_objindex"];
+$insn_data{xio_subprocess} = [41, \&PUT_U16, "GET_U16"];
+$insn_data{xio_type} = [42, \&PUT_U8, "GET_U8"];
+$insn_data{xio_flags} = [43, \&PUT_U8, "GET_U8"];
+$insn_data{xcv_stash} = [44, \&PUT_objindex, "GET_objindex"];
+$insn_data{xcv_start} = [45, \&PUT_objindex, "GET_objindex"];
+$insn_data{xcv_root} = [46, \&PUT_objindex, "GET_objindex"];
+$insn_data{xcv_gv} = [47, \&PUT_objindex, "GET_objindex"];
+$insn_data{xcv_filegv} = [48, \&PUT_objindex, "GET_objindex"];
+$insn_data{xcv_depth} = [49, \&PUT_I32, "GET_I32"];
+$insn_data{xcv_padlist} = [50, \&PUT_objindex, "GET_objindex"];
+$insn_data{xcv_outside} = [51, \&PUT_objindex, "GET_objindex"];
+$insn_data{xcv_flags} = [52, \&PUT_U8, "GET_U8"];
+$insn_data{av_extend} = [53, \&PUT_I32, "GET_I32"];
+$insn_data{av_push} = [54, \&PUT_objindex, "GET_objindex"];
+$insn_data{xav_fill} = [55, \&PUT_I32, "GET_I32"];
+$insn_data{xav_max} = [56, \&PUT_I32, "GET_I32"];
+$insn_data{xav_flags} = [57, \&PUT_U8, "GET_U8"];
+$insn_data{xhv_riter} = [58, \&PUT_I32, "GET_I32"];
+$insn_data{xhv_name} = [59, \&PUT_pvcontents, "GET_pvcontents"];
+$insn_data{hv_store} = [60, \&PUT_objindex, "GET_objindex"];
+$insn_data{sv_magic} = [61, \&PUT_U8, "GET_U8"];
+$insn_data{mg_obj} = [62, \&PUT_objindex, "GET_objindex"];
+$insn_data{mg_private} = [63, \&PUT_U16, "GET_U16"];
+$insn_data{mg_flags} = [64, \&PUT_U8, "GET_U8"];
+$insn_data{mg_pv} = [65, \&PUT_pvcontents, "GET_pvcontents"];
+$insn_data{xmg_stash} = [66, \&PUT_objindex, "GET_objindex"];
+$insn_data{gv_fetchpv} = [67, \&PUT_strconst, "GET_strconst"];
+$insn_data{gv_stashpv} = [68, \&PUT_strconst, "GET_strconst"];
+$insn_data{gp_sv} = [69, \&PUT_objindex, "GET_objindex"];
+$insn_data{gp_refcnt} = [70, \&PUT_U32, "GET_U32"];
+$insn_data{gp_refcnt_add} = [71, \&PUT_I32, "GET_I32"];
+$insn_data{gp_av} = [72, \&PUT_objindex, "GET_objindex"];
+$insn_data{gp_hv} = [73, \&PUT_objindex, "GET_objindex"];
+$insn_data{gp_cv} = [74, \&PUT_objindex, "GET_objindex"];
+$insn_data{gp_filegv} = [75, \&PUT_objindex, "GET_objindex"];
+$insn_data{gp_io} = [76, \&PUT_objindex, "GET_objindex"];
+$insn_data{gp_form} = [77, \&PUT_objindex, "GET_objindex"];
+$insn_data{gp_cvgen} = [78, \&PUT_U32, "GET_U32"];
+$insn_data{gp_line} = [79, \&PUT_U16, "GET_U16"];
+$insn_data{gp_share} = [80, \&PUT_objindex, "GET_objindex"];
+$insn_data{xgv_flags} = [81, \&PUT_U8, "GET_U8"];
+$insn_data{op_next} = [82, \&PUT_objindex, "GET_objindex"];
+$insn_data{op_sibling} = [83, \&PUT_objindex, "GET_objindex"];
+$insn_data{op_ppaddr} = [84, \&PUT_strconst, "GET_strconst"];
+$insn_data{op_targ} = [85, \&PUT_U32, "GET_U32"];
+$insn_data{op_type} = [86, \&PUT_U16, "GET_U16"];
+$insn_data{op_seq} = [87, \&PUT_U16, "GET_U16"];
+$insn_data{op_flags} = [88, \&PUT_U8, "GET_U8"];
+$insn_data{op_private} = [89, \&PUT_U8, "GET_U8"];
+$insn_data{op_first} = [90, \&PUT_objindex, "GET_objindex"];
+$insn_data{op_last} = [91, \&PUT_objindex, "GET_objindex"];
+$insn_data{op_other} = [92, \&PUT_objindex, "GET_objindex"];
+$insn_data{op_true} = [93, \&PUT_objindex, "GET_objindex"];
+$insn_data{op_false} = [94, \&PUT_objindex, "GET_objindex"];
+$insn_data{op_children} = [95, \&PUT_U32, "GET_U32"];
+$insn_data{op_pmreplroot} = [96, \&PUT_objindex, "GET_objindex"];
+$insn_data{op_pmreplrootgv} = [97, \&PUT_objindex, "GET_objindex"];
+$insn_data{op_pmreplstart} = [98, \&PUT_objindex, "GET_objindex"];
+$insn_data{op_pmnext} = [99, \&PUT_objindex, "GET_objindex"];
+$insn_data{pregcomp} = [100, \&PUT_pvcontents, "GET_pvcontents"];
+$insn_data{op_pmshort} = [101, \&PUT_objindex, "GET_objindex"];
+$insn_data{op_pmflags} = [102, \&PUT_U16, "GET_U16"];
+$insn_data{op_pmpermflags} = [103, \&PUT_U16, "GET_U16"];
+$insn_data{op_pmslen} = [104, \&PUT_U8, "GET_U8"];
+$insn_data{op_sv} = [105, \&PUT_objindex, "GET_objindex"];
+$insn_data{op_gv} = [106, \&PUT_objindex, "GET_objindex"];
+$insn_data{op_pv} = [107, \&PUT_pvcontents, "GET_pvcontents"];
+$insn_data{op_pv_tr} = [108, \&PUT_op_tr_array, "GET_op_tr_array"];
+$insn_data{op_redoop} = [109, \&PUT_objindex, "GET_objindex"];
+$insn_data{op_nextop} = [110, \&PUT_objindex, "GET_objindex"];
+$insn_data{op_lastop} = [111, \&PUT_objindex, "GET_objindex"];
+$insn_data{cop_label} = [112, \&PUT_pvcontents, "GET_pvcontents"];
+$insn_data{cop_stash} = [113, \&PUT_objindex, "GET_objindex"];
+$insn_data{cop_filegv} = [114, \&PUT_objindex, "GET_objindex"];
+$insn_data{cop_seq} = [115, \&PUT_U32, "GET_U32"];
+$insn_data{cop_arybase} = [116, \&PUT_I32, "GET_I32"];
+$insn_data{cop_line} = [117, \&PUT_U16, "GET_U16"];
+$insn_data{main_start} = [118, \&PUT_objindex, "GET_objindex"];
+$insn_data{main_root} = [119, \&PUT_objindex, "GET_objindex"];
+$insn_data{curpad} = [120, \&PUT_objindex, "GET_objindex"];
+
+my ($insn_name, $insn_data);
+while (($insn_name, $insn_data) = each %insn_data) {
+    $insn_name[$insn_data->[0]] = $insn_name;
+}
+# Fill in any gaps
+@insn_name = map($_ || "unused", @insn_name);
+
+1;
diff --git a/B/Assembler.pm b/B/Assembler.pm
new file mode 100644 (file)
index 0000000..0729b90
--- /dev/null
@@ -0,0 +1,207 @@
+#      Assembler.pm
+#
+#      Copyright (c) 1996 Malcolm Beattie
+#
+#      You may distribute under the terms of either the GNU General Public
+#      License or the Artistic License, as specified in the README file.
+package B::Assembler;
+use Exporter;
+use B qw(ppname);
+use B::Asmdata qw(%insn_data @insn_name);
+
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(assemble_fh assemble_insn strip_comments
+               parse_statement uncstring);
+
+use strict;
+my %opnumber;
+my ($i, $opname);
+for ($i = 0; defined($opname = ppname($i)); $i++) {
+    $opnumber{$opname} = $i;
+}
+
+my ($linenum, $errors);
+
+sub error {
+    my $str = shift;
+    warn "$linenum: $str\n";
+    $errors++;
+}
+
+my $debug = 0;
+sub debug { $debug = shift }
+
+#
+# First define all the data conversion subs to which Asmdata will refer
+#
+
+sub B::Asmdata::PUT_U8 {
+    my $arg = shift;
+    my $c = uncstring($arg);
+    if (defined($c)) {
+       if (length($c) != 1) {
+           error "argument for U8 is too long: $c";
+           $c = substr($c, 0, 1);
+       }
+    } else {
+       $c = chr($arg);
+    }
+    return $c;
+}
+
+sub B::Asmdata::PUT_U16 { pack("n", $_[0]) }
+sub B::Asmdata::PUT_U32 { pack("N", $_[0]) }
+sub B::Asmdata::PUT_I32 { pack("N", $_[0]) }
+sub B::Asmdata::PUT_objindex { pack("N", $_[0]) } # could allow names here
+
+sub B::Asmdata::PUT_strconst {
+    my $arg = shift;
+    $arg = uncstring($arg);
+    if (!defined($arg)) {
+       error "bad string constant: $arg";
+       return "";
+    }
+    if ($arg =~ s/\0//g) {
+       error "string constant argument contains NUL: $arg";
+    }
+    return $arg . "\0";
+}
+
+sub B::Asmdata::PUT_pvcontents {
+    my $arg = shift;
+    error "extraneous argument: $arg" if defined $arg;
+    return "";
+}
+sub B::Asmdata::PUT_PV {
+    my $arg = shift;
+    $arg = uncstring($arg);
+    error "bad string argument: $arg" unless defined($arg);
+    return pack("N", length($arg)) . $arg;
+}
+sub B::Asmdata::PUT_comment {
+    my $arg = shift;
+    $arg = uncstring($arg);
+    error "bad string argument: $arg" unless defined($arg);
+    if ($arg =~ s/\n//g) {
+       error "comment argument contains linefeed: $arg";
+    }
+    return $arg . "\n";
+}
+sub B::Asmdata::PUT_double { sprintf("%s\0", $_[0]) }
+sub B::Asmdata::PUT_none {
+    my $arg = shift;
+    error "extraneous argument: $arg" if defined $arg;
+    return "";
+}
+sub B::Asmdata::PUT_op_tr_array {
+    my $arg = shift;
+    my @ary = split(/\s*,\s*/, $arg);
+    if (@ary != 256) {
+       error "wrong number of arguments to op_tr_array";
+       @ary = (0) x 256;
+    }
+    return pack("n256", @ary);
+}
+# XXX Check this works
+sub B::Asmdata::PUT_IV64 {
+    my $arg = shift;
+    return pack("NN", $arg >> 32, $arg & 0xffffffff);
+}
+
+my %unesc = (n => "\n", r => "\r", t => "\t", a => "\a",
+            b => "\b", f => "\f", v => "\013");
+
+sub uncstring {
+    my $s = shift;
+    $s =~ s/^"// and $s =~ s/"$// or return undef;
+    $s =~ s/\\(\d\d\d|.)/length($1) == 3 ? chr(oct($1)) : ($unesc{$1}||$1)/eg;
+    return $s;
+}
+
+sub strip_comments {
+    my $stmt = shift;
+    # Comments only allowed in instructions which don't take string arguments
+    $stmt =~ s{
+       (?sx)   # Snazzy extended regexp coming up. Also, treat
+               # string as a single line so .* eats \n characters.
+       ^\s*    # Ignore leading whitespace
+       (
+         [^"]* # A double quote '"' indicates a string argument. If we
+               # find a double quote, the match fails and we strip nothing.
+       )
+       \s*\#   # Any amount of whitespace plus the comment marker...
+       .*$     # ...which carries on to end-of-string.
+    }{$1};     # Keep only the instruction and optional argument.
+    return $stmt;
+}
+
+sub parse_statement {
+    my $stmt = shift;
+    my ($insn, $arg) = $stmt =~ m{
+       (?sx)
+       ^\s*    # allow (but ignore) leading whitespace
+       (.*?)   # Instruction continues up until...
+       (?:     # ...an optional whitespace+argument group
+           \s+         # first whitespace.
+           (.*)        # The argument is all the rest (newlines included).
+       )?$     # anchor at end-of-line
+    }; 
+    if (defined($arg)) {
+       if ($arg =~ s/^0x(?=[0-9a-fA-F]+$)//) {
+           $arg = hex($arg);
+       } elsif ($arg =~ s/^0(?=[0-7]+$)//) {
+           $arg = oct($arg);
+       } elsif ($arg =~ /^pp_/) {
+           $arg =~ s/\s*$//; # strip trailing whitespace
+           my $opnum = $opnumber{$arg};
+           if (defined($opnum)) {
+               $arg = $opnum;
+           } else {
+               error qq(No such op type "$arg");
+               $arg = 0;
+           }
+       }
+    }
+    return ($insn, $arg);
+}
+
+sub assemble_insn {
+    my ($insn, $arg) = @_;
+    my $data = $insn_data{$insn};
+    if (defined($data)) {
+       my ($bytecode, $putsub) = @{$data}[0, 1];
+       my $argcode = &$putsub($arg);
+       return chr($bytecode).$argcode;
+    } else {
+       error qq(no such instruction "$insn");
+       return "";
+    }
+}
+
+sub assemble_fh {
+    my ($fh, $out) = @_;
+    my ($line, $insn, $arg);
+    $linenum = 0;
+    $errors = 0;
+    while ($line = <$fh>) {
+       $linenum++;
+       chomp $line;
+       if ($debug) {
+           my $quotedline = $line;
+           $quotedline =~ s/\\/\\\\/g;
+           $quotedline =~ s/"/\\"/g;
+           &$out(assemble_insn("comment", qq("$quotedline")));
+       }
+       $line = strip_comments($line) or next;
+       ($insn, $arg) = parse_statement($line);
+       &$out(assemble_insn($insn, $arg));
+       if ($debug) {
+           &$out(assemble_insn("nop", undef));
+       }
+    }
+    if ($errors) {
+       die "Assembly failed with $errors error(s)\n";
+    }
+}
+
+1;
diff --git a/B/Bblock.pm b/B/Bblock.pm
new file mode 100644 (file)
index 0000000..cd43d37
--- /dev/null
@@ -0,0 +1,149 @@
+package B::Bblock;
+use Exporter ();
+@ISA = "Exporter";
+@EXPORT_OK = qw(find_leaders);
+
+use B qw(ad peekop walkoptree walkoptree_exec
+        main_root main_start svref_2object);
+use B::Terse;
+use strict;
+
+my $bblock;
+my @bblock_ends;
+
+sub mark_leader {
+    my $op = shift;
+    if (ad($op)) {
+       $bblock->{ad($op)} = $op;
+    }
+}
+
+sub find_leaders {
+    my ($root, $start) = @_;
+    $bblock = {};
+    mark_leader($start);
+    walkoptree($root, "mark_if_leader");
+    return $bblock;
+}
+
+# Debugging
+sub walk_bblocks {
+    my ($root, $start) = @_;
+    my ($op, $lastop, $leader, $bb);
+    $bblock = {};
+    mark_leader($start);
+    walkoptree($root, "mark_if_leader");
+    my @leaders = values %$bblock;
+    while ($leader = shift @leaders) {
+       $lastop = $leader;
+       $op = $leader->next;
+       while (ad($op) && !exists($bblock->{ad($op)})) {
+           $bblock->{ad($op)} = $leader;
+           $lastop = $op;
+           $op = $op->next;
+       }
+       push(@bblock_ends, [$leader, $lastop]);
+    }
+    foreach $bb (@bblock_ends) {
+       ($leader, $lastop) = @$bb;
+       printf "%s .. %s\n", peekop($leader), peekop($lastop);
+       for ($op = $leader; ad($op) != ad($lastop); $op = $op->next) {
+           printf "    %s\n", peekop($op);
+       }
+       printf "    %s\n", peekop($lastop);
+    }
+    print "-------\n";
+    walkoptree_exec($start, "terse");
+}
+
+sub walk_bblocks_obj {
+    my $cvref = shift;
+    my $cv = svref_2object($cvref);
+    walk_bblocks($cv->ROOT, $cv->START);
+}
+
+sub B::OP::mark_if_leader {
+    my $op = shift;
+    my $ppaddr = $op->ppaddr;
+#    if ($ppaddr eq "pp_enter" || $ppaddr eq "pp_entersub" || $ppaddr eq "pp_return") {
+    if ($ppaddr eq "pp_enter" || $ppaddr eq "pp_return") {
+       mark_leader($op->next);
+    }
+}
+
+sub B::COP::mark_if_leader {
+    my $op = shift;
+    if ($op->label) {
+       mark_leader($op);
+    }
+}
+
+sub B::LOOP::mark_if_leader {
+    my $op = shift;
+    mark_leader($op->next);
+    mark_leader($op->nextop);
+    mark_leader($op->redoop);
+    mark_leader($op->lastop->next);
+}
+
+sub B::LOGOP::mark_if_leader {
+    my $op = shift;
+    my $ppaddr = $op->ppaddr;
+    mark_leader($op->next);
+    if ($ppaddr eq "pp_entertry") {
+       mark_leader($op->other->next);
+    } else {
+       mark_leader($op->other);
+    }
+}
+
+sub B::CONDOP::mark_if_leader {
+    my $op = shift;
+    mark_leader($op->next);
+    mark_leader($op->true);
+    mark_leader($op->false);
+}
+
+sub B::PMOP::mark_if_leader {
+    my $op = shift;
+    if ($op->ppaddr ne "pp_pushre") {
+       my $replroot = $op->pmreplroot;
+       if (ad($replroot)) {
+           mark_leader($replroot);
+           mark_leader($op->next);
+           mark_leader($op->pmreplstart);
+       }
+    }
+}
+
+# PMOP stuff omitted
+
+sub compile {
+    my @options = @_;
+    if (@options) {
+       return sub {
+           my $objname;
+           foreach $objname (@options) {
+               $objname = "main::$objname" unless $objname =~ /::/;
+               eval "walk_bblocks_obj(\\&$objname)";
+               die "walk_bblocks_obj(\\&$objname) failed: $@" if $@;
+           }
+       }
+    } else {
+       return sub { walk_bblocks(main_root, main_start) };
+    }
+}
+
+# Basic block leaders:
+#     Any COP (pp_nextstate) with a non-NULL label
+#     The op after a pp_enter
+#     [The op after a pp_entersub. Don't count this one.]
+#     The ops pointed at by nextop, redoop and lastop->op_next of a LOOP
+#     The ops pointed at by op_next and op_other of a LOGOP, except
+#     for pp_entertry which has op_next and op_other->op_next
+#     The ops pointed at by op_true and op_false of a CONDOP
+#     The op pointed at by op_pmreplstart of a PMOP
+#     The op pointed at by op_other->op_pmreplstart of pp_substcont?
+#     The op after a pp_return
+
+1;
diff --git a/B/Bytecode.pm b/B/Bytecode.pm
new file mode 100644 (file)
index 0000000..9e763de
--- /dev/null
@@ -0,0 +1,757 @@
+#      Bytecode.pm
+#
+#      Copyright (c) 1996 Malcolm Beattie
+#
+#      You may distribute under the terms of either the GNU General Public
+#      License or the Artistic License, as specified in the README file.
+#
+package B::Bytecode;
+use strict;
+use Carp;
+
+use B qw(ad minus_c main_cv main_root main_start comppadlist
+        class peekop walkoptree svref_2object cstring walksymtable);
+use B::Asmdata qw(@optype @specialsv_name);
+use B::Assembler qw(assemble_fh);
+
+my %optype_enum;
+my $i;
+for ($i = 0; $i < @optype; $i++) {
+    $optype_enum{$optype[$i]} = $i;
+}
+
+# Following is SVf_POK|SVp_POK
+# XXX Shouldn't be hardwired
+sub POK () { 0x04040000 }
+
+# Following is SVf_IOK|SVp_OK
+# XXX Shouldn't be hardwired
+sub IOK () { 0x01010000 }
+
+my ($verbose, $module_only, $no_assemble, $debug_cv);
+my $assembler_pid;
+
+# Optimisation options. On the command line, use hyphens instead of
+# underscores for compatibility with gcc-style options. We use
+# underscores here because they are OK in (strict) barewords.
+my ($strip_syntree, $compress_nullops, $omit_seq, $bypass_nullops);
+my %optimise = (strip_syntax_tree      => \$strip_syntree,
+               compress_nullops        => \$compress_nullops,
+               omit_sequence_numbers   => \$omit_seq,
+               bypass_nullops          => \$bypass_nullops);
+
+my $nextix = 0;
+my %symtable;  # maps object addresses to object indices.
+               # Filled in at allocation (newsv/newop) time.
+my %saved;     # maps object addresses (for SVish classes) to "saved yet?"
+               # flag. Set at FOO::bytecode time usually by SV::bytecode.
+               # Manipulated via saved(), mark_saved(), unmark_saved().
+
+my $svix = -1; # we keep track of when the sv register contains an element
+               # of the object table to avoid unnecessary repeated
+               # consecutive ldsv instructions.
+my $opix = -1; # Ditto for the op register.
+
+sub ldsv {
+    my $ix = shift;
+    if ($ix != $svix) {
+       print "ldsv $ix\n";
+       $svix = $ix;
+    }
+}
+
+sub stsv {
+    my $ix = shift;
+    print "stsv $ix\n";
+    $svix = $ix;
+}
+
+sub set_svix {
+    $svix = shift;
+}
+
+sub ldop {
+    my $ix = shift;
+    if ($ix != $opix) {
+       print "ldop $ix\n";
+       $opix = $ix;
+    }
+}
+
+sub stop {
+    my $ix = shift;
+    print "stop $ix\n";
+    $opix = $ix;
+}
+
+sub set_opix {
+    $opix = shift;
+}
+
+sub pvstring {
+    my $str = shift;
+    if (defined($str)) {
+       return cstring($str . "\0");
+    } else {
+       return '""';
+    }
+}
+
+sub saved { $saved{ad($_[0])} }
+sub mark_saved { $saved{ad($_[0])} = 1 }
+sub unmark_saved { $saved{ad($_[0])} = 0 }
+
+my $debug = 0;
+sub debug { $debug = shift }
+
+sub B::OBJECT::nyi {
+    my $obj = shift;
+    warn sprintf("bytecode save method for %s (0x%x) not yet implemented\n",
+                class($obj), ad($obj));
+}
+
+#
+# objix may stomp on the op register (for op objects)
+# or the sv register (for SV objects)
+#
+sub B::OBJECT::objix {
+    my $obj = shift;
+    my $ix = $symtable{$$obj};
+    if (defined($ix)) {
+       return $ix;
+    } else {
+       $obj->newix($nextix);
+       return $symtable{$$obj} = $nextix++;
+    }
+}
+
+sub B::SV::newix {
+    my ($sv, $ix) = @_;
+    printf "newsv %d\t# %s\n", $sv->FLAGS & 0xf, class($sv);
+    stsv($ix);    
+}
+
+sub B::GV::newix {
+    my ($gv, $ix) = @_;
+    my $gvname = $gv->NAME;
+    my $name = cstring($gv->STASH->NAME . "::" . $gvname);
+    print "gv_fetchpv $name\n";
+    stsv($ix);
+}
+
+sub B::HV::newix {
+    my ($hv, $ix) = @_;
+    my $name = $hv->NAME;
+    if ($name) {
+       # It's a stash
+       printf "gv_stashpv %s\n", cstring($name);
+       stsv($ix);
+    } else {
+       # It's an ordinary HV. Fall back to ordinary newix method
+       $hv->B::SV::newix($ix);
+    }
+}
+
+sub B::SPECIAL::newix {
+    my ($sv, $ix) = @_;
+    # Special case. $$sv is not the address of the SV but an
+    # index into svspecialsv_list.
+    printf "ldspecsv $$sv\t# %s\n", $specialsv_name[$$sv];
+    stsv($ix);
+}
+
+sub B::OP::newix {
+    my ($op, $ix) = @_;
+    my $class = class($op);
+    my $typenum = $optype_enum{$class};
+    croak "OP::newix: can't understand class $class" unless defined($typenum);
+    print "newop $typenum\t# $class\n";
+    stop($ix);
+}
+
+sub B::OP::bytecode {
+    my $op = shift;
+    my $next = $op->next;
+    my $nextix;
+    my $sibix = $op->sibling->objix;
+    my $ix = $op->objix;
+    my $type = $op->type;
+
+    if ($bypass_nullops) {
+       $next = $next->next while ad($next) && $next->type == 0;
+    }
+    $nextix = $next->objix;
+
+    printf "# %s\n", peekop($op) if $debug;
+    ldop($ix);
+    print "op_next $nextix\n";
+    print "op_sibling $sibix\n" unless $strip_syntree;
+    printf "op_type %s\t# %d\n", $op->ppaddr, $type;
+    printf("op_seq %d\n", $op->seq) unless $omit_seq;
+    if ($type || !$compress_nullops) {
+       printf "op_targ %d\nop_flags 0x%x\nop_private 0x%x\n",
+           $op->targ, $op->flags, $op->private;
+    }
+}
+
+sub B::UNOP::bytecode {
+    my $op = shift;
+    my $firstix = $op->first->objix;
+    $op->B::OP::bytecode;
+    if (($op->type || !$compress_nullops) && !$strip_syntree) {
+       print "op_first $firstix\n";
+    }
+}
+
+sub B::LOGOP::bytecode {
+    my $op = shift;
+    my $otherix = $op->other->objix;
+    $op->B::UNOP::bytecode;
+    print "op_other $otherix\n";
+}
+
+sub B::SVOP::bytecode {
+    my $op = shift;
+    my $sv = $op->sv;
+    my $svix = $sv->objix;
+    $op->B::OP::bytecode;
+    print "op_sv $svix\n";
+    $sv->bytecode;
+}
+
+sub B::GVOP::bytecode {
+    my $op = shift;
+    my $gv = $op->gv;
+    my $gvix = $gv->objix;
+    $op->B::OP::bytecode;
+    print "op_gv $gvix\n";
+    $gv->bytecode;
+}
+
+sub B::PVOP::bytecode {
+    my $op = shift;
+    my $pv = $op->pv;
+    $op->B::OP::bytecode;
+    #
+    # This would be easy except that OP_TRANS uses a PVOP to store an
+    # endian-dependent array of 256 shorts instead of a plain string.
+    #
+    if ($op->ppaddr eq "pp_trans") {
+       my @shorts = unpack("s256", $pv); # assembler handles endianness
+       print "op_pv_tr ", join(",", @shorts), "\n";
+    } else {
+       printf "newpv %s\nop_pv\n", pvstring($pv);
+    }
+}
+
+sub B::BINOP::bytecode {
+    my $op = shift;
+    my $lastix = $op->last->objix;
+    $op->B::UNOP::bytecode;
+    if (($op->type || !$compress_nullops) && !$strip_syntree) {
+       print "op_last $lastix\n";
+    }
+}
+
+sub B::CONDOP::bytecode {
+    my $op = shift;
+    my $trueix = $op->true->objix;
+    my $falseix = $op->false->objix;
+    $op->B::UNOP::bytecode;
+    print "op_true $trueix\nop_false $falseix\n";
+}
+
+sub B::LISTOP::bytecode {
+    my $op = shift;
+    my $children = $op->children;
+    $op->B::BINOP::bytecode;
+    if (($op->type || !$compress_nullops) && !$strip_syntree) {
+       print "op_children $children\n";
+    }
+}
+
+sub B::LOOP::bytecode {
+    my $op = shift;
+    my $redoopix = $op->redoop->objix;
+    my $nextopix = $op->nextop->objix;
+    my $lastopix = $op->lastop->objix;
+    $op->B::LISTOP::bytecode;
+    print "op_redoop $redoopix\nop_nextop $nextopix\nop_lastop $lastopix\n";
+}
+
+sub B::COP::bytecode {
+    my $op = shift;
+    my $stash = $op->stash;
+    my $stashix = $stash->objix;
+    my $filegv = $op->filegv;
+    my $filegvix = $filegv->objix;
+    my $line = $op->line;
+    if ($debug) {
+       printf "# line %s:%d\n", $filegv->SV->PV, $line;
+    }
+    $op->B::OP::bytecode;
+    printf <<"EOT", pvstring($op->label), $op->cop_seq, $op->arybase;
+newpv %s
+cop_label
+cop_stash $stashix
+cop_seq %d
+cop_filegv $filegvix
+cop_arybase %d
+cop_line $line
+EOT
+    $filegv->bytecode;
+    $stash->bytecode;
+}
+
+sub B::PMOP::bytecode {
+    my $op = shift;
+    my $short = $op->pmshort;
+    my $shortix = $short->objix;
+    my $replroot = $op->pmreplroot;
+    my $replrootix = $replroot->objix;
+    my $replstartix = $op->pmreplstart->objix;
+    my $ppaddr = $op->ppaddr;
+    # pmnext is corrupt in some PMOPs (see misc.t for example)
+    #my $pmnextix = $op->pmnext->objix;
+
+    $short->bytecode;
+    if (ad($replroot)) {
+       # OP_PUSHRE (a mutated version of OP_MATCH for the regexp
+       # argument to a split) stores a GV in op_pmreplroot instead
+       # of a substitution syntax tree. We don't want to walk that...
+       if ($ppaddr eq "pp_pushre") {
+           $replroot->bytecode;
+       } else {
+           walkoptree($replroot, "bytecode");
+       }
+    }
+    $op->B::LISTOP::bytecode;
+    if ($ppaddr eq "pp_pushre") {
+       printf "op_pmreplrootgv $replrootix\n";
+    } else {
+       print "op_pmreplroot $replrootix\nop_pmreplstart $replstartix\n";
+    }
+    my $re = pvstring($op->precomp);
+    # op_pmnext omitted since a perl bug means it's sometime corrupt
+    printf <<"EOT", $op->pmflags, $op->pmpermflags, $op->pmslen;
+op_pmshort $shortix
+op_pmflags 0x%x
+op_pmpermflags 0x%x
+op_pmslen %d
+newpv $re
+pregcomp
+EOT
+}
+
+sub B::SV::bytecode {
+    my $sv = shift;
+    return if saved($sv);
+    my $ix = $sv->objix;
+    my $refcnt = $sv->REFCNT;
+    my $flags = sprintf("0x%x", $sv->FLAGS);
+    ldsv($ix);
+    print "sv_refcnt $refcnt\nsv_flags $flags\n";
+    mark_saved($sv);
+}
+
+sub B::PV::bytecode {
+    my $sv = shift;
+    return if saved($sv);
+    $sv->B::SV::bytecode;
+    printf("newpv %s\nxpv\n", pvstring($sv->PV)) if $sv->FLAGS & POK;
+}
+
+sub B::IV::bytecode {
+    my $sv = shift;
+    return if saved($sv);
+    my $iv = $sv->IVX;
+    $sv->B::SV::bytecode;
+    printf "%s $iv\n", $sv->needs64bits ? "xiv64" : "xiv32";
+}
+
+sub B::NV::bytecode {
+    my $sv = shift;
+    return if saved($sv);
+    $sv->B::SV::bytecode;
+    printf "xnv %s\n", $sv->NVX;
+}
+
+sub B::RV::bytecode {
+    my $sv = shift;
+    return if saved($sv);
+    my $rvix = $sv->RV->objix;
+    $sv->B::SV::bytecode;
+    print "xrv $rvix\n";
+}
+
+sub B::PVIV::bytecode {
+    my $sv = shift;
+    return if saved($sv);
+    my $iv = $sv->IVX;
+    $sv->B::PV::bytecode;
+    printf "%s $iv\n", $sv->needs64bits ? "xiv64" : "xiv32";
+}
+
+sub B::PVNV::bytecode {
+    my ($sv, $flag) = @_;
+    # The $flag argument is passed through PVMG::bytecode by BM::bytecode
+    # and AV::bytecode and indicates special handling. $flag = 1 is used by
+    # BM::bytecode and means that we should ensure we save the whole B-M
+    # table. It consists of 257 bytes (256 char array plus a final \0)
+    # which follow the ordinary PV+\0 and the 257 bytes are *not* reflected
+    # in SvCUR. $flag = 2 is used by AV::bytecode and means that we only
+    # call SV::bytecode instead of saving PV and calling NV::bytecode since
+    # PV/NV/IV stuff is different for AVs.
+    return if saved($sv);
+    if ($flag == 2) {
+       $sv->B::SV::bytecode;
+    } else {
+       my $pv = $sv->PV;
+       $sv->B::IV::bytecode;
+       printf "xnv %s\n", $sv->NVX;
+       if ($flag == 1) {
+           $pv .= "\0" . $sv->TABLE;
+           printf "newpv %s\npv_cur %d\nxpv\n", pvstring($pv),length($pv)-257;
+       } else {
+           printf("newpv %s\nxpv\n", pvstring($pv)) if $sv->FLAGS & POK;
+       }
+    }
+}
+
+sub B::PVMG::bytecode {
+    my ($sv, $flag) = @_;
+    # See B::PVNV::bytecode for an explanation of $flag.
+    return if saved($sv);
+    # XXX We assume SvSTASH is already saved and don't save it later ourselves
+    my $stashix = $sv->SvSTASH->objix;
+    my @mgchain = $sv->MAGIC;
+    my (@mgobjix, $mg);
+    #
+    # We need to traverse the magic chain and get objix for each OBJ
+    # field *before* we do B::PVNV::bytecode since objix overwrites
+    # the sv register. However, we need to write the magic-saving
+    # bytecode *after* B::PVNV::bytecode since sv isn't initialised
+    # to refer to $sv until then.
+    #
+    @mgobjix = map($_->OBJ->objix, @mgchain);
+    $sv->B::PVNV::bytecode($flag);
+    print "xmg_stash $stashix\n";
+    foreach $mg (@mgchain) {
+       printf "sv_magic %s\nmg_obj %d\nnewpv %s\nmg_pv\n",
+           cstring($mg->TYPE), shift(@mgobjix), pvstring($mg->PTR);
+    }
+}
+
+sub B::PVLV::bytecode {
+    my $sv = shift;
+    return if saved($sv);
+    $sv->B::PVMG::bytecode;
+    printf <<'EOT', $sv->TARGOFF, $sv->TARGLEN, cstring($sv->TYPE);
+xlv_targoff %d
+xlv_targlen %d
+xlv_type %s
+EOT
+}
+
+sub B::BM::bytecode {
+    my $sv = shift;
+    return if saved($sv);
+    # See PVNV::bytecode for an explanation of what the argument does
+    $sv->B::PVMG::bytecode(1);
+    printf "xbm_useful %d\nxbm_previous %d\nxbm_rare %d\n",
+       $sv->USEFUL, $sv->PREVIOUS, $sv->RARE;
+}
+
+sub B::GV::bytecode {
+    my $gv = shift;
+    return if saved($gv);
+    my $ix = $gv->objix;
+    mark_saved($gv);
+    my $gvname = $gv->NAME;
+    my $name = cstring($gv->STASH->NAME . "::" . $gvname);
+    my $egv = $gv->EGV;
+    my $egvix = $egv->objix;
+    ldsv($ix);
+    printf <<"EOT", $gv->FLAGS, $gv->GvFLAGS, $gv->LINE;
+sv_flags 0x%x
+xgv_flags 0x%x
+gp_line %d
+EOT
+    my $refcnt = $gv->REFCNT;
+    printf("sv_refcnt_add %d\n", $refcnt - 1) if $refcnt > 1;
+    my $gvrefcnt = $gv->GvREFCNT;
+    printf("gp_refcnt_add %d\n", $gvrefcnt - 1) if $gvrefcnt > 1;
+    if ($gvrefcnt > 1 &&  $ix != $egvix) {
+       print "gp_share $egvix\n";
+    } else {
+       if ($gvname !~ /^([^A-Za-z]|STDIN|STDOUT|STDERR|ARGV|SIG|ENV)$/) {
+           my $i;
+           my @subfield_names = qw(SV AV HV CV FILEGV FORM IO);
+           my @subfields = map($gv->$_(), @subfield_names);
+           my @ixes = map($_->objix, @subfields);
+           # Reset sv register for $gv
+           ldsv($ix);
+           for ($i = 0; $i < @ixes; $i++) {
+               printf "gp_%s %d\n", lc($subfield_names[$i]), $ixes[$i];
+           }
+           # Now save all the subfields
+           my $sv;
+           foreach $sv (@subfields) {
+               $sv->bytecode;
+           }
+       }
+    }
+}
+
+sub B::HV::bytecode {
+    my $hv = shift;
+    return if saved($hv);
+    mark_saved($hv);
+    my $name = $hv->NAME;
+    my $ix = $hv->objix;
+    if (!$name) {
+       # It's an ordinary HV. Stashes have NAME set and need no further
+       # saving beyond the gv_stashpv that $hv->objix already ensures.
+       #
+       # XXX We don't yet save the contents of non-empty HVs
+       ldsv($ix);
+       printf "sv_refcnt %d\nsv_flags 0x%x\n", $hv->REFCNT, $hv->FLAGS;
+    }
+}
+
+sub B::AV::bytecode {
+    my $av = shift;
+    return if saved($av);
+    my $ix = $av->objix;
+    my $fill = $av->FILL;
+    my $max = $av->MAX;
+    my (@array, @ixes);
+    if ($fill > -1) {
+       @array = $av->ARRAY;
+       @ixes = map($_->objix, @array);
+       my $sv;
+       foreach $sv (@array) {
+           $sv->bytecode;
+       }
+    }
+    # See PVNV::bytecode for the meaning of the flag argument of 2.
+    $av->B::PVMG::bytecode(2);
+    # Recover sv register and set AvMAX and AvFILL to -1 (since we
+    # create an AV with NEWSV and SvUPGRADE rather than doing newAV
+    # which is what sets AvMAX and AvFILL.
+    ldsv($ix);
+    printf "xav_flags 0x%x\nxav_max -1\nxav_fill -1\n", $av->AvFLAGS;
+    if ($fill > -1) {
+       my $elix;
+       foreach $elix (@ixes) {
+           print "av_push $elix\n";
+       }
+    } else {
+       if ($max > -1) {
+           print "av_extend $max\n";
+       }
+    }
+}
+
+sub B::CV::bytecode {
+    my $cv = shift;
+    return if saved($cv);
+    my $ix = $cv->objix;
+    $cv->B::PVMG::bytecode;
+    my $i;
+    my @subfield_names = qw(ROOT START STASH GV FILEGV PADLIST OUTSIDE);
+    my @subfields = map($cv->$_(), @subfield_names);
+    my @ixes = map($_->objix, @subfields);
+    # Save OP tree from CvROOT (first element of @subfields)
+    my $root = shift @subfields;
+    if (ad($root)) {
+       walkoptree($root, "bytecode");
+    }
+    # Reset sv register for $cv (since above ->objix calls stomped on it)
+    ldsv($ix);
+    for ($i = 0; $i < @ixes; $i++) {
+       printf "xcv_%s %d\n", lc($subfield_names[$i]), $ixes[$i];
+    }
+    printf "xcv_depth %d\nxcv_flags 0x%x\n", $cv->DEPTH, $cv->FLAGS;
+    # Now save all the subfields (except for CvROOT which was handled
+    # above) and CvSTART (now the initial element of @subfields).
+    shift @subfields; # bye-bye CvSTART
+    my $sv;
+    foreach $sv (@subfields) {
+       $sv->bytecode;
+    }
+}
+
+sub B::IO::bytecode {
+    my $io = shift;
+    return if saved($io);
+    my $ix = $io->objix;
+    my $top_gv = $io->TOP_GV;
+    my $top_gvix = $top_gv->objix;
+    my $fmt_gv = $io->FMT_GV;
+    my $fmt_gvix = $fmt_gv->objix;
+    my $bottom_gv = $io->BOTTOM_GV;
+    my $bottom_gvix = $bottom_gv->objix;
+
+    $io->B::PVMG::bytecode;
+    ldsv($ix);
+    print "xio_top_gv $top_gvix\n";
+    print "xio_fmt_gv $fmt_gvix\n";
+    print "xio_bottom_gv $bottom_gvix\n";
+    my $field;
+    foreach $field (qw(TOP_NAME FMT_NAME BOTTOM_NAME)) {
+       printf "newpv %s\nxio_%s\n", pvstring($io->$field()), lc($field);
+    }
+    foreach $field (qw(LINES PAGE PAGE_LEN LINES_LEFT SUBPROCESS)) {
+       printf "xio_%s %d\n", lc($field), $io->$field();
+    }
+    printf "xio_type %s\nxio_flags 0x%x\n", cstring($io->IoTYPE), $io->IoFLAGS;
+    $top_gv->bytecode;
+    $fmt_gv->bytecode;
+    $bottom_gv->bytecode;
+}
+
+sub B::SPECIAL::bytecode {
+    # nothing extra needs doing
+}
+
+sub bytecompile_object {
+    my $sv;
+    foreach $sv (@_) {
+       svref_2object($sv)->bytecode;
+    }
+}
+
+sub B::GV::bytecodecv {
+    my $gv = shift;
+    my $cv = $gv->CV;
+    if (ad($cv) && !saved($cv)) {
+       if ($debug_cv) {
+           warn sprintf("saving extra CV &%s::%s (0x%x) from GV 0x%x\n",
+                        $gv->STASH->NAME, $gv->NAME, ad($cv), ad($gv));
+       }
+       $gv->bytecode;
+    }
+}
+
+sub bytecompile_main {
+    my $curpad = (comppadlist->ARRAY)[1];
+    my $curpadix = $curpad->objix;
+    $curpad->bytecode;
+    walkoptree(main_root, "bytecode");
+    my ($pack, %exclude);
+    foreach $pack (qw(B O AutoLoader DynaLoader Config DB VMS
+                     strict vars FileHandle Exporter Carp)) {
+       $exclude{$pack."::"} = 1;
+    }
+    no strict qw(vars refs);
+    walksymtable(\%{"main::"}, "bytecodecv",sub { !defined($exclude{$_[0]}) });
+    if (!$module_only) {
+       printf "main_root %d\n", main_root->objix;
+       printf "main_start %d\n", main_start->objix;
+       printf "curpad $curpadix\n";
+       # XXX Do min_intro_pending and max_intro_pending matter?
+    }
+}
+
+sub prepare_output {
+    # Plumbing for output
+    if (!$no_assemble) {
+       pipe(READER, WRITER) or die "pipe: $!\n";
+       $assembler_pid = fork();
+       die "fork: $!\n" unless defined($assembler_pid);
+       if ($assembler_pid) {
+           # parent
+           close WRITER;
+           assemble_fh(\*READER, sub { print @_ });
+           exit(0);
+       } else {
+           # child
+           close READER;
+           open(STDOUT, ">&WRITER") or die "dup: $!\n";
+       }
+    }
+}
+
+sub compile {
+    my @options = @_;
+    my ($option, $opt, $arg);
+  OPTION:
+    while ($option = shift @options) {
+       if ($option =~ /^-(.)(.*)/) {
+           $opt = $1;
+           $arg = $2;
+       } else {
+           unshift @options, $option;
+           last OPTION;
+       }
+       if ($opt eq "-" && $arg eq "-") {
+           shift @options;
+           last OPTION;
+       } elsif ($opt eq "o") {
+           $arg ||= shift @options;
+           open(STDOUT, ">$arg") or return "$arg: $!\n";
+       } elsif ($opt eq "D") {
+           $arg ||= shift @options;
+           foreach $arg (split(//, $arg)) {
+               if ($arg eq "o") {
+                   B->debug(1);
+               } elsif ($arg eq "a") {
+                   B::Assembler::debug(1);
+               } elsif ($arg eq "D") {
+                   $debug_cv = 1;
+               }
+           }
+       } elsif ($opt eq "v") {
+           $verbose = 1;
+       } elsif ($opt eq "m") {
+           $module_only = 1;
+       } elsif ($opt eq "S") {
+           $no_assemble = 1;
+       } elsif ($opt eq "f") {
+           $arg ||= shift @options;
+           my $value = $arg !~ s/^no-//;
+           $arg =~ s/-/_/g;
+           my $ref = $optimise{$arg};
+           if (defined($ref)) {
+               $$ref = $value;
+           } else {
+               warn qq(ignoring unknown optimisation option "$arg"\n);
+           }
+       } elsif ($opt eq "O") {
+           $arg = 1 if $arg eq "";
+           my $ref;
+           foreach $ref (values %optimise) {
+               $$ref = 0;
+           }
+           if ($arg >= 6) {
+               $strip_syntree = 1;
+           }
+           if ($arg >= 2) {
+               $bypass_nullops = 1;
+           }
+           if ($arg >= 1) {
+               $compress_nullops = 1;
+               $omit_seq = 1;
+           }
+       }
+    }
+    if (@options) {
+       return sub {
+           my $objname;
+           prepare_output();
+           foreach $objname (@options) {
+               eval "bytecompile_object(\\$objname)";
+           }
+           waitpid($assembler_pid, 0) if defined($assembler_pid);
+       }
+    } else {
+       return sub {
+           prepare_output();
+           bytecompile_main();
+           waitpid($assembler_pid, 0) if defined($assembler_pid);
+       }
+    }
+}
+
+1;
diff --git a/B/C.pm b/B/C.pm
new file mode 100644 (file)
index 0000000..338b9c9
--- /dev/null
+++ b/B/C.pm
@@ -0,0 +1,1197 @@
+#      C.pm
+#
+#      Copyright (c) 1996 Malcolm Beattie
+#
+#      You may distribute under the terms of either the GNU General Public
+#      License or the Artistic License, as specified in the README file.
+#
+package B::C;
+use Exporter ();
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(push_decl init_init push_init output_all output_boilerplate
+              output_main set_callback save_unused_subs objsym);
+
+use B qw(minus_c sv_undef walkoptree walksymtable main_root main_start
+        ad peekop class cstring cchar svref_2object compile_stats
+        comppadlist hash);
+use B::Asmdata qw(@specialsv_name);
+
+use FileHandle;
+use Carp;
+use strict;
+
+my $hv_index = 0;
+my $gv_index = 0;
+my $re_index = 0;
+my $pv_index = 0;
+my $anonsub_index = 0;
+my (@binop_list, @condop_list, @cop_list, @cvop_list, @decl_list,
+    @gvop_list, @listop_list, @logop_list, @loop_list, @op_list, @pmop_list,
+    @pvop_list, @sv_list, @svop_list, @unop_list, @xpv_list,
+    @xpvav_list, @xpvhv_list, @xpvcv_list, @xpviv_list, @xpvnv_list, 
+    @xpvmg_list, @xpvlv_list, @xrv_list, @xpvbm_list, @xpvio_list);
+
+my $init_list_fh;
+my %symtable;
+my $warn_undefined_syms;
+my $verbose;
+my @unused_sub_packages;
+my $nullop_count;
+my $pv_copy_on_grow;
+my ($debug_cops, $debug_av, $debug_cv, $debug_mg);
+
+sub walk_and_save_optree;
+my $saveoptree_callback = \&walk_and_save_optree;
+sub set_callback { $saveoptree_callback = shift }
+sub saveoptree { &$saveoptree_callback(@_) }
+
+sub walk_and_save_optree {
+    my ($name, $root, $start) = @_;
+    walkoptree($root, "save");
+    return objsym($start);
+}
+
+sub push_decl {
+    push(@decl_list, @_);
+}
+
+sub init_init {
+    $init_list_fh->close if defined $init_list_fh;
+    $init_list_fh = FileHandle->new_tmpfile;
+    return $init_list_fh ? 1 : 0;
+}
+
+sub push_init {
+    map { print $init_list_fh $_, "\n" } @_;
+}
+
+# Current workaround/fix for op_free() trying to free statically
+# defined OPs is to set op_seq = -1 and check for that in op_free().
+# Instead of hardwiring -1 in place of $op->seq, we use $op_seq
+# so that it can be changed back easily if necessary. In fact, to
+# stop compilers from moaning about a U16 being initialised with an
+# uncast -1 (the printf format is %d so we can't tweak it), we have
+# to "know" that op_seq is a U16 and use 65535. Ugh.
+my $op_seq = 65535;
+
+sub AVf_REAL () { 1 }
+
+sub savesym {
+    my ($obj, $value) = @_;
+#    warn(sprintf("savesym: sym_%x => %s\n", ad($obj), $value)); # debug
+    $symtable{sprintf("sym_%x", ad($obj))} = $value;
+}
+
+sub objsym {
+    my $obj = shift;
+    return $symtable{sprintf("sym_%x", ad($obj))};
+}
+
+sub getsym {
+    my $sym = shift;
+    my $value;
+
+    return 0 if $sym eq "sym_0";       # special case
+    $value = $symtable{$sym};
+    if (defined($value)) {
+       return $value;
+    } else {
+       warn "warning: undefined symbol $sym\n" if $warn_undefined_syms;
+       return "UNUSED";
+    }
+}
+
+sub fixsyms {
+    $_[0] =~ s/(sym_[0-9a-f]+)/getsym($1)/ge;
+}
+
+sub savepv {
+    my $pv = shift;
+    my $pvsym = 0;
+    my $pvmax = 0;
+    if ($pv_copy_on_grow) {
+       my $cstring = cstring($pv);
+       if ($cstring ne "0") { # sic
+           $pvsym = sprintf("pv%d", $pv_index++);
+           push(@decl_list,sprintf("static char %s[] = %s;",$pvsym,$cstring));
+       }
+    } else {
+       $pvmax = length($pv) + 1;
+    }
+    return ($pvsym, $pvmax);
+}
+
+sub B::OP::save {
+    my ($op, $level) = @_;
+    my $type = $op->type;
+    $nullop_count++ unless $type;
+    push(@op_list,
+        sprintf("sym_%x, sym_%x, %s, %u, %u, %u, 0x%x, 0x%x",
+                ad($op->next), ad($op->sibling), $op->ppaddr, $op->targ,
+                $type, $op_seq, $op->flags, $op->private));
+    savesym($op, "&op_list[$#op_list]");
+}
+
+sub B::FAKEOP::new {
+    my ($class, %objdata) = @_;
+    bless \%objdata, $class;
+}
+
+sub B::FAKEOP::save {
+    my ($op, $level) = @_;
+    push(@op_list,
+        sprintf("%s, %s, %s, %u, %u, %u, 0x%x, 0x%x",
+                $op->next, $op->sibling, $op->ppaddr, $op->targ,
+                $op->type, $op_seq, $op->flags, $op->private));
+    return "&op_list[$#op_list]";
+}
+
+sub B::FAKEOP::next { $_[0]->{"next"} || 0 }
+sub B::FAKEOP::type { $_[0]->{type} || 0}
+sub B::FAKEOP::sibling { $_[0]->{sibling} || 0 }
+sub B::FAKEOP::ppaddr { $_[0]->{ppaddr} || 0 }
+sub B::FAKEOP::targ { $_[0]->{targ} || 0 }
+sub B::FAKEOP::flags { $_[0]->{flags} || 0 }
+sub B::FAKEOP::private { $_[0]->{private} || 0 }
+
+sub B::UNOP::save {
+    my ($op, $level) = @_;
+    push(@unop_list,
+        sprintf("sym_%x, sym_%x, %s, %u, %u, %u, 0x%x, 0x%x, sym_%x",
+                ad($op->next), ad($op->sibling), $op->ppaddr, $op->targ,
+                $op->type, $op_seq, $op->flags,$op->private,ad($op->first)));
+    savesym($op, "(OP*)&unop_list[$#unop_list]");
+}
+
+sub B::BINOP::save {
+    my ($op, $level) = @_;
+    push(@binop_list,
+        sprintf("sym_%x, sym_%x, %s, %u, %u, %u, 0x%x, 0x%x, sym_%x, sym_%x",
+                ad($op->next), ad($op->sibling), $op->ppaddr, $op->targ,
+                $op->type, $op_seq, $op->flags, $op->private,
+                ad($op->first), ad($op->last)));
+    savesym($op, "(OP*)&binop_list[$#binop_list]");
+}
+
+sub B::LISTOP::save {
+    my ($op, $level) = @_;
+    push(@listop_list, sprintf(
+       "sym_%x, sym_%x, %s, %u, %u, %u, 0x%x, 0x%x, sym_%x, sym_%x, %u",
+       ad($op->next), ad($op->sibling), $op->ppaddr, $op->targ,
+       $op->type, $op_seq, $op->flags, $op->private, ad($op->first),
+       ad($op->last), $op->children));
+    savesym($op, "(OP*)&listop_list[$#listop_list]");
+}
+
+sub B::LOGOP::save {
+    my ($op, $level) = @_;
+    push(@logop_list,
+        sprintf("sym_%x, sym_%x, %s, %u, %u, %u, 0x%x, 0x%x, sym_%x, sym_%x",
+                ad($op->next), ad($op->sibling), $op->ppaddr, $op->targ,
+                $op->type, $op_seq, $op->flags, $op->private,
+                ad($op->first), ad($op->other)));
+    savesym($op, "(OP*)&logop_list[$#logop_list]");
+}
+
+sub B::CONDOP::save {
+    my ($op, $level) = @_;
+    push(@condop_list, sprintf(
+       "sym_%x, sym_%x, %s, %u, %u, %u, 0x%x, 0x%x, sym_%x, sym_%x, sym_%x",
+       ad($op->next), ad($op->sibling), $op->ppaddr, $op->targ,
+       $op->type, $op_seq, $op->flags, $op->private, ad($op->first),
+       ad($op->true), ad($op->false)));
+    savesym($op, "(OP*)&condop_list[$#condop_list]");
+}
+
+sub B::LOOP::save {
+    my ($op, $level) = @_;
+    #warn sprintf("LOOP: redoop %s, nextop %s, lastop %s\n",
+    #           peekop($op->redoop), peekop($op->nextop),
+    #           peekop($op->lastop)); # debug
+    push(@loop_list, sprintf(
+       "sym_%x, sym_%x, %s, %u, %u, %u, 0x%x, 0x%x, "
+       ."sym_%x, sym_%x, %u, sym_%x, sym_%x, sym_%x",
+       ad($op->next), ad($op->sibling), $op->ppaddr, $op->targ, $op->type,
+       $op_seq, $op->flags, $op->private, ad($op->first), ad($op->last),
+       $op->children, ad($op->redoop), ad($op->nextop), ad($op->lastop)));
+    savesym($op, "(OP*)&loop_list[$#loop_list]");
+}
+
+sub B::PVOP::save {
+    my ($op, $level) = @_;
+    push(@pvop_list,
+        sprintf("sym_%x, sym_%x, %s, %u, %u, %u, 0x%x, 0x%x, %s",
+                ad($op->next), ad($op->sibling), $op->ppaddr, $op->targ,
+                $op->type, $op_seq, $op->flags, $op->private,
+                cstring($op->pv)));
+    savesym($op, "(OP*)&pvop_list[$#pvop_list]");
+}
+
+sub B::SVOP::save {
+    my ($op, $level) = @_;
+    push(@svop_list,
+        sprintf("sym_%x, sym_%x, %s, %u, %u, %u, 0x%x, 0x%x, (SV*)sym_%x",
+                ad($op->next), ad($op->sibling), $op->ppaddr, $op->targ,
+                $op->type, $op_seq, $op->flags, $op->private, ad($op->sv)));
+    savesym($op, "(OP*)&svop_list[$#svop_list]");
+#    warn sprintf("svop saving sv %s 0x%x\n", ref($op->sv), ad($op->sv));#debug
+    $op->sv->save;
+}
+
+sub B::GVOP::save {
+    my ($op, $level) = @_;
+    my $gvsym = $op->gv->save;
+    push(@gvop_list,
+        sprintf("sym_%x, sym_%x, %s, %u, %u, %u, 0x%x, 0x%x, Nullgv",
+                ad($op->next), ad($op->sibling), $op->ppaddr, $op->targ,
+                $op->type, $op_seq, $op->flags, $op->private));
+    push_init(sprintf("gvop_list[$#gvop_list].op_gv = %s;", $gvsym));
+    savesym($op, "(OP*)&gvop_list[$#gvop_list]");
+}
+
+sub B::COP::save {
+    my ($op, $level) = @_;
+    my $gvsym = $op->filegv->save;
+    my $stashsym = $op->stash->save;
+    warn sprintf("COP: line %d file %s\n", $op->line, $op->filegv->SV->PV)
+       if $debug_cops;
+    push(@cop_list,
+        sprintf("sym_%x, sym_%x, %s, %u, %u, %u, 0x%x, 0x%x, %s, "
+                ."Nullhv, Nullgv, %u, %d, %u",
+                ad($op->next), ad($op->sibling), $op->ppaddr, $op->targ,
+                $op->type, $op_seq, $op->flags, $op->private,
+                cstring($op->label), $op->cop_seq, $op->arybase, $op->line));
+    push_init(sprintf("cop_list[$#cop_list].cop_filegv = %s;", $gvsym),
+             sprintf("cop_list[$#cop_list].cop_stash = %s;", $stashsym));
+    savesym($op, "(OP*)&cop_list[$#cop_list]");
+}
+
+sub B::PMOP::save {
+    my ($op, $level) = @_;
+    my $shortsym = $op->pmshort->save;
+    my $replroot = $op->pmreplroot;
+    my $replstart = $op->pmreplstart;
+    my $replrootfield = sprintf("sym_%x", ad($replroot));
+    my $replstartfield = sprintf("sym_%x", ad($replstart));
+    my $gvsym;
+    my $ppaddr = $op->ppaddr;
+    if (ad($replroot)) {
+       # OP_PUSHRE (a mutated version of OP_MATCH for the regexp
+       # argument to a split) stores a GV in op_pmreplroot instead
+       # of a substitution syntax tree. We don't want to walk that...
+       if ($ppaddr eq "pp_pushre") {
+           $gvsym = $replroot->save;
+#          warn "PMOP::save saving a pp_pushre with GV $gvsym\n"; # debug
+           $replrootfield = 0;
+       } else {
+           $replstartfield = saveoptree("*ignore*", $replroot, $replstart);
+       }
+    }
+    # pmnext handling is broken in perl itself, I think. Bad op_pmnext
+    # fields aren't noticed in perl's runtime (unless you try reset) but we
+    # segfault when trying to dereference it to find op->op_pmnext->op_type
+    push(@pmop_list,
+        sprintf("sym_%x, sym_%x, %s, %u, %u, %u, 0x%x, 0x%x, sym_%x, sym_%x,"
+                ." %u, %s, %s, 0, 0, %s, 0x%x, 0x%x, %u",
+                ad($op->next), ad($op->sibling), $ppaddr, $op->targ,
+                $op->type, $op_seq, $op->flags, $op->private,
+                ad($op->first), ad($op->last), $op->children,
+                $replrootfield, $replstartfield,
+                $shortsym, $op->pmflags, $op->pmpermflags, $op->pmslen));
+    my $pm = "pmop_list[$#pmop_list]";
+    my $re = $op->precomp;
+    if (defined($re)) {
+       my $resym = sprintf("re%d", $re_index++);
+       push(@decl_list, sprintf("static char *$resym = %s;", cstring($re)));
+       push_init(sprintf(
+           "$pm.op_pmregexp = pregcomp($resym, $resym + %u, &$pm);",
+           length($re)));
+    }
+    if ($gvsym) {
+       push_init("$pm.op_pmreplroot = (OP*)$gvsym;");
+    }
+    savesym($op, "(OP*)&pmop_list[$#pmop_list]");
+}
+
+sub B::SPECIAL::save {
+    my ($sv) = @_;
+    # special case: $$sv is not the address but an index into specialsv_list
+#   warn "SPECIAL::save specialsv $$sv\n"; # debug
+    my $sym = $specialsv_name[$$sv];
+    if (!defined($sym)) {
+       confess "unknown specialsv index $$sv passed to B::SPECIAL::save";
+    }
+    return $sym;
+}
+
+sub B::OBJECT::save {}
+
+sub B::NULL::save {
+    my ($sv) = @_;
+    my $sym = objsym($sv);
+    return $sym if defined $sym;
+#   warn "Saving SVt_NULL SV\n"; # debug
+    # debug
+    #if ($$sv == 0) {
+    #  warn "NULL::save for sv = 0 called from @{[(caller(1))[3]]}\n";
+    #}
+    push(@sv_list, sprintf("0, %u, 0x%x", $sv->REFCNT + 1, $sv->FLAGS));
+    return savesym($sv, "&sv_list[$#sv_list]");
+}
+
+sub B::IV::save {
+    my ($sv) = @_;
+    my $sym = objsym($sv);
+    return $sym if defined $sym;
+    push(@xpviv_list, sprintf("0, 0, 0, %d", $sv->IVX));
+    push(@sv_list, sprintf("&xpviv_list[$#xpviv_list], %lu, 0x%x",
+                          $sv->REFCNT + 1, $sv->FLAGS));
+    return savesym($sv, "&sv_list[$#sv_list]");
+}
+
+sub B::NV::save {
+    my ($sv) = @_;
+    my $sym = objsym($sv);
+    return $sym if defined $sym;
+    push(@xpvnv_list, sprintf("0, 0, 0, %d, %s", $sv->IVX, $sv->NVX));
+    push(@sv_list, sprintf("&xpvnv_list[$#xpvnv_list], %lu, 0x%x",
+                          $sv->REFCNT + 1, $sv->FLAGS));
+    return savesym($sv, "&sv_list[$#sv_list]");
+}
+
+sub B::PVLV::save {
+    my ($sv) = @_;
+    my $sym = objsym($sv);
+    return $sym if defined $sym;
+    my $pv = $sv->PV;
+    my $len = length($pv);
+    my ($pvsym, $pvmax) = savepv($pv);
+    my ($lvtarg, $lvtarg_sym);
+    push(@xpvlv_list, sprintf("%s, %u, %u, %d, %g, 0, 0, %u, %u, 0, %s",
+                             $pvsym, $len, $pvmax, $sv->IVX, $sv->NVX, 
+                             $sv->TARGOFF, $sv->TARGLEN, cchar($sv->TYPE)));
+      
+    push(@sv_list, sprintf("&xpvlv_list[$#xpvlv_list], %lu, 0x%x",
+                          $sv->REFCNT + 1, $sv->FLAGS));
+    if (!$pv_copy_on_grow) {
+       push_init(sprintf("xpvlv_list[$#xpvlv_list].xpv_pv = savepvn(%s, %u);",
+                         cstring($pv), $len));
+    }
+    $sv->save_magic;
+    return savesym($sv, "&sv_list[$#sv_list]");
+}
+
+sub B::PVIV::save {
+    my ($sv) = @_;
+    my $sym = objsym($sv);
+    return $sym if defined $sym;
+    my $pv = $sv->PV;
+    my $len = length($pv);
+    my ($pvsym, $pvmax) = savepv($pv);
+    push(@xpviv_list, sprintf("%s, %u, %u, %d", $pvsym, $len,$pvmax,$sv->IVX));
+    push(@sv_list, sprintf("&xpviv_list[$#xpviv_list], %u, 0x%x",
+                          $sv->REFCNT + 1, $sv->FLAGS));
+    if (!$pv_copy_on_grow) {
+       push_init(sprintf("xpviv_list[$#xpviv_list].xpv_pv = savepvn(%s, %u);",
+                         cstring($pv), $len));
+    }
+    return savesym($sv, "&sv_list[$#sv_list]");
+}
+
+sub B::PVNV::save {
+    my ($sv) = @_;
+    my $sym = objsym($sv);
+    return $sym if defined $sym;
+    my $pv = $sv->PV;
+    my $len = length($pv);
+    my ($pvsym, $pvmax) = savepv($pv);
+    push(@xpvnv_list, sprintf("%s, %u, %u, %d, %s",
+                             $pvsym, $len, $pvmax, $sv->IVX, $sv->NVX));
+    push(@sv_list, sprintf("&xpvnv_list[$#xpvnv_list], %lu, 0x%x",
+                          $sv->REFCNT + 1, $sv->FLAGS));
+    if (!$pv_copy_on_grow) {
+       push_init(sprintf("xpvnv_list[$#xpvnv_list].xpv_pv = savepvn(%s, %u);",
+                         cstring($pv), $len));
+    }
+    return savesym($sv, "&sv_list[$#sv_list]");
+}
+
+sub B::BM::save {
+    my ($sv) = @_;
+    my $sym = objsym($sv);
+    return $sym if defined $sym;
+    my $pv = $sv->PV . "\0" . $sv->TABLE;
+    my $len = length($pv);
+    push(@xpvbm_list, sprintf("0, %u, %u, %d, %s, 0, 0, %d, %u, 0x%x",
+                             $len, $len + 258, $sv->IVX, $sv->NVX,
+                             $sv->USEFUL, $sv->PREVIOUS, $sv->RARE));
+    push(@sv_list, sprintf("&xpvbm_list[$#xpvbm_list], %lu, 0x%x",
+                          $sv->REFCNT + 1, $sv->FLAGS));
+    $sv->save_magic;
+    push_init(sprintf("xpvbm_list[$#xpvbm_list].xpv_pv = savepvn(%s, %u);",
+                     cstring($pv), $len),
+             sprintf("xpvbm_list[$#xpvbm_list].xpv_cur = %u;", $len - 257));
+#            "sv_magic(&sv_list[$#sv_list], Nullsv, 'B', Nullch, 0);");
+    return savesym($sv, "&sv_list[$#sv_list]");
+}
+
+sub B::PV::save {
+    my ($sv) = @_;
+    my $sym = objsym($sv);
+    return $sym if defined $sym;
+    my $pv = $sv->PV;
+    my $len = length($pv);
+    my ($pvsym, $pvmax) = savepv($pv);
+    push(@xpv_list, sprintf("%s, %u, %u", $pvsym, $len, $pvmax));
+    push(@sv_list, sprintf("&xpv_list[$#xpv_list], %lu, 0x%x",
+                          $sv->REFCNT + 1, $sv->FLAGS));
+    if (!$pv_copy_on_grow) {
+       push_init(sprintf("xpv_list[$#xpv_list].xpv_pv = savepvn(%s, %u);",
+                         cstring($pv), $len));
+    }
+    return savesym($sv, "&sv_list[$#sv_list]");
+}
+
+sub B::PVMG::save {
+    my ($sv) = @_;
+    my $sym = objsym($sv);
+    return $sym if defined $sym;
+    my $pv = $sv->PV;
+    my $len = length($pv);
+    my ($pvsym, $pvmax) = savepv($pv);
+    push(@xpvmg_list, sprintf("%s, %u, %u, %d, %s, 0, 0",
+                             $pvsym, $len, $pvmax, $sv->IVX, $sv->NVX));
+    push(@sv_list, sprintf("&xpvmg_list[$#xpvmg_list], %lu, 0x%x",
+                          $sv->REFCNT + 1, $sv->FLAGS));
+    if (!$pv_copy_on_grow) {
+       push_init(sprintf("xpvmg_list[$#xpvmg_list].xpv_pv = savepvn(%s, %u);",
+                         cstring($pv), $len));
+    }
+    $sym = savesym($sv, "&sv_list[$#sv_list]");
+    $sv->save_magic;
+    return $sym;
+}
+
+sub B::PVMG::save_magic {
+    my ($sv) = @_;
+    #warn sprintf("saving magic for %s (0x%x)\n", class($sv), ad($sv)); # debug
+    my $stash = $sv->SvSTASH;
+    if (ad($stash)) {
+       warn sprintf("xmg_stash = %s (0x%x)\n", $stash->NAME, ad($stash))
+           if $debug_mg;
+       # XXX Hope stash is already going to be saved.
+       push_init(sprintf("SvSTASH(sym_%x) = sym_%x;", ad($sv), ad($stash)));
+    }
+    my @mgchain = $sv->MAGIC;
+    my ($mg, $type, $obj, $ptr);
+    foreach $mg (@mgchain) {
+       $type = $mg->TYPE;
+       $obj = $mg->OBJ;
+       $ptr = $mg->PTR;
+       my $len = defined($ptr) ? length($ptr) : 0;
+       if ($debug_mg) {
+           warn sprintf("magic %s (0x%x), obj %s (0x%x), type %s, ptr %s\n",
+                        class($sv), ad($sv), class($obj), ad($obj),
+                        cchar($type), cstring($ptr));
+       }
+       push_init(sprintf("sv_magic((SV*)sym_%x, (SV*)sym_%x, %s, %s, %d);",
+                         ad($sv), ad($obj), cchar($type),cstring($ptr),$len));
+    }
+}
+
+sub B::RV::save {
+    my ($sv) = @_;
+    my $sym = objsym($sv);
+    return $sym if defined $sym;
+    push(@xrv_list, $sv->RV->save);
+    push(@sv_list, sprintf("&xrv_list[$#xrv_list], %lu, 0x%x",
+                          $sv->REFCNT + 1, $sv->FLAGS));
+    return savesym($sv, "&sv_list[$#sv_list]");
+}
+
+sub B::CV::save {
+    my ($cv) = @_;
+    my $sym = objsym($cv);
+    if (defined($sym)) {
+#      warn sprintf("CV 0x%x already saved as $sym\n", ad($cv)); # debug
+       return $sym;
+    }
+    # Reserve a place on sv_list and xpvcv_list and record indices
+    push(@sv_list, undef);
+    my $sv_ix = $#sv_list;
+    push(@xpvcv_list, undef);
+    my $xpvcv_ix = $#xpvcv_list;
+    # Save symbol now so that GvCV() doesn't recurse back to us via CvGV()
+    $sym = savesym($cv, "&sv_list[$sv_ix]");
+    warn sprintf("saving CV 0x%x as $sym\n", ad($cv)) if $debug_cv;
+    my $gv = $cv->GV;
+    my $root = $cv->ROOT;
+    my $startfield = 0;
+    my $padlist = $cv->PADLIST;
+    if (ad($root)) {
+       warn sprintf("saving op tree for CV 0x%x, root = 0x%x\n",
+                    ad($cv), ad($root)) if $debug_cv;
+       my $ppname;
+       if (ad($gv)) {
+           my $stashname = $gv->STASH->NAME;
+           my $gvname = $gv->NAME;
+           $ppname = "pp_sub_";
+           $ppname .= $stashname eq "main" ? $gvname : "$stashname\::$gvname";
+           $ppname =~ s/::/__/g;
+       } else {
+           $ppname = "pp_anonsub_$anonsub_index";
+           $anonsub_index++;
+       }
+       $startfield = saveoptree($ppname, $root, $cv->START, $padlist->ARRAY);
+       warn sprintf("done saving op tree for CV 0x%x, name %s, root 0x%x\n",
+                    ad($cv), $ppname, ad($root)) if $debug_cv;
+    }
+    if (ad($padlist)) {
+       warn sprintf("saving PADLIST 0x%x for CV 0x%x\n",
+                    ad($padlist), ad($cv)) if $debug_cv;
+       $padlist->save;
+       warn sprintf("done saving PADLIST 0x%x for CV 0x%x\n",
+                    ad($padlist), ad($cv)) if $debug_cv;
+    }
+    my $pv = $cv->PV;
+    my $xsub = 0;
+    my $xsubany = "Nullany";
+    if ($cv->XSUB) {
+       $xsubany = sprintf("ANYINIT((void*)0x%x)", $cv->XSUBANY);
+       # Find out canonical name of XSUB function from EGV (I hope)
+       my $egv = $gv->EGV;
+       my $stashname = $egv->STASH->NAME;
+       $stashname =~ s/::/__/g;
+       $xsub = sprintf("XS_%s_%s", $stashname, $egv->NAME);
+       push(@decl_list, "void $xsub _((CV*));");
+    }
+    $xpvcv_list[$xpvcv_ix] = sprintf(
+       "%s, %u, 0, %d, %s, 0, Nullhv, Nullhv, %s, sym_%lx, $xsub, $xsubany,".
+       " Nullgv, Nullgv, %d, sym_%lx, (CV*)sym_%lx, 0",
+       cstring($pv), length($pv), $cv->IVX, $cv->NVX, $startfield,
+       ad($cv->ROOT), $cv->DEPTH, ad($padlist), ad($cv->OUTSIDE));
+    if (ad($gv)) {
+       $gv->save;
+       push_init(sprintf("CvGV(sym_%lx) = sym_%lx;",ad($cv),ad($gv)));
+       warn sprintf("done saving GV 0x%x for CV 0x%x\n",
+                    ad($gv), ad($cv)) if $debug_cv;
+    }
+    my $filegv = $cv->FILEGV;
+    if (ad($filegv)) {
+       $filegv->save;
+       push_init(sprintf("CvFILEGV(sym_%lx) = sym_%lx;",ad($cv),ad($filegv)));
+       warn sprintf("done saving FILEGV 0x%x for CV 0x%x\n",
+                    ad($filegv), ad($cv)) if $debug_cv;
+    }
+    my $stash = $cv->STASH;
+    if (ad($stash)) {
+       $stash->save;
+       push_init(sprintf("CvSTASH(sym_%lx) = sym_%lx;", ad($cv), ad($stash)));
+       warn sprintf("done saving STASH 0x%x for CV 0x%x\n",
+                    ad($stash), ad($cv)) if $debug_cv;
+    }
+    $sv_list[$sv_ix] = sprintf("(XPVCV*)&xpvcv_list[%u], %lu, 0x%x",
+                              $xpvcv_ix, $cv->REFCNT + 1, $cv->FLAGS);
+    return $sym;
+}
+
+sub B::GV::save {
+    my ($gv) = @_;
+    my $sym = objsym($gv);
+    if (defined($sym)) {
+       #warn sprintf("GV 0x%x already saved as $sym\n", ad($gv)); # debug
+       return $sym;
+    } else {
+       my $ix = $gv_index++;
+       $sym = savesym($gv, "gv_list[$ix]");
+       #warn sprintf("Saving GV 0x%x as $sym\n", ad($gv)); # debug
+    }
+    my $gvname = $gv->NAME;
+    my $name = cstring($gv->STASH->NAME . "::" . $gvname);
+    #warn "GV name is $name\n"; # debug
+    my $egv = $gv->EGV;
+    my $egvsym;
+    if (ad($gv) != ad($egv)) {
+       #warn(sprintf("EGV name is %s, saving it now\n",
+       #            $egv->STASH->NAME . "::" . $egv->NAME)); # debug
+       $egvsym = $egv->save;
+    }
+    push_init(qq[$sym = gv_fetchpv($name, TRUE, SVt_PV);],
+             sprintf("SvFLAGS($sym) = 0x%x;", $gv->FLAGS),
+             sprintf("GvFLAGS($sym) = 0x%x;", $gv->GvFLAGS),
+             sprintf("GvLINE($sym) = %u;", $gv->LINE));
+    # Shouldn't need to do save_magic since gv_fetchpv handles that
+    #$gv->save_magic;
+    my $refcnt = $gv->REFCNT + 1;
+    push_init(sprintf("SvREFCNT($sym) += %u;", $refcnt - 1)) if $refcnt > 1;
+    my $gvrefcnt = $gv->GvREFCNT;
+    if ($gvrefcnt > 1) {
+       push_init(sprintf("GvREFCNT($sym) += %u;", $gvrefcnt - 1));
+    }
+    if (defined($egvsym)) {
+       # Shared glob *foo = *bar
+       push_init("gp_free($sym);",
+                 "GvGP($sym) = GvGP($egvsym);");
+    } elsif ($gvname !~ /^([^A-Za-z]|STDIN|STDOUT|STDERR|ARGV|SIG|ENV)$/) {
+       # Don't save subfields of special GVs (*_, *1, *# and so on)
+#      warn "GV::save saving subfields\n"; # debug
+       my $gvsv = $gv->SV;
+       if (ad($gvsv)) {
+           push_init(sprintf("GvSV($sym) = sym_%x;", ad($gvsv)));
+#          warn "GV::save \$$name\n"; # debug
+           $gvsv->save;
+       }
+       my $gvav = $gv->AV;
+       if (ad($gvav)) {
+           push_init(sprintf("GvAV($sym) = sym_%x;", ad($gvav)));
+#          warn "GV::save \@$name\n"; # debug
+           $gvav->save;
+       }
+       my $gvhv = $gv->HV;
+       if (ad($gvhv)) {
+           push_init(sprintf("GvHV($sym) = sym_%x;", ad($gvhv)));
+#          warn "GV::save \%$name\n"; # debug
+           $gvhv->save;
+       }
+       my $gvcv = $gv->CV;
+       if (ad($gvcv)) {
+           push_init(sprintf("GvCV($sym) = (CV*)sym_%x;", ad($gvcv)));
+#          warn "GV::save &$name\n"; # debug
+           $gvcv->save;
+       }
+       my $gvfilegv = $gv->FILEGV;
+       if (ad($gvfilegv)) {
+           push_init(sprintf("GvFILEGV($sym) = sym_%x;",ad($gvfilegv)));
+#          warn "GV::save GvFILEGV(*$name)\n"; # debug
+           $gvfilegv->save;
+       }
+       my $gvform = $gv->FORM;
+       if (ad($gvform)) {
+           push_init(sprintf("GvFORM($sym) = (CV*)sym_%x;", ad($gvform)));
+#          warn "GV::save GvFORM(*$name)\n"; # debug
+           $gvform->save;
+       }
+       my $gvio = $gv->IO;
+       if (ad($gvio)) {
+           push_init(sprintf("GvIOp($sym) = sym_%x;", ad($gvio)));
+#          warn "GV::save GvIO(*$name)\n"; # debug
+           $gvio->save;
+       }
+    }
+    return $sym;
+}
+sub B::AV::save {
+    my ($av) = @_;
+    my $sym = objsym($av);
+    return $sym if defined $sym;
+    my $avflags = $av->AvFLAGS;
+    push(@xpvav_list,
+        sprintf("0, -1, -1, 0, 0.0, 0, Nullhv, 0, 0, 0x%x", $avflags));
+    push(@sv_list, sprintf("&xpvav_list[$#xpvav_list], %lu, 0x%x",
+                          $av->REFCNT + 1, $av->FLAGS));
+    my $sv_list_index = $#sv_list;
+    my $fill = $av->FILL;
+    $av->save_magic;
+    warn sprintf("saving AV 0x%x FILL=$fill AvFLAGS=0x%x", ad($av), $avflags)
+       if $debug_av;
+    # XXX AVf_REAL is wrong test: need to save comppadlist but not stack
+    #if ($fill > -1 && ($avflags & AVf_REAL)) {
+    if ($fill > -1) {
+       my @array = $av->ARRAY;
+       if ($debug_av) {
+           my $el;
+           my $i = 0;
+           foreach $el (@array) {
+               warn sprintf("AV 0x%x[%d] = %s 0x%x\n",
+                            ad($av), $i++, class($el), ad($el));
+           }
+       }
+       my @names = map($_->save, @array);
+       # XXX Better ways to write loop?
+       # Perhaps svp[0] = ...; svp[1] = ...; svp[2] = ...;
+       # Perhaps I32 i = 0; svp[i++] = ...; svp[i++] = ...; svp[i++] = ...;
+       push_init("{",
+                 "\tSV **svp;",
+                 "\tAV *av = (AV*)&sv_list[$sv_list_index];",
+                 "\tav_extend(av, $fill);",
+                 "\tsvp = AvARRAY(av);",
+             map("\t*svp++ = (SV*)$_;", @names),
+                 "\tAvFILL(av) = $fill;",
+                 "}");
+    } else {
+       my $max = $av->MAX;
+       push_init("av_extend((AV*)&sv_list[$sv_list_index], $max);")
+           if $max > -1;
+    }
+    return savesym($av, "(AV*)&sv_list[$sv_list_index]");
+}
+
+sub B::HV::save {
+    my ($hv) = @_;
+    my $sym = objsym($hv);
+    return $sym if defined $sym;
+    my $name = $hv->NAME;
+    if ($name) {
+       # It's a stash
+
+       # A perl bug means HvPMROOT isn't altered when a PMOP is freed. Usually
+       # the only symptom is that sv_reset tries to reset the PMf_USED flag of
+       # a trashed op but we look at the trashed op_type and segfault.
+       #my $adpmroot = ad($hv->PMROOT);
+       my $adpmroot = 0;
+       push(@decl_list, "static HV *hv$hv_index;");
+       # XXX Beware of weird package names containing double-quotes, \n, ...?
+       push_init(qq[hv$hv_index = gv_stashpv("$name", TRUE);]);
+       if ($adpmroot) {
+           push_init(sprintf("HvPMROOT(hv$hv_index) = (PMOP*)sym_%x;",
+                             $adpmroot));
+       }
+       $sym = savesym($hv, "hv$hv_index");
+       $hv_index++;
+       return $sym;
+    }
+    # It's just an ordinary HV
+    push(@xpvhv_list,
+        sprintf("0, 0, %d, 0, 0.0, 0, Nullhv, %d, 0, 0, 0",
+                $hv->MAX, $hv->RITER));
+    push(@sv_list, sprintf("&xpvhv_list[$#xpvhv_list], %lu, 0x%x",
+                          $hv->REFCNT + 1, $hv->FLAGS));
+    my $sv_list_index = $#sv_list;
+    my @contents = $hv->ARRAY;
+    if (@contents) {
+       my $i;
+       for ($i = 1; $i < @contents; $i += 2) {
+           $contents[$i] = $contents[$i]->save;
+       }
+       push_init("{", "\tHV *hv = (HV*)&sv_list[$sv_list_index];");
+       while (@contents) {
+           my ($key, $value) = splice(@contents, 0, 2);
+           push_init(sprintf("\thv_store(hv, %s, %u, %s, %s);",
+                             cstring($key),length($key), $value, hash($key)));
+       }
+       push_init("}");
+    }
+    return savesym($hv, "(HV*)&sv_list[$sv_list_index]");
+}
+
+sub B::IO::save {
+    my ($io) = @_;
+    my $sym = objsym($io);
+    return $sym if defined $sym;
+    my $pv = $io->PV;
+    my $len = length($pv);
+    push(@xpvio_list,
+        sprintf("0, %u, %u, %d, %s, 0, 0, 0, 0, 0, %d, %d, %d, %d, %s, "
+                ."Nullgv, %s, Nullgv, %s, Nullgv, %d, %s, 0x%x",
+                $len, $len+1, $io->IVX, $io->NVX,
+                $io->LINES, $io->PAGE, $io->PAGE_LEN, $io->LINES_LEFT, 
+                cstring($io->TOP_NAME), cstring($io->FMT_NAME), 
+                cstring($io->BOTTOM_NAME), $io->SUBPROCESS,
+                cchar($io->IoTYPE), $io->IoFLAGS));
+    push(@sv_list, sprintf("&xpvio_list[$#xpvio_list], %lu, 0x%x",
+                          $io->REFCNT + 1, $io->FLAGS));
+    $sym = savesym($io, "(IO*)&sv_list[$#sv_list]");
+    my ($field, $fsym);
+    foreach $field (qw(TOP_GV FMT_GV BOTTOM_GV)) {
+       $fsym = $io->$field();
+       if (ad($fsym)) {
+           push_init(sprintf("Io$field($sym) = (GV*)sym_%x;", ad($fsym)));
+           $fsym->save;
+       }
+    }
+    $io->save_magic;
+    return $sym;
+}
+
+sub B::SV::save {
+    my $sv = shift;
+    # This is where we catch an honest-to-goodness Nullsv (which gets
+    # blessed into B::SV explicitly) and any stray erroneous SVs.
+    return 0 unless ad($sv);
+    confess sprintf("cannot save that type of SV: %s (0x%x)\n",
+                   class($sv), ad($sv));
+}
+
+sub output_all {
+    my $init_name = shift;
+
+    output_declarations();
+    print "$_\n" while $_ = shift @decl_list;
+    print "\n";
+    output_list("op", \@op_list) if @op_list;
+    output_list("unop", \@unop_list) if @unop_list;
+    output_list("binop", \@binop_list) if @binop_list;
+    output_list("logop", \@logop_list) if @logop_list;
+    output_list("condop", \@condop_list) if @condop_list;
+    output_list("listop", \@listop_list) if @listop_list;
+    output_list("pmop", \@pmop_list) if @pmop_list;
+    output_list("svop", \@svop_list) if @svop_list;
+    output_list("gvop", \@gvop_list) if @gvop_list;
+    output_list("pvop", \@pvop_list) if @pvop_list;
+    output_list("cvop", \@cvop_list) if @cvop_list;
+    output_list("loop", \@loop_list) if @loop_list;
+    output_list("cop", \@cop_list) if @cop_list;
+
+    output_list("sv", \@sv_list) if @sv_list;
+    output_list("xrv", \@xrv_list) if @xrv_list;
+    output_list("xpv", \@xpv_list) if @xpv_list;
+    output_list("xpviv", \@xpviv_list) if @xpviv_list;
+    output_list("xpvnv", \@xpvnv_list) if @xpvnv_list;
+    output_list("xpvmg", \@xpvmg_list) if @xpvmg_list;
+    output_list("xpvlv", \@xpvlv_list) if @xpvlv_list;
+    output_list("xpvbm", \@xpvbm_list) if @xpvbm_list;
+    output_list("xpvav", \@xpvav_list) if @xpvav_list;
+    output_list("xpvhv", \@xpvhv_list) if @xpvhv_list;
+    output_list("xpvio", \@xpvio_list) if @xpvio_list;
+    output_list("xpvcv", \@xpvcv_list) if @xpvcv_list;
+
+    output_init($init_name);
+    if ($verbose) {
+       warn compile_stats();
+       warn "NULLOP count: $nullop_count\n";
+    }
+}
+
+sub output_init {
+    my $name = shift;
+    print "static int $name()\n{\n";
+    seek($init_list_fh, 0, 0);
+    while (<$init_list_fh>) {
+       fixsyms($_);
+       print "\t", $_;
+    }
+    print "\treturn 0;\n}\n";
+}
+
+sub output_list {
+    my ($name, $listref) = @_;
+    # Support pre-Standard C compilers which can't cope with static
+    # initialisation of union members. Sheesh.
+    my $typename = ($name eq "xpvcv") ? "XPVCV_or_similar" : uc($name);
+    printf "static %s %s_list[%u] = {\n", $typename, $name, scalar(@$listref);
+    while ($_ = shift @$listref) {
+       fixsyms($_);
+       print "\t{ $_ },\n";
+    }
+    print "};\n\n";
+}
+
+sub output_declarations {
+    print <<'EOT';
+#ifdef BROKEN_STATIC_REDECL
+#define Static extern
+#else
+#define Static static
+#endif /* BROKEN_STATIC_REDECL */
+
+#ifdef BROKEN_UNION_INIT
+/*
+ * Cribbed from cv.h with ANY (a union) replaced by void*.
+ * Some pre-Standard compilers can't cope with initialising unions. Ho hum.
+ */
+typedef struct {
+    char *     xpv_pv;         /* pointer to malloced string */
+    STRLEN     xpv_cur;        /* length of xp_pv as a C string */
+    STRLEN     xpv_len;        /* allocated size */
+    IV         xof_off;        /* integer value */
+    double     xnv_nv;         /* numeric value, if any */
+    MAGIC*     xmg_magic;      /* magic for scalar array */
+    HV*                xmg_stash;      /* class package */
+
+    HV *       xcv_stash;
+    OP *       xcv_start;
+    OP *       xcv_root;
+    void      (*xcv_xsub) _((CV*));
+    void *     xcv_xsubany;
+    GV *       xcv_gv;
+    GV *       xcv_filegv;
+    long       xcv_depth;              /* >= 2 indicates recursive call */
+    AV *       xcv_padlist;
+    CV *       xcv_outside;
+    U8         xcv_flags;
+} XPVCV_or_similar;
+#define ANYINIT(i) i
+#else
+#define XPVCV_or_similar XPVCV
+#define ANYINIT(i) {i}
+#endif /* BROKEN_UNION_INIT */
+#define Nullany ANYINIT(0)
+
+#define UNUSED 0
+
+EOT
+    printf("Static OP op_list[%d];\n", scalar(@op_list)) if @op_list;
+    printf("Static UNOP unop_list[%d];\n", scalar(@unop_list)) if @unop_list;
+    printf("Static BINOP binop_list[%d];\n", scalar(@binop_list))
+       if @binop_list;
+    printf("Static LOGOP logop_list[%d];\n", scalar(@logop_list))
+       if @logop_list;
+    printf("Static CONDOP condop_list[%d];\n", scalar(@condop_list))
+       if @condop_list;
+    printf("Static LISTOP listop_list[%d];\n", scalar(@listop_list))
+       if @listop_list;
+    printf("Static PMOP pmop_list[%d];\n", scalar(@pmop_list)) if @pmop_list;
+    printf("Static SVOP svop_list[%d];\n", scalar(@svop_list)) if @svop_list;
+    printf("Static GVOP gvop_list[%d];\n", scalar(@gvop_list)) if @gvop_list;
+    printf("Static PVOP pvop_list[%d];\n", scalar(@pvop_list)) if @pvop_list;
+    printf("Static CVOP cvop_list[%d];\n", scalar(@cvop_list)) if @cvop_list;
+    printf("Static LOOP loop_list[%d];\n", scalar(@loop_list)) if @loop_list;
+    printf("Static COP cop_list[%d];\n", scalar(@cop_list)) if @cop_list;
+
+    printf("Static SV sv_list[%d];\n", scalar(@sv_list)) if @sv_list;
+    printf("Static XPV xpv_list[%d];\n", scalar(@xpv_list)) if @xpv_list;
+    printf("Static XRV xrv_list[%d];\n", scalar(@xrv_list)) if @xrv_list;
+    printf("Static XPVIV xpviv_list[%d];\n", scalar(@xpviv_list))
+       if @xpviv_list;
+    printf("Static XPVNV xpvnv_list[%d];\n", scalar(@xpvnv_list))
+       if @xpvnv_list;
+    printf("Static XPVMG xpvmg_list[%d];\n", scalar(@xpvmg_list))
+       if @xpvmg_list;
+    printf("Static XPVLV xpvlv_list[%d];\n", scalar(@xpvlv_list))
+       if @xpvlv_list;
+    printf("Static XPVBM xpvbm_list[%d];\n", scalar(@xpvbm_list))
+       if @xpvbm_list;
+    printf("Static XPVAV xpvav_list[%d];\n", scalar(@xpvav_list))
+       if @xpvav_list;
+    printf("Static XPVHV xpvhv_list[%d];\n", scalar(@xpvhv_list))
+       if @xpvhv_list;
+    printf("Static XPVCV_or_similar xpvcv_list[%d];\n", scalar(@xpvcv_list))
+       if @xpvcv_list;
+    printf("Static XPVIO xpvio_list[%d];\n", scalar(@xpvio_list))
+       if @xpvio_list;
+    print "static GV *gv_list[$gv_index];\n" if $gv_index;
+    print "\n";
+}
+
+
+sub output_boilerplate {
+    print <<'EOT';
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "EXTERN.h"
+#include "perl.h"
+
+#ifdef __cplusplus
+}
+#  define EXTERN_C extern "C"
+#else
+#  define EXTERN_C extern
+#endif
+
+/* Workaround for mapstart: the only op which needs a different ppaddr */
+#undef pp_mapstart
+#define pp_mapstart pp_grepstart
+
+static void xs_init _((void));
+static PerlInterpreter *my_perl;
+EOT
+}
+
+sub output_main {
+    print <<'EOT';
+int
+#ifndef CAN_PROTOTYPE
+main(argc, argv, env)
+int argc;
+char **argv;
+char **env;
+#else  /* def(CAN_PROTOTYPE) */
+main(int argc, char **argv, char **env)
+#endif  /* def(CAN_PROTOTYPE) */
+{
+    int exitstatus;
+    int i;
+    char **fakeargv;
+
+    PERL_SYS_INIT(&argc,&argv);
+#if PATCHLEVEL > 3 || (PATCHLEVEL == 3 && SUBVERSION >= 1)
+    perl_init_i18nl10n(1);
+#else
+    perl_init_i18nl14n(1);
+#endif
+
+    if (!do_undump) {
+       my_perl = perl_alloc();
+       if (!my_perl)
+           exit(1);
+       perl_construct( my_perl );
+    }
+
+    if (!cshlen) 
+      cshlen = strlen(cshname);
+
+#ifdef ALLOW_PERL_OPTIONS
+#define EXTRA_OPTIONS 2
+#else
+#define EXTRA_OPTIONS 3
+#endif /* ALLOW_PERL_OPTIONS */
+    New(666, fakeargv, argc + EXTRA_OPTIONS + 1, char *);
+    fakeargv[0] = argv[0];
+    fakeargv[1] = "-e";
+    fakeargv[2] = "";
+#ifndef ALLOW_PERL_OPTIONS
+    fakeargv[3] = "--";
+#endif /* ALLOW_PERL_OPTIONS */
+    for (i = 1; i < argc; i++)
+       fakeargv[i + EXTRA_OPTIONS] = argv[i];
+    fakeargv[argc + EXTRA_OPTIONS] = 0;
+    
+    exitstatus = perl_parse(my_perl, xs_init, argc + EXTRA_OPTIONS,
+                           fakeargv, NULL);
+    if (exitstatus)
+       exit( exitstatus );
+
+    sv_setpv(GvSV(gv_fetchpv("0", TRUE, SVt_PV)), argv[0]);
+    main_cv = compcv;
+    compcv = 0;
+
+    exitstatus = perl_init();
+    if (exitstatus)
+       exit( exitstatus );
+
+    exitstatus = perl_run( my_perl );
+
+    perl_destruct( my_perl );
+    perl_free( my_perl );
+
+    exit( exitstatus );
+}
+
+static void
+xs_init()
+{
+}
+EOT
+}
+
+sub dump_symtable {
+    # For debugging
+    my ($sym, $val);
+    warn "----Symbol table:\n";
+    while (($sym, $val) = each %symtable) {
+       warn "$sym => $val\n";
+    }
+    warn "---End of symbol table\n";
+}
+
+sub save_object {
+    my $sv;
+    foreach $sv (@_) {
+       svref_2object($sv)->save;
+    }
+}
+
+sub B::GV::savecv {
+    my $gv = shift;
+    my $cv = $gv->CV;
+    my $name = $gv->NAME;
+    if (ad($cv) && !objsym($cv) && !($name eq "bootstrap" && $cv->XSUB)) {
+       if ($debug_cv) {
+           warn sprintf("saving extra CV &%s::%s (0x%x) from GV 0x%x\n",
+                        $gv->STASH->NAME, $name, ad($cv), ad($gv));
+       }
+       $gv->save;
+    }
+}
+
+sub save_unused_subs {
+    my %search_pack;
+    map { $search_pack{"$_\::"} = 1 } @_;
+    no strict qw(vars refs);
+    walksymtable(\%{"main::"}, "savecv", sub { exists($search_pack{$_[0]}) });
+}
+
+sub save_main {
+    my $curpad_sym = (comppadlist->ARRAY)[1]->save;
+    walkoptree(main_root, "save");
+    if (@unused_sub_packages) {
+       warn "done main optree, walking symtable for extras\n" if $debug_cv;
+       save_unused_subs(@unused_sub_packages);
+    }
+    push_init(sprintf("main_root = sym_%x;", ad(main_root)),
+             sprintf("main_start = sym_%x;", ad(main_start)),
+             "curpad = AvARRAY($curpad_sym);");
+    output_boilerplate();
+    print "\n";
+    output_all("perl_init");
+    print "\n";
+    output_main();
+}
+
+sub compile {
+    my @options = @_;
+    my ($option, $opt, $arg);
+  OPTION:
+    while ($option = shift @options) {
+       if ($option =~ /^-(.)(.*)/) {
+           $opt = $1;
+           $arg = $2;
+       } else {
+           unshift @options, $option;
+           last OPTION;
+       }
+       if ($opt eq "-" && $arg eq "-") {
+           shift @options;
+           last OPTION;
+       }
+       if ($opt eq "w") {
+           $warn_undefined_syms = 1;
+       } elsif ($opt eq "D") {
+           $arg ||= shift @options;
+           foreach $arg (split(//, $arg)) {
+               if ($arg eq "o") {
+                   B->debug(1);
+               } elsif ($arg eq "c") {
+                   $debug_cops = 1;
+               } elsif ($arg eq "A") {
+                   $debug_av = 1;
+               } elsif ($arg eq "C") {
+                   $debug_cv = 1;
+               } elsif ($arg eq "M") {
+                   $debug_mg = 1;
+               } else {
+                   warn "ignoring unknown debug option: $arg\n";
+               }
+           }
+       } elsif ($opt eq "o") {
+           $arg ||= shift @options;
+           open(STDOUT, ">$arg") or return "$arg: $!\n";
+       } elsif ($opt eq "v") {
+           $verbose = 1;
+       } elsif ($opt eq "u") {
+           $arg ||= shift @options;
+           push(@unused_sub_packages, $arg);
+       } elsif ($opt eq "f") {
+           $arg ||= shift @options;
+           if ($arg eq "cog") {
+               $pv_copy_on_grow = 1;
+           } elsif ($arg eq "no-cog") {
+               $pv_copy_on_grow = 0;
+           }
+       } elsif ($opt eq "O") {
+           $arg = 1 if $arg eq "";
+           $pv_copy_on_grow = 0;
+           if ($arg >= 1) {
+               # Optimisations for -O1
+               $pv_copy_on_grow = 1;
+           }
+       }
+    }
+    init_init();
+    if (@options) {
+       return sub {
+           my $objname;
+           foreach $objname (@options) {
+               eval "save_object(\\$objname)";
+           }
+           output_all();
+       }
+    } else {
+       return sub { save_main() };
+    }
+}
+
+1;
diff --git a/B/CC.pm b/B/CC.pm
new file mode 100644 (file)
index 0000000..a25e7e2
--- /dev/null
+++ b/B/CC.pm
@@ -0,0 +1,1466 @@
+#      CC.pm
+#
+#      Copyright (c) 1996 Malcolm Beattie
+#
+#      You may distribute under the terms of either the GNU General Public
+#      License or the Artistic License, as specified in the README file.
+#
+package B::CC;
+use strict;
+use B qw(main_start main_root class comppadlist peekop svref_2object ad
+       timing_info);
+use B::C qw(push_decl init_init push_init save_unused_subs objsym
+           output_all output_boilerplate output_main);
+use B::Bblock qw(find_leaders);
+use B::Stackobj qw(:types :flags);
+
+# These should probably be elsewhere
+# Flags for $op->flags
+sub OPf_LIST () { 1 }
+sub OPf_KNOW () { 2 }
+sub OPf_MOD () { 32 }
+sub OPf_STACKED () { 64 }
+sub OPf_SPECIAL () { 128 }
+# op-specific flags for $op->private 
+sub OPpASSIGN_BACKWARDS () { 64 }
+sub OPpLVAL_INTRO () { 128 }
+sub OPpDEREF_AV () { 32 }
+sub OPpDEREF_HV () { 64 }
+sub OPpFLIP_LINENUM () { 64 }
+sub G_ARRAY () { 1 }
+# cop.h
+sub CXt_NULL () { 0 }
+sub CXt_SUB () { 1 }
+sub CXt_EVAL () { 2 }
+sub CXt_LOOP () { 3 }
+sub CXt_SUBST () { 4 }
+sub CXt_BLOCK () { 5 }
+
+my %done;              # hash keyed by $$op of leaders of basic blocks
+                       # which have already been done.
+my $leaders;           # ref to hash of basic block leaders. Keys are $$op
+                       # addresses, values are the $op objects themselves.
+my @bblock_todo;       # list of leaders of basic blocks that need visiting
+                       # sometime.
+my @cc_todo;           # list of tuples defining what PP code needs to be
+                       # saved (e.g. CV, main or PMOP repl code). Each tuple
+                       # is [$name, $root, $start, @padlist]. PMOP repl code
+                       # tuples inherit padlist.
+my @stack;             # shadows perl's stack when contents are known.
+                       # Values are objects derived from class B::Stackobj
+my @pad;               # Lexicals in current pad as Stackobj-derived objects
+my @padlist;           # Copy of current padlist so PMOP repl code can find it
+my @cxstack;           # Shadows the (compile-time) cxstack for next,last,redo
+my $jmpbuf_ix = 0;     # Next free index for dynamically allocated jmpbufs
+my %constobj;          # OP_CONST constants as Stackobj-derived objects
+                       # keyed by ad($sv).
+my $need_freetmps = 0; # We may postpone FREETMPS to the end of each basic
+                       # block or even to the end of each loop of blocks,
+                       # depending on optimisation options.
+my $know_op = 0;       # Set when C variable op already holds the right op
+                       # (from an immediately preceding DOOP(ppname)).
+my $errors = 0;                # Number of errors encountered
+my %skip_stack;                # Hash of PP names which don't need write_back_stack
+my %skip_lexicals;     # Hash of PP names which don't need write_back_lexicals
+my %skip_invalidate;   # Hash of PP names which don't need invalidate_lexicals
+my %ignore_op;         # Hash of ops which do nothing except returning op_next
+
+BEGIN {
+    foreach (qw(pp_scalar pp_regcmaybe pp_lineseq pp_scope pp_null)) {
+       $ignore_op{$_} = 1;
+    }
+}
+
+my @unused_sub_packages; # list of packages (given by -u options) to search
+                        # explicitly and save every sub we find there, even
+                        # if apparently unused (could be only referenced from
+                        # an eval "" or from a $SIG{FOO} = "bar").
+
+my ($module_name);
+my ($debug_op, $debug_stack, $debug_cxstack, $debug_pad, $debug_runtime,
+    $debug_shadow, $debug_queue, $debug_lineno, $debug_timings);
+
+# Optimisation options. On the command line, use hyphens instead of
+# underscores for compatibility with gcc-style options. We use
+# underscores here because they are OK in (strict) barewords.
+my ($freetmps_each_bblock, $freetmps_each_loop, $omit_taint);
+my %optimise = (freetmps_each_bblock   => \$freetmps_each_bblock,
+               freetmps_each_loop      => \$freetmps_each_loop,
+               omit_taint              => \$omit_taint);
+
+# Could rewrite push_runtime() and output_runtime() to use a
+# temporary file if memory is at a premium.
+my $ppname;            # name of current fake PP function
+my $runtime_list_ref;
+my $declare_ref;       # Hash ref keyed by C variable type of declarations.
+
+my @pp_list;           # list of [$ppname, $runtime_list_ref, $declare_ref]
+                       # tuples to be written out.
+
+sub init_hash { map { $_ => 1 } @_ }
+
+#
+# Initialise the hashes for the default PP functions where we can avoid
+# either write_back_stack, write_back_lexicals or invalidate_lexicals.
+#
+%skip_lexicals = init_hash qw(pp_enter pp_enterloop);
+%skip_invalidate = init_hash qw(pp_enter pp_enterloop);
+
+sub debug {
+    if ($debug_runtime) {
+       warn(@_);
+    } else {
+       runtime(map { chomp; "/* $_ */"} @_);
+    }
+}
+
+sub declare {
+    my ($type, $var) = @_;
+    push(@{$declare_ref->{$type}}, $var);
+}
+
+sub push_runtime {
+    push(@$runtime_list_ref, @_);
+    warn join("\n", @_) . "\n" if $debug_runtime;
+}
+
+sub save_runtime {
+    push(@pp_list, [$ppname, $runtime_list_ref, $declare_ref]);
+}
+
+sub output_runtime {
+    my $ppdata;
+    print qq(#include "cc_runtime.h"\n);
+    foreach $ppdata (@pp_list) {
+       my ($name, $runtime, $declare) = @$ppdata;
+       print "\nstatic\nPP($name)\n{\n";
+       my ($type, $varlist, $line);
+       while (($type, $varlist) = each %$declare) {
+           print "\t$type ", join(", ", @$varlist), ";\n";
+       }
+       foreach $line (@$runtime) {
+           print $line, "\n";
+       }
+       print "}\n";
+    }
+}
+
+sub runtime {
+    my $line;
+    foreach $line (@_) {
+       push_runtime("\t$line");
+    }
+}
+
+sub init_pp {
+    $ppname = shift;
+    $runtime_list_ref = [];
+    $declare_ref = {};
+    runtime("dSP;");
+    declare("I32", "oldsave");
+    declare("SV", "**svp");
+    map { declare("SV", "*$_") } qw(sv src dst left right);
+    declare("MAGIC", "*mg");
+    push_decl("static OP * $ppname _((ARGSproto));");
+    debug "init_pp: $ppname\n" if $debug_queue;
+}
+
+# Initialise runtime_callback function for Stackobj class
+BEGIN { B::Stackobj::set_callback(\&runtime) }
+
+# Initialise saveoptree_callback for B::C class
+sub cc_queue {
+    my ($name, $root, $start, @pl) = @_;
+    debug "cc_queue: name $name, root $root, start $start, padlist (@pl)\n"
+       if $debug_queue;
+    if ($name eq "*ignore*") {
+       $name = 0;
+    } else {
+       push(@cc_todo, [$name, $root, $start, (@pl ? @pl : @padlist)]);
+    }
+    my $fakeop = new B::FAKEOP ("next" => 0, sibling => 0, ppaddr => $name);
+    $start = $fakeop->save;
+    debug "cc_queue: name $name returns $start\n" if $debug_queue;
+    return $start;
+}
+BEGIN { B::C::set_callback(\&cc_queue) }
+
+sub valid_int { $_[0]->{flags} & VALID_INT }
+sub valid_double { $_[0]->{flags} & VALID_DOUBLE }
+sub valid_numeric { $_[0]->{flags} & (VALID_INT | VALID_DOUBLE) }
+sub valid_sv { $_[0]->{flags} & VALID_SV }
+
+sub top_int { @stack ? $stack[-1]->as_int : "TOPi" }
+sub top_double { @stack ? $stack[-1]->as_double : "TOPn" }
+sub top_numeric { @stack ? $stack[-1]->as_numeric : "TOPn" }
+sub top_sv { @stack ? $stack[-1]->as_sv : "TOPs" }
+sub top_bool { @stack ? $stack[-1]->as_numeric : "SvTRUE(TOPs)" }
+
+sub pop_int { @stack ? (pop @stack)->as_int : "POPi" }
+sub pop_double { @stack ? (pop @stack)->as_double : "POPn" }
+sub pop_numeric { @stack ? (pop @stack)->as_numeric : "POPn" }
+sub pop_sv { @stack ? (pop @stack)->as_sv : "POPs" }
+sub pop_bool {
+    if (@stack) {
+       return ((pop @stack)->as_numeric);
+    } else {
+       # Careful: POPs has an auto-decrement and SvTRUE evaluates
+       # its argument more than once.
+       runtime("sv = POPs;");
+       return "SvTRUE(sv)";
+    }
+}
+
+sub write_back_lexicals {
+    my $avoid = shift || 0;
+    debug "write_back_lexicals($avoid) called from @{[(caller(1))[3]]}\n"
+       if $debug_shadow;
+    my $lex;
+    foreach $lex (@pad) {
+       next unless ref($lex);
+       $lex->write_back unless $lex->{flags} & $avoid;
+    }
+}
+
+sub write_back_stack {
+    my $obj;
+    return unless @stack;
+    runtime(sprintf("EXTEND(sp, %d);", scalar(@stack)));
+    foreach $obj (@stack) {
+       runtime(sprintf("PUSHs((SV*)%s);", $obj->as_sv));
+    }
+    @stack = ();
+}
+
+sub invalidate_lexicals {
+    my $avoid = shift || 0;
+    debug "invalidate_lexicals($avoid) called from @{[(caller(1))[3]]}\n"
+       if $debug_shadow;
+    my $lex;
+    foreach $lex (@pad) {
+       next unless ref($lex);
+       $lex->invalidate unless $lex->{flags} & $avoid;
+    }
+}
+
+sub reload_lexicals {
+    my $lex;
+    foreach $lex (@pad) {
+       next unless ref($lex);
+       my $type = $lex->{type};
+       if ($type == T_INT) {
+           $lex->as_int;
+       } elsif ($type == T_DOUBLE) {
+           $lex->as_double;
+       } else {
+           $lex->as_sv;
+       }
+    }
+}
+
+{
+    package B::Pseudoreg;
+    #
+    # This class allocates pseudo-registers (OK, so they're C variables).
+    #
+    my %alloc;         # Keyed by variable name. A value of 1 means the
+                       # variable has been declared. A value of 2 means
+                       # it's in use.
+    
+    sub new_scope { %alloc = () }
+    
+    sub new ($$$) {
+       my ($class, $type, $prefix) = @_;
+       my ($ptr, $i, $varname, $status, $obj);
+       $prefix =~ s/^(\**)//;
+       $ptr = $1;
+       $i = 0;
+       do {
+           $varname = "$prefix$i";
+           $status = $alloc{$varname};
+       } while $status == 2;
+       if ($status != 1) {
+           # Not declared yet
+           B::CC::declare($type, "$ptr$varname");
+           $alloc{$varname} = 2;       # declared and in use
+       }
+       $obj = bless \$varname, $class;
+       return $obj;
+    }
+    sub DESTROY {
+       my $obj = shift;
+       $alloc{$$obj} = 1; # no longer in use but still declared
+    }
+}
+{
+    package B::Shadow;
+    #
+    # This class gives a standard API for a perl object to shadow a
+    # C variable and only generate reloads/write-backs when necessary.
+    #
+    # Use $obj->load($foo) instead of runtime("shadowed_c_var = foo").
+    # Use $obj->write_back whenever shadowed_c_var needs to be up to date.
+    # Use $obj->invalidate whenever an unknown function may have
+    # set shadow itself.
+
+    sub new {
+       my ($class, $write_back) = @_;
+       # Object fields are perl shadow variable, validity flag
+       # (for *C* variable) and callback sub for write_back
+       # (passed perl shadow variable as argument).
+       bless [undef, 1, $write_back], $class;
+    }
+    sub load {
+       my ($obj, $newval) = @_;
+       $obj->[1] = 0;          # C variable no longer valid
+       $obj->[0] = $newval;
+    }
+    sub write_back {
+       my $obj = shift;
+       if (!($obj->[1])) {
+           $obj->[1] = 1;      # C variable will now be valid
+           &{$obj->[2]}($obj->[0]);
+       }
+    }
+    sub invalidate { $_[0]->[1] = 0 } # force C variable to be invalid
+}
+my $curcop = new B::Shadow (sub {
+    my $opsym = shift->save;
+    runtime("curcop = (COP*)$opsym;");
+});
+
+#
+# Context stack shadowing. Mimics stuff in pp_ctl.c, cop.h and so on.
+#
+sub dopoptoloop {
+    my $cxix = $#cxstack;
+    while ($cxix >= 0 && $cxstack[$cxix]->{type} != CXt_LOOP) {
+       $cxix--;
+    }
+    debug "dopoptoloop: returning $cxix" if $debug_cxstack;
+    return $cxix;
+}
+
+sub dopoptolabel {
+    my $label = shift;
+    my $cxix = $#cxstack;
+    while ($cxix >= 0 && $cxstack[$cxix]->{type} != CXt_LOOP
+          && $cxstack[$cxix]->{label} ne $label) {
+       $cxix--;
+    }
+    debug "dopoptolabel: returning $cxix" if $debug_cxstack;
+    return $cxix;
+}
+
+sub error {
+    my $format = shift;
+    my $file = $curcop->[0]->filegv->SV->PV;
+    my $line = $curcop->[0]->line;
+    $errors++;
+    if (@_) {
+       warn sprintf("%s:%d: $format\n", $file, $line, @_);
+    } else {
+       warn sprintf("%s:%d: %s\n", $file, $line, $format);
+    }
+}
+
+#
+# Load pad takes (the elements of) a PADLIST as arguments and loads
+# up @pad with Stackobj-derived objects which represent those lexicals.
+# If/when perl itself can generate type information (my int $foo) then
+# we'll take advantage of that here. Until then, we'll use various hacks
+# to tell the compiler when we want a lexical to be a particular type
+# or to be a register.
+#
+sub load_pad {
+    my ($namelistav, $valuelistav) = @_;
+    @padlist = @_;
+    my @namelist = $namelistav->ARRAY;
+    my @valuelist = $valuelistav->ARRAY;
+    my $ix;
+    @pad = ();
+    debug "load_pad: $#namelist names, $#valuelist values\n" if $debug_pad;
+    # Temporary lexicals don't get named so it's possible for @valuelist
+    # to be strictly longer than @namelist. We count $ix up to the end of
+    # @valuelist but index into @namelist for the name. Any temporaries which
+    # run off the end of @namelist will make $namesv undefined and we treat
+    # that the same as having an explicit SPECIAL sv_undef object in @namelist.
+    # [XXX If/when @_ becomes a lexical, we must start at 0 here.]
+    for ($ix = 1; $ix < @valuelist; $ix++) {
+       my $namesv = $namelist[$ix];
+       my $type = T_UNKNOWN;
+       my $flags = 0;
+       my $name = "tmp$ix";
+       my $class = class($namesv);
+       if (!defined($namesv) || $class eq "SPECIAL") {
+           # temporaries have &sv_undef instead of a PVNV for a name
+           $flags = VALID_SV|TEMPORARY|REGISTER;
+       } else {
+           if ($namesv->PV =~ /^\$(.*)_([di])(r?)$/) {
+               $name = $1;
+               if ($2 eq "i") {
+                   $type = T_INT;
+                   $flags = VALID_SV|VALID_INT;
+               } elsif ($2 eq "d") {
+                   $type = T_DOUBLE;
+                   $flags = VALID_SV|VALID_DOUBLE;
+               }
+               $flags |= REGISTER if $3;
+           }
+       }
+       $pad[$ix] = new B::Stackobj::Padsv ($type, $flags, $ix,
+                                           "i_$name", "d_$name");
+       declare("IV", $type == T_INT ? "i_$name = 0" : "i_$name");
+       declare("double", $type == T_DOUBLE ? "d_$name = 0" : "d_$name");
+       debug sprintf("curpad[$ix] = %s\n", $pad[$ix]->peek) if $debug_pad;
+    }
+}
+
+#
+# Debugging stuff
+#
+sub peek_stack { sprintf "stack = %s\n", join(" ", map($_->minipeek, @stack)) }
+
+#
+# OP stuff
+#
+
+sub label {
+    my $op = shift;
+    # XXX Preserve original label name for "real" labels?
+    return sprintf("lab_%x", $$op);
+}
+
+sub write_label {
+    my $op = shift;
+    push_runtime(sprintf("  %s:", label($op)));
+}
+
+sub loadop {
+    my $op = shift;
+    my $opsym = $op->save;
+    runtime("op = $opsym;") unless $know_op;
+    return $opsym;
+}
+
+sub doop {
+    my $op = shift;
+    my $ppname = $op->ppaddr;
+    my $sym = loadop($op);
+    runtime("DOOP($ppname);");
+    $know_op = 1;
+    return $sym;
+}
+
+sub gimme {
+    my $op = shift;
+    my $flags = $op->flags;
+    return (($flags & OPf_KNOW) ? ($flags & OPf_LIST) : "dowantarray()");
+}
+
+#
+# Code generation for PP code
+#
+
+sub pp_null {
+    my $op = shift;
+    return $op->next;
+}
+
+sub pp_stub {
+    my $op = shift;
+    my $gimme = gimme($op);
+    if ($gimme != 1) {
+       # XXX Change to push a constant sv_undef Stackobj onto @stack
+       write_back_stack();
+       runtime("if ($gimme != G_ARRAY) XPUSHs(&sv_undef);");
+    }
+    return $op->next;
+}
+
+sub pp_unstack {
+    my $op = shift;
+    @stack = ();
+    runtime("PP_UNSTACK;");
+    return $op->next;
+}
+
+sub pp_and {
+    my $op = shift;
+    my $next = $op->next;
+    reload_lexicals();
+    unshift(@bblock_todo, $next);
+    if (@stack >= 1) {
+       my $bool = pop_bool();
+       write_back_stack();
+       runtime(sprintf("if (!$bool) goto %s;", label($next)));
+    } else {
+       runtime(sprintf("if (!%s) goto %s;", top_bool(), label($next)),
+               "*sp--;");
+    }
+    return $op->other;
+}
+           
+sub pp_or {
+    my $op = shift;
+    my $next = $op->next;
+    reload_lexicals();
+    unshift(@bblock_todo, $next);
+    if (@stack >= 1) {
+       my $obj = pop @stack;
+       write_back_stack();
+       runtime(sprintf("if (%s) { XPUSHs(%s); goto %s; }",
+                       $obj->as_numeric, $obj->as_sv, label($next)));
+    } else {
+       runtime(sprintf("if (%s) goto %s;", top_bool(), label($next)),
+               "*sp--;");
+    }
+    return $op->other;
+}
+           
+sub pp_cond_expr {
+    my $op = shift;
+    my $false = $op->false;
+    unshift(@bblock_todo, $false);
+    reload_lexicals();
+    my $bool = pop_bool();
+    write_back_stack();
+    runtime(sprintf("if (!$bool) goto %s;", label($false)));
+    return $op->true;
+}
+           
+
+sub pp_padsv {
+    my $op = shift;
+    my $ix = $op->targ;
+    push(@stack, $pad[$ix]);
+    if ($op->flags & OPf_MOD) {
+       my $private = $op->private;
+       if ($private & OPpLVAL_INTRO) {
+           runtime("SAVECLEARSV(curpad[$ix]);");
+       } elsif ($private & (OPpDEREF_HV|OPpDEREF_AV)) {
+           loadop($op);
+           runtime("provide_ref(op, curpad[$ix]);");
+           $pad[$ix]->invalidate;
+       }
+    }
+    return $op->next;
+}
+
+sub pp_const {
+    my $op = shift;
+    my $sv = $op->sv;
+    my $obj = $constobj{ad($sv)};
+    if (!defined($obj)) {
+       $obj = $constobj{ad($sv)} = new B::Stackobj::Const ($sv);
+    }
+    push(@stack, $obj);
+    return $op->next;
+}
+
+sub pp_nextstate {
+    my $op = shift;
+    $curcop->load($op);
+    @stack = ();
+    debug(sprintf("%s:%d\n", $op->filegv->SV->PV, $op->line)) if $debug_lineno;
+    runtime("TAINT_NOT;") unless $omit_taint;
+    runtime("sp = stack_base + cxstack[cxstack_ix].blk_oldsp;");
+    if ($freetmps_each_bblock || $freetmps_each_loop) {
+       $need_freetmps = 1;
+    } else {
+       runtime("FREETMPS;");
+    }
+    return $op->next;
+}
+
+sub pp_dbstate {
+    my $op = shift;
+    $curcop->invalidate; # XXX?
+    return default_pp($op);
+}
+
+sub pp_rv2gv { $curcop->write_back; default_pp(@_) }
+sub pp_bless { $curcop->write_back; default_pp(@_) }
+sub pp_repeat { $curcop->write_back; default_pp(@_) }
+# The following subs need $curcop->write_back if we decide to support arybase:
+# pp_pos, pp_substr, pp_index, pp_rindex, pp_aslice, pp_lslice, pp_splice
+sub pp_sort { $curcop->write_back; default_pp(@_) }
+sub pp_caller { $curcop->write_back; default_pp(@_) }
+sub pp_reset { $curcop->write_back; default_pp(@_) }
+
+sub pp_gv {
+    my $op = shift;
+    my $gvsym = $op->gv->save;
+    write_back_stack();
+    runtime("XPUSHs((SV*)$gvsym);");
+    return $op->next;
+}
+
+sub pp_gvsv {
+    my $op = shift;
+    my $gvsym = $op->gv->save;
+    write_back_stack();
+    if ($op->private & OPpLVAL_INTRO) {
+       runtime("XPUSHs(save_scalar($gvsym));");
+    } else {
+       runtime("XPUSHs(GvSV($gvsym));");
+    }
+    return $op->next;
+}
+
+sub pp_aelemfast {
+    my $op = shift;
+    my $gvsym = $op->gv->save;
+    my $ix = $op->private;
+    my $flag = $op->flags & OPf_MOD;
+    write_back_stack();
+    runtime("svp = av_fetch(GvAV($gvsym), $ix, $flag);",
+           "PUSHs(svp ? *svp : &sv_undef);");
+    return $op->next;
+}
+
+sub int_binop {
+    my ($op, $operator) = @_;
+    if ($op->flags & OPf_STACKED) {
+       my $right = pop_int();
+       if (@stack >= 1) {
+           my $left = top_int();
+           $stack[-1]->set_int(&$operator($left, $right));
+       } else {
+           runtime(sprintf("sv_setiv(TOPs, %s);",&$operator("TOPi", $right)));
+       }
+    } else {
+       my $targ = $pad[$op->targ];
+       my $right = new B::Pseudoreg ("IV", "riv");
+       my $left = new B::Pseudoreg ("IV", "liv");
+       runtime(sprintf("$$right = %s; $$left = %s;", pop_int(), pop_int));
+       $targ->set_int(&$operator($$left, $$right));
+       push(@stack, $targ);
+    }
+    return $op->next;
+}
+
+sub INTS_CLOSED () { 0x1 }
+sub INT_RESULT () { 0x2 }
+sub NUMERIC_RESULT () { 0x4 }
+
+sub numeric_binop {
+    my ($op, $operator, $flags) = @_;
+    my $force_int = 0;
+    $force_int ||= ($flags & INT_RESULT);
+    $force_int ||= ($flags & INTS_CLOSED && @stack >= 2
+                   && valid_int($stack[-2]) && valid_int($stack[-1]));
+    if ($op->flags & OPf_STACKED) {
+       my $right = pop_numeric();
+       if (@stack >= 1) {
+           my $left = top_numeric();
+           if ($force_int) {
+               $stack[-1]->set_int(&$operator($left, $right));
+           } else {
+               $stack[-1]->set_numeric(&$operator($left, $right));
+           }
+       } else {
+           if ($force_int) {
+               runtime(sprintf("sv_setiv(TOPs, %s);",
+                               &$operator("TOPi", $right)));
+           } else {
+               runtime(sprintf("sv_setnv(TOPs, %s);",
+                               &$operator("TOPn", $right)));
+           }
+       }
+    } else {
+       my $targ = $pad[$op->targ];
+       $force_int ||= ($targ->{type} == T_INT);
+       if ($force_int) {
+           my $right = new B::Pseudoreg ("IV", "riv");
+           my $left = new B::Pseudoreg ("IV", "liv");
+           runtime(sprintf("$$right = %s; $$left = %s;",
+                           pop_numeric(), pop_numeric));
+           $targ->set_int(&$operator($$left, $$right));
+       } else {
+           my $right = new B::Pseudoreg ("double", "rnv");
+           my $left = new B::Pseudoreg ("double", "lnv");
+           runtime(sprintf("$$right = %s; $$left = %s;",
+                           pop_numeric(), pop_numeric));
+           $targ->set_numeric(&$operator($$left, $$right));
+       }
+       push(@stack, $targ);
+    }
+    return $op->next;
+}
+
+sub sv_binop {
+    my ($op, $operator, $flags) = @_;
+    if ($op->flags & OPf_STACKED) {
+       my $right = pop_sv();
+       if (@stack >= 1) {
+           my $left = top_sv();
+           if ($flags & INT_RESULT) {
+               $stack[-1]->set_int(&$operator($left, $right));
+           } elsif ($flags & NUMERIC_RESULT) {
+               $stack[-1]->set_numeric(&$operator($left, $right));
+           } else {
+               # XXX Does this work?
+               runtime(sprintf("sv_setsv($left, %s);",
+                               &$operator($left, $right)));
+               $stack[-1]->invalidate;
+           }
+       } else {
+           my $f;
+           if ($flags & INT_RESULT) {
+               $f = "sv_setiv";
+           } elsif ($flags & NUMERIC_RESULT) {
+               $f = "sv_setnv";
+           } else {
+               $f = "sv_setsv";
+           }
+           runtime(sprintf("%s(TOPs, %s);", $f, &$operator("TOPs", $right)));
+       }
+    } else {
+       my $targ = $pad[$op->targ];
+       runtime(sprintf("right = %s; left = %s;", pop_sv(), pop_sv));
+       if ($flags & INT_RESULT) {
+           $targ->set_int(&$operator("left", "right"));
+       } elsif ($flags & NUMERIC_RESULT) {
+           $targ->set_numeric(&$operator("left", "right"));
+       } else {
+           # XXX Does this work?
+           runtime(sprintf("sv_setsv(%s, %s);",
+                           $targ->as_sv, &$operator("left", "right")));
+           $targ->invalidate;
+       }
+       push(@stack, $targ);
+    }
+    return $op->next;
+}
+    
+sub bool_int_binop {
+    my ($op, $operator) = @_;
+    my $right = new B::Pseudoreg ("IV", "riv");
+    my $left = new B::Pseudoreg ("IV", "liv");
+    runtime(sprintf("$$right = %s; $$left = %s;", pop_int(), pop_int()));
+    my $bool = new B::Stackobj::Bool (new B::Pseudoreg ("int", "b"));
+    $bool->set_int(&$operator($$left, $$right));
+    push(@stack, $bool);
+    return $op->next;
+}
+
+sub bool_numeric_binop {
+    my ($op, $operator) = @_;
+    my $right = new B::Pseudoreg ("double", "rnv");
+    my $left = new B::Pseudoreg ("double", "lnv");
+    runtime(sprintf("$$right = %s; $$left = %s;",
+                   pop_numeric(), pop_numeric()));
+    my $bool = new B::Stackobj::Bool (new B::Pseudoreg ("int", "b"));
+    $bool->set_numeric(&$operator($$left, $$right));
+    push(@stack, $bool);
+    return $op->next;
+}
+
+sub bool_sv_binop {
+    my ($op, $operator) = @_;
+    runtime(sprintf("right = %s; left = %s;", pop_sv(), pop_sv()));
+    my $bool = new B::Stackobj::Bool (new B::Pseudoreg ("int", "b"));
+    $bool->set_numeric(&$operator("left", "right"));
+    push(@stack, $bool);
+    return $op->next;
+}
+
+sub infix_op {
+    my $opname = shift;
+    return sub { "$_[0] $opname $_[1]" }
+}
+
+sub prefix_op {
+    my $opname = shift;
+    return sub { sprintf("%s(%s)", $opname, join(", ", @_)) }
+}
+
+BEGIN {
+    my $plus_op = infix_op("+");
+    my $minus_op = infix_op("-");
+    my $multiply_op = infix_op("*");
+    my $divide_op = infix_op("/");
+    my $modulo_op = infix_op("%");
+    my $lshift_op = infix_op("<<");
+    my $rshift_op = infix_op("<<");
+    my $ncmp_op = sub { "($_[0] > $_[1] ? 1 : ($_[0] < $_[1]) ? -1 : 0)" };
+    my $scmp_op = prefix_op("sv_cmp");
+    my $seq_op = prefix_op("sv_eq");
+    my $sne_op = prefix_op("!sv_eq");
+    my $slt_op = sub { "sv_cmp($_[0], $_[1]) < 0" };
+    my $sgt_op = sub { "sv_cmp($_[0], $_[1]) > 0" };
+    my $sle_op = sub { "sv_cmp($_[0], $_[1]) <= 0" };
+    my $sge_op = sub { "sv_cmp($_[0], $_[1]) >= 0" };
+    my $eq_op = infix_op("==");
+    my $ne_op = infix_op("!=");
+    my $lt_op = infix_op("<");
+    my $gt_op = infix_op(">");
+    my $le_op = infix_op("<=");
+    my $ge_op = infix_op(">=");
+
+    #
+    # XXX The standard perl PP code has extra handling for
+    # some special case arguments of these operators.
+    #
+    sub pp_add { numeric_binop($_[0], $plus_op, INTS_CLOSED) }
+    sub pp_subtract { numeric_binop($_[0], $minus_op, INTS_CLOSED) }
+    sub pp_multiply { numeric_binop($_[0], $multiply_op, INTS_CLOSED) }
+    sub pp_divide { numeric_binop($_[0], $divide_op) }
+    sub pp_modulo { int_binop($_[0], $modulo_op) } # differs from perl's
+    sub pp_ncmp { numeric_binop($_[0], $ncmp_op, INT_RESULT) }
+
+    sub pp_left_shift { int_binop($_[0], $lshift_op) }
+    sub pp_right_shift { int_binop($_[0], $rshift_op) }
+    sub pp_i_add { int_binop($_[0], $plus_op) }
+    sub pp_i_subtract { int_binop($_[0], $minus_op) }
+    sub pp_i_multiply { int_binop($_[0], $multiply_op) }
+    sub pp_i_divide { int_binop($_[0], $divide_op) }
+    sub pp_i_modulo { int_binop($_[0], $modulo_op) }
+
+    sub pp_eq { bool_numeric_binop($_[0], $eq_op) }
+    sub pp_ne { bool_numeric_binop($_[0], $ne_op) }
+    sub pp_lt { bool_numeric_binop($_[0], $lt_op) }
+    sub pp_gt { bool_numeric_binop($_[0], $gt_op) }
+    sub pp_le { bool_numeric_binop($_[0], $le_op) }
+    sub pp_ge { bool_numeric_binop($_[0], $ge_op) }
+
+    sub pp_i_eq { bool_int_binop($_[0], $eq_op) }
+    sub pp_i_ne { bool_int_binop($_[0], $ne_op) }
+    sub pp_i_lt { bool_int_binop($_[0], $lt_op) }
+    sub pp_i_gt { bool_int_binop($_[0], $gt_op) }
+    sub pp_i_le { bool_int_binop($_[0], $le_op) }
+    sub pp_i_ge { bool_int_binop($_[0], $ge_op) }
+
+    sub pp_scmp { sv_binop($_[0], $scmp_op, INT_RESULT) }
+    sub pp_slt { bool_sv_binop($_[0], $slt_op) }
+    sub pp_sgt { bool_sv_binop($_[0], $sgt_op) }
+    sub pp_sle { bool_sv_binop($_[0], $sle_op) }
+    sub pp_sge { bool_sv_binop($_[0], $sge_op) }
+    sub pp_seq { bool_sv_binop($_[0], $seq_op) }
+    sub pp_sne { bool_sv_binop($_[0], $sne_op) }
+}
+
+
+sub pp_sassign {
+    my $op = shift;
+    my $backwards = $op->private & OPpASSIGN_BACKWARDS;
+    my ($dst, $src);
+    if (@stack >= 2) {
+       $dst = pop @stack;
+       $src = pop @stack;
+       ($src, $dst) = ($dst, $src) if $backwards;
+       my $type = $src->{type};
+       if ($type == T_INT) {
+           $dst->set_int($src->as_int);
+       } elsif ($type == T_DOUBLE) {
+           $dst->set_numeric($src->as_numeric);
+       } else {
+           $dst->set_sv($src->as_sv);
+       }
+       push(@stack, $dst);
+    } elsif (@stack == 1) {
+       if ($backwards) {
+           my $src = pop @stack;
+           my $type = $src->{type};
+           runtime("if (tainting && tainted) TAINT_NOT;");
+           if ($type == T_INT) {
+               runtime sprintf("sv_setiv(TOPs, %s);", $src->as_int);
+           } elsif ($type == T_DOUBLE) {
+               runtime sprintf("sv_setnv(TOPs, %s);", $src->as_double);
+           } else {
+               runtime sprintf("sv_setsv(TOPs, %s);", $src->as_sv);
+           }
+           runtime("SvSETMAGIC(TOPs);");
+       } else {
+           my $dst = pop @stack;
+           my $type = $dst->{type};
+           runtime("sv = POPs;");
+           runtime("MAYBE_TAINT_SASSIGN_SRC(sv);");
+           if ($type == T_INT) {
+               $dst->set_int("SvIV(sv)");
+           } elsif ($type == T_DOUBLE) {
+               $dst->set_double("SvNV(sv)");
+           } else {
+               runtime("SvSetSV($dst->{sv}, sv);");
+               $dst->invalidate;
+           }
+       }
+    } else {
+       if ($backwards) {
+           runtime("src = POPs; dst = TOPs;");
+       } else {
+           runtime("dst = POPs; src = TOPs;");
+       }
+       runtime("MAYBE_TAINT_SASSIGN_SRC(src);",
+               "SvSetSV(dst, src);",
+               "SvSETMAGIC(dst);",
+               "SETs(dst);");
+    }
+    return $op->next;
+}
+
+sub pp_preinc {
+    my $op = shift;
+    if (@stack >= 1) {
+       my $obj = $stack[-1];
+       my $type = $obj->{type};
+       if ($type == T_INT || $type == T_DOUBLE) {
+           $obj->set_int($obj->as_int . " + 1");
+       } else {
+           runtime sprintf("PP_PREINC(%s);", $obj->as_sv);
+           $obj->invalidate();
+       }
+    } else {
+       runtime sprintf("PP_PREINC(TOPs);");
+    }
+    return $op->next;
+}
+
+sub pp_pushmark {
+    my $op = shift;
+    write_back_stack();
+    runtime("PUSHMARK(sp);");
+    return $op->next;
+}
+
+sub pp_list {
+    my $op = shift;
+    write_back_stack();
+    my $gimme = gimme($op);
+    if ($gimme == 1) { # sic
+       runtime("POPMARK;"); # need this even though not a "full" pp_list
+    } else {
+       runtime("PP_LIST($gimme);");
+    }
+    return $op->next;
+}
+
+sub pp_entersub {
+    my $op = shift;
+    write_back_lexicals(REGISTER|TEMPORARY);
+    write_back_stack();
+    my $sym = doop($op);
+    runtime("if (op != ($sym)->op_next) op = (*op->op_ppaddr)();");
+    runtime("SPAGAIN;");
+    $know_op = 0;
+    invalidate_lexicals(REGISTER|TEMPORARY);
+    return $op->next;
+}
+
+sub doeval {
+    my $op = shift;
+    $curcop->write_back;
+    write_back_lexicals(REGISTER|TEMPORARY);
+    write_back_stack();
+    my $sym = loadop($op);
+    my $ppaddr = $op->ppaddr;
+    runtime("PP_EVAL($ppaddr, ($sym)->op_next);");
+    $know_op = 1;
+    invalidate_lexicals(REGISTER|TEMPORARY);
+    return $op->next;
+}
+
+sub pp_entereval { doeval(@_) }
+sub pp_require { doeval(@_) }
+sub pp_dofile { doeval(@_) }
+
+sub pp_entertry {
+    my $op = shift;
+    $curcop->write_back;
+    write_back_lexicals(REGISTER|TEMPORARY);
+    write_back_stack();
+    my $sym = doop($op);
+    my $jmpbuf = sprintf("jmpbuf%d", $jmpbuf_ix++);
+    declare("Sigjmp_buf", $jmpbuf);
+    runtime(sprintf("PP_ENTERTRY(%s,%s);", $jmpbuf, label($op->other->next)));
+    invalidate_lexicals(REGISTER|TEMPORARY);
+    return $op->next;
+}
+
+sub pp_grepstart {
+    my $op = shift;
+    if ($need_freetmps && $freetmps_each_loop) {
+       runtime("FREETMPS;"); # otherwise the grepwhile loop messes things up
+       $need_freetmps = 0;
+    }
+    write_back_stack();
+    doop($op);
+    return $op->next->other;
+}
+
+sub pp_mapstart {
+    my $op = shift;
+    if ($need_freetmps && $freetmps_each_loop) {
+       runtime("FREETMPS;"); # otherwise the mapwhile loop messes things up
+       $need_freetmps = 0;
+    }
+    write_back_stack();
+    doop($op);
+    return $op->next->other;
+}
+
+sub pp_grepwhile {
+    my $op = shift;
+    my $next = $op->next;
+    unshift(@bblock_todo, $next);
+    write_back_lexicals();
+    write_back_stack();
+    my $sym = doop($op);
+    # pp_grepwhile can return either op_next or op_other and we need to
+    # be able to distinguish the two at runtime. Since it's possible for
+    # both ops to be "inlined", the fields could both be zero. To get
+    # around that, we hack op_next to be our own op (purely because we
+    # know it's a non-NULL pointer and can't be the same as op_other).
+    push_init("((LOGOP*)$sym)->op_next = $sym;");
+    runtime(sprintf("if (op == ($sym)->op_next) goto %s;", label($next)));
+    $know_op = 0;
+    return $op->other;
+}
+
+sub pp_mapwhile {
+    pp_grepwhile(@_);
+}
+
+sub pp_return {
+    my $op = shift;
+    write_back_lexicals(REGISTER|TEMPORARY);
+    write_back_stack();
+    doop($op);
+    runtime("PUTBACK;", "return 0;");
+    $know_op = 0;
+    return $op->next;
+}
+
+sub nyi {
+    my $op = shift;
+    warn sprintf("%s not yet implemented properly\n", $op->ppaddr);
+    return default_pp($op);
+}
+
+sub pp_range {
+    my $op = shift;
+    my $flags = $op->flags;
+    if (!($flags & OPf_KNOW)) {
+       error("context of range unknown at compile-time");
+    }
+    write_back_lexicals();
+    write_back_stack();
+    if (!($flags & OPf_LIST)) {
+       # We need to save our UNOP structure since pp_flop uses
+       # it to find and adjust out targ. We don't need it ourselves.
+       $op->save;
+       runtime sprintf("if (SvTRUE(curpad[%d])) goto %s;",
+                       $op->targ, label($op->false));
+       unshift(@bblock_todo, $op->false);
+    }
+    return $op->true;
+}
+
+sub pp_flip {
+    my $op = shift;
+    my $flags = $op->flags;
+    if (!($flags & OPf_KNOW)) {
+       error("context of flip unknown at compile-time");
+    }
+    if ($flags & OPf_LIST) {
+       return $op->first->false;
+    }
+    write_back_lexicals();
+    write_back_stack();
+    # We need to save our UNOP structure since pp_flop uses
+    # it to find and adjust out targ. We don't need it ourselves.
+    $op->save;
+    my $ix = $op->targ;
+    my $rangeix = $op->first->targ;
+    runtime(($op->private & OPpFLIP_LINENUM) ?
+           "if (last_in_gv && SvIV(TOPs) == IoLINES(GvIOp(last_in_gv))) {"
+         : "if (SvTRUE(TOPs)) {");
+    runtime("\tsv_setiv(curpad[$rangeix], 1);");
+    if ($op->flags & OPf_SPECIAL) {
+       runtime("sv_setiv(curpad[$ix], 1);");
+    } else {
+       runtime("\tsv_setiv(curpad[$ix], 0);",
+               "\tsp--;",
+               sprintf("\tgoto %s;", label($op->first->false)));
+    }
+    runtime("}",
+         qq{sv_setpv(curpad[$ix], "");},
+           "SETs(curpad[$ix]);");
+    $know_op = 0;
+    return $op->next;
+}
+
+sub pp_flop {
+    my $op = shift;
+    default_pp($op);
+    $know_op = 0;
+    return $op->next;
+}
+
+sub enterloop {
+    my $op = shift;
+    my $nextop = $op->nextop;
+    my $lastop = $op->lastop;
+    my $redoop = $op->redoop;
+    $curcop->write_back;
+    debug "enterloop: pushing on cxstack" if $debug_cxstack;
+    push(@cxstack, {
+       type => CXt_LOOP,
+       op => $op,
+       "label" => $curcop->[0]->label,
+       nextop => $nextop,
+       lastop => $lastop,
+       redoop => $redoop
+    });
+    $nextop->save;
+    $lastop->save;
+    $redoop->save;
+    return default_pp($op);
+}
+
+sub pp_enterloop { enterloop(@_) }
+sub pp_enteriter { enterloop(@_) }
+
+sub pp_leaveloop {
+    my $op = shift;
+    if (!@cxstack) {
+       die "panic: leaveloop";
+    }
+    debug "leaveloop: popping from cxstack" if $debug_cxstack;
+    pop(@cxstack);
+    return default_pp($op);
+}
+
+sub pp_next {
+    my $op = shift;
+    my $cxix;
+    if ($op->flags & OPf_SPECIAL) {
+       $cxix = dopoptoloop();
+       if ($cxix < 0) {
+           error('"next" used outside loop');
+           return $op->next; # ignore the op
+       }
+    } else {
+       $cxix = dopoptolabel($op->pv);
+       if ($cxix < 0) {
+           error('Label not found at compile time for "next %s"', $op->pv);
+           return $op->next; # ignore the op
+       }
+    }
+    default_pp($op);
+    my $nextop = $cxstack[$cxix]->{nextop};
+    push(@bblock_todo, $nextop);
+    runtime(sprintf("goto %s;", label($nextop)));
+    return $op->next;
+}
+
+sub pp_redo {
+    my $op = shift;
+    my $cxix;
+    if ($op->flags & OPf_SPECIAL) {
+       $cxix = dopoptoloop();
+       if ($cxix < 0) {
+           error('"redo" used outside loop');
+           return $op->next; # ignore the op
+       }
+    } else {
+       $cxix = dopoptolabel($op->pv);
+       if ($cxix < 0) {
+           error('Label not found at compile time for "redo %s"', $op->pv);
+           return $op->next; # ignore the op
+       }
+    }
+    default_pp($op);
+    my $redoop = $cxstack[$cxix]->{redoop};
+    push(@bblock_todo, $redoop);
+    runtime(sprintf("goto %s;", label($redoop)));
+    return $op->next;
+}
+
+sub pp_last {
+    my $op = shift;
+    my $cxix;
+    if ($op->flags & OPf_SPECIAL) {
+       $cxix = dopoptoloop();
+       if ($cxix < 0) {
+           error('"last" used outside loop');
+           return $op->next; # ignore the op
+       }
+    } else {
+       $cxix = dopoptolabel($op->pv);
+       if ($cxix < 0) {
+           error('Label not found at compile time for "last %s"', $op->pv);
+           return $op->next; # ignore the op
+       }
+       # XXX Add support for "last" to leave non-loop blocks
+       if ($cxstack[$cxix]->{type} != CXt_LOOP) {
+           error('Use of "last" for non-loop blocks is not yet implemented');
+           return $op->next; # ignore the op
+       }
+    }
+    default_pp($op);
+    my $lastop = $cxstack[$cxix]->{lastop}->next;
+    push(@bblock_todo, $lastop);
+    runtime(sprintf("goto %s;", label($lastop)));
+    return $op->next;
+}
+
+sub pp_subst {
+    my $op = shift;
+    write_back_lexicals();
+    write_back_stack();
+    my $sym = doop($op);
+    my $replroot = $op->pmreplroot;
+    if (ad($replroot)) {
+       runtime sprintf("if (op == ((PMOP*)(%s))->op_pmreplroot) goto %s;",
+                       $sym, label($replroot));
+       $op->pmreplstart->save;
+       push(@bblock_todo, $replroot);
+    }
+    invalidate_lexicals();
+    return $op->next;
+}
+
+sub pp_substcont {
+    my $op = shift;
+    write_back_lexicals();
+    write_back_stack();
+    doop($op);
+    my $pmop = $op->other;
+    my $pmopsym = objsym($pmop);
+    runtime sprintf("if (op == ((PMOP*)(%s))->op_pmreplstart) goto %s;",
+                   $pmopsym, label($pmop->pmreplstart));
+    invalidate_lexicals();
+    return $pmop->next;
+}
+
+sub default_pp {
+    my $op = shift;
+    my $ppname = $op->ppaddr;
+    write_back_lexicals() unless $skip_lexicals{$ppname};
+    write_back_stack() unless $skip_stack{$ppname};
+    doop($op);
+    # XXX If the only way that ops can write to a TEMPORARY lexical is
+    # when it's named in $op->targ then we could call
+    # invalidate_lexicals(TEMPORARY) and avoid having to write back all
+    # the temporaries. For now, we'll play it safe and write back the lot.
+    invalidate_lexicals() unless $skip_invalidate{$ppname};
+    return $op->next;
+}
+
+sub compile_op {
+    my $op = shift;
+    my $ppname = $op->ppaddr;
+    if (exists $ignore_op{$ppname}) {
+       return $op->next;
+    }
+    debug peek_stack() if $debug_stack;
+    if ($debug_op) {
+       debug sprintf("%s [%s]\n",
+                    peekop($op),
+                    $op->flags & OPf_STACKED ? "OPf_STACKED" : $op->targ);
+    }
+    no strict 'refs';
+    if (defined(&$ppname)) {
+       $know_op = 0;
+       return &$ppname($op);
+    } else {
+       return default_pp($op);
+    }
+}
+
+sub compile_bblock {
+    my $op = shift;
+    #warn "compile_bblock: ", peekop($op), "\n"; # debug
+    write_label($op);
+    $know_op = 0;
+    do {
+       $op = compile_op($op);
+    } while (defined($op) && $$op && !exists($leaders->{$$op}));
+    write_back_stack(); # boo hoo: big loss
+    reload_lexicals();
+    return $op;
+}
+
+sub cc {
+    my ($name, $root, $start, @padlist) = @_;
+    my $op;
+    init_pp($name);
+    load_pad(@padlist);
+    B::Pseudoreg->new_scope;
+    @cxstack = ();
+    if ($debug_timings) {
+       warn sprintf("Basic block analysis at %s\n", timing_info);
+    }
+    $leaders = find_leaders($root, $start);
+    @bblock_todo = ($start, values %$leaders);
+    if ($debug_timings) {
+       warn sprintf("Compilation at %s\n", timing_info);
+    }
+    while (@bblock_todo) {
+       $op = shift @bblock_todo;
+       #warn sprintf("Considering basic block %s\n", peekop($op)); # debug
+       next if !defined($op) || !$$op || $done{$$op};
+       #warn "...compiling it\n"; # debug
+       do {
+           $done{$$op} = 1;
+           $op = compile_bblock($op);
+           if ($need_freetmps && $freetmps_each_bblock) {
+               runtime("FREETMPS;");
+               $need_freetmps = 0;
+           }
+       } while defined($op) && $$op && !$done{$$op};
+       if ($need_freetmps && $freetmps_each_loop) {
+           runtime("FREETMPS;");
+           $need_freetmps = 0;
+       }
+       if (!$$op) {
+           runtime("PUTBACK;", "return 0;");
+       } elsif ($done{$$op}) {
+           runtime(sprintf("goto %s;", label($op)));
+       }
+    }
+    if ($debug_timings) {
+       warn sprintf("Saving runtime at %s\n", timing_info);
+    }
+    save_runtime();
+}
+
+sub cc_recurse {
+    my $ccinfo;
+    my $start;
+    $start = cc_queue(@_) if @_;
+    while ($ccinfo = shift @cc_todo) {
+       cc(@$ccinfo);
+    }
+    return $start;
+}    
+
+sub cc_obj {
+    my ($name, $cvref) = @_;
+    my $cv = svref_2object($cvref);
+    my @padlist = $cv->PADLIST->ARRAY;
+    my $curpad_sym = $padlist[1]->save;
+    cc_recurse($name, $cv->ROOT, $cv->START, @padlist);
+}
+
+sub cc_main {
+    my @comppadlist = comppadlist->ARRAY;
+    my $curpad_sym = $comppadlist[1]->save;
+    my $start = cc_recurse("pp_main", main_root, main_start, @comppadlist);
+    if (@unused_sub_packages) {
+       save_unused_subs(@unused_sub_packages);
+       # That only queues them. Now we need to generate code for them.
+       cc_recurse();
+    }
+    return if $errors;
+    push_init(sprintf("main_root = sym_%x;", ad(main_root)),
+             "main_start = $start;",
+             "curpad = AvARRAY($curpad_sym);");
+    output_boilerplate();
+    print "\n";
+    output_all("perl_init");
+    output_runtime();
+    print "\n";
+    output_main();
+    if ($debug_timings) {
+       warn sprintf("Done at %s\n", timing_info);
+    }
+}
+
+sub compile {
+    my @options = @_;
+    my ($option, $opt, $arg);
+  OPTION:
+    while ($option = shift @options) {
+       if ($option =~ /^-(.)(.*)/) {
+           $opt = $1;
+           $arg = $2;
+       } else {
+           unshift @options, $option;
+           last OPTION;
+       }
+       if ($opt eq "-" && $arg eq "-") {
+           shift @options;
+           last OPTION;
+       } elsif ($opt eq "o") {
+           $arg ||= shift @options;
+           open(STDOUT, ">$arg") or return "$arg: $!\n";
+       } elsif ($opt eq "n") {
+           $arg ||= shift @options;
+           $module_name = $arg;
+       } elsif ($opt eq "u") {
+           $arg ||= shift @options;
+           push(@unused_sub_packages, $arg);
+       } elsif ($opt eq "f") {
+           $arg ||= shift @options;
+           my $value = $arg !~ s/^no-//;
+           $arg =~ s/-/_/g;
+           my $ref = $optimise{$arg};
+           if (defined($ref)) {
+               $$ref = $value;
+           } else {
+               warn qq(ignoring unknown optimisation option "$arg"\n);
+           }
+       } elsif ($opt eq "O") {
+           $arg = 1 if $arg eq "";
+           my $ref;
+           foreach $ref (values %optimise) {
+               $$ref = 0;
+           }
+           if ($arg >= 2) {
+               $freetmps_each_loop = 1;
+           }
+           if ($arg >= 1) {
+               $freetmps_each_bblock = 1 unless $freetmps_each_loop;
+           }
+       } elsif ($opt eq "D") {
+            $arg ||= shift @options;
+           foreach $arg (split(//, $arg)) {
+               if ($arg eq "o") {
+                   B->debug(1);
+               } elsif ($arg eq "O") {
+                   $debug_op = 1;
+               } elsif ($arg eq "s") {
+                   $debug_stack = 1;
+               } elsif ($arg eq "c") {
+                   $debug_cxstack = 1;
+               } elsif ($arg eq "p") {
+                   $debug_pad = 1;
+               } elsif ($arg eq "r") {
+                   $debug_runtime = 1;
+               } elsif ($arg eq "S") {
+                   $debug_shadow = 1;
+               } elsif ($arg eq "q") {
+                   $debug_queue = 1;
+               } elsif ($arg eq "l") {
+                   $debug_lineno = 1;
+               } elsif ($arg eq "t") {
+                   $debug_timings = 1;
+               }
+           }
+       }
+    }
+    init_init();
+    if (@options) {
+       return sub {
+           my ($objname, $ppname);
+           foreach $objname (@options) {
+               $objname = "main::$objname" unless $objname =~ /::/;
+               ($ppname = $objname) =~ s/^.*?:://;
+               eval "cc_obj(qq(pp_sub_$ppname), \\&$objname)";
+               die "cc_obj(qq(pp_sub_$ppname, \\&$objname) failed: $@" if $@;
+               return if $errors;
+           }
+           output_boilerplate();
+           print "\n";
+           output_all($module_name || "init_module");
+           output_runtime();
+       }
+    } else {
+       return sub { cc_main() };
+    }
+}
+
+1;
diff --git a/B/Debug.pm b/B/Debug.pm
new file mode 100644 (file)
index 0000000..859e6f1
--- /dev/null
@@ -0,0 +1,258 @@
+package B::Debug;
+use strict;
+use B qw(peekop class ad walkoptree walkoptree_exec
+         main_start main_root cstring sv_undef);
+use B::Asmdata qw(@specialsv_name);
+
+sub B::OP::debug {
+    my ($op) = @_;
+    printf <<'EOT', class($op), ad($op), ad($op->next), ad($op->sibling), $op->ppaddr, $op->targ, $op->type, $op->seq, $op->flags, $op->private;
+%s (0x%lx)
+       op_next         0x%x
+       op_sibling      0x%x
+       op_ppaddr       %s
+       op_targ         %d
+       op_type         %d
+       op_seq          %d
+       op_flags        %d
+       op_private      %d
+EOT
+}
+
+sub B::UNOP::debug {
+    my ($op) = @_;
+    $op->B::OP::debug();
+    printf "\top_first\t0x%x\n", ad($op->first);
+}
+
+sub B::BINOP::debug {
+    my ($op) = @_;
+    $op->B::UNOP::debug();
+    printf "\top_last\t\t0x%x\n", ad($op->last);
+}
+
+sub B::LOGOP::debug {
+    my ($op) = @_;
+    $op->B::UNOP::debug();
+    printf "\top_other\t0x%x\n", ad($op->other);
+}
+
+sub B::CONDOP::debug {
+    my ($op) = @_;
+    $op->B::UNOP::debug();
+    printf "\top_true\t0x%x\n", ad($op->true);
+    printf "\top_false\t0x%x\n", ad($op->false);
+}
+
+sub B::LISTOP::debug {
+    my ($op) = @_;
+    $op->B::BINOP::debug();
+    printf "\top_children\t%d\n", $op->children;
+}
+
+sub B::PMOP::debug {
+    my ($op) = @_;
+    $op->B::LISTOP::debug();
+    printf "\top_pmreplroot\t0x%x\n", ad($op->pmreplroot);
+    printf "\top_pmreplstart\t0x%x\n", ad($op->pmreplstart);
+    printf "\top_pmnext\t0x%x\n", ad($op->pmnext);
+    printf "\top_pmregexp->precomp\t%s\n", cstring($op->precomp);
+    printf "\top_pmshort\t0x%x\n", ad($op->pmshort);
+    printf "\top_pmflags\t0x%x\n", $op->pmflags;
+    printf "\top_pmslen\t%d\n", $op->pmslen;
+    $op->pmshort->debug;
+    $op->pmreplroot->debug;
+}
+
+sub B::COP::debug {
+    my ($op) = @_;
+    $op->B::OP::debug();
+    my ($filegv) = $op->filegv;
+    printf <<'EOT', $op->label, ad($op->stash), ad($filegv), $op->seq, $op->arybase, $op->line;
+       cop_label       %s
+       cop_stash       0x%x
+       cop_filegv      0x%x
+       cop_seq         %d
+       cop_arybase     %d
+       cop_line        %d
+EOT
+    $filegv->debug;
+}
+
+sub B::SVOP::debug {
+    my ($op) = @_;
+    $op->B::OP::debug();
+    printf "\top_sv\t\t0x%x\n", ad($op->sv);
+    $op->sv->debug;
+}
+
+sub B::PVOP::debug {
+    my ($op) = @_;
+    $op->B::OP::debug();
+    printf "\top_pv\t\t0x%x\n", $op->pv;
+}
+
+sub B::GVOP::debug {
+    my ($op) = @_;
+    $op->B::OP::debug();
+    printf "\top_gv\t\t0x%x\n", ad($op->gv);
+}
+
+sub B::CVOP::debug {
+    my ($op) = @_;
+    $op->B::OP::debug();
+    printf "\top_cv\t\t0x%x\n", ad($op->cv);
+}
+
+sub B::NULL::debug {
+    my ($sv) = @_;
+    if (ad($sv) == ad(sv_undef())) {
+       print "&sv_undef\n";
+    } else {
+       printf "NULL (0x%x)\n", ad($sv);
+    }
+}
+
+sub B::SV::debug {
+    my ($sv) = @_;
+    if (!$$sv) {
+       print class($sv), " = NULL\n";
+       return;
+    }
+    printf <<'EOT', class($sv), ad($sv), $sv->REFCNT, $sv->FLAGS;
+%s (0x%x)
+       REFCNT          %d
+       FLAGS           0x%x
+EOT
+}
+
+sub B::PV::debug {
+    my ($sv) = @_;
+    $sv->B::SV::debug();
+    my $pv = $sv->PV();
+    printf <<'EOT', cstring($pv), length($pv);
+       xpv_pv          %s
+       xpv_cur         %d
+EOT
+}
+
+sub B::IV::debug {
+    my ($sv) = @_;
+    $sv->B::SV::debug();
+    printf "\txiv_iv\t\t%d\n", $sv->IV;
+}
+
+sub B::NV::debug {
+    my ($sv) = @_;
+    $sv->B::IV::debug();
+    printf "\txnv_nv\t\t%s\n", $sv->NV;
+}
+
+sub B::PVIV::debug {
+    my ($sv) = @_;
+    $sv->B::PV::debug();
+    printf "\txiv_iv\t\t%d\n", $sv->IV;
+}
+
+sub B::PVNV::debug {
+    my ($sv) = @_;
+    $sv->B::PVIV::debug();
+    printf "\txnv_nv\t\t%s\n", $sv->NV;
+}
+
+sub B::PVLV::debug {
+    my ($sv) = @_;
+    $sv->B::PVNV::debug();
+    printf "\txlv_targoff\t%d\n", $sv->TARGOFF;
+    printf "\txlv_targlen\t%u\n", $sv->TARGLEN;
+    printf "\txlv_type\t%s\n", cstring(chr($sv->TYPE));
+}
+
+sub B::BM::debug {
+    my ($sv) = @_;
+    $sv->B::PVNV::debug();
+    printf "\txbm_useful\t%d\n", $sv->USEFUL;
+    printf "\txbm_previous\t%u\n", $sv->PREVIOUS;
+    printf "\txbm_rare\t%s\n", cstring(chr($sv->RARE));
+}
+
+sub B::CV::debug {
+    my ($sv) = @_;
+    $sv->B::PVNV::debug();
+    my ($stash) = $sv->STASH;
+    my ($start) = $sv->START;
+    my ($root) = $sv->ROOT;
+    my ($padlist) = $sv->PADLIST;
+    my ($gv) = $sv->GV;
+    my ($filegv) = $sv->FILEGV;
+    printf <<'EOT', ad($stash), ad($start), ad($root), ad($gv), ad($filegv), $sv->DEPTH, $padlist, ad($sv->OUTSIDE);
+       STASH           0x%x
+       START           0x%x
+       ROOT            0x%x
+       GV              0x%x
+       FILEGV          0x%x
+       DEPTH           %d
+       PADLIST         0x%x                           
+       OUTSIDE         0x%x
+EOT
+    $start->debug if $start;
+    $root->debug if $root;
+    $gv->debug if $gv;
+    $filegv->debug if $filegv;
+    $padlist->debug if $padlist;
+}
+
+sub B::AV::debug {
+    my ($av) = @_;
+    $av->B::SV::debug;
+    my(@array) = $av->ARRAY;
+    print "\tARRAY\t\t(", join(", ", map("0x" . ad($_), @array)), ")\n";
+    printf <<'EOT', scalar(@array), $av->MAX, $av->OFF, $av->AvFLAGS;
+       FILL            %d    
+       MAX             %d
+       OFF             %d
+       AvFLAGS         %d
+EOT
+}
+    
+sub B::GV::debug {
+    my ($gv) = @_;
+    my ($sv) = $gv->SV;
+    my ($av) = $gv->AV;
+    my ($cv) = $gv->CV;
+    $gv->B::SV::debug;
+    printf <<'EOT', $gv->NAME, $gv->STASH, ad($sv), $gv->GvREFCNT, $gv->FORM, ad($av), ad($gv->HV), ad($gv->EGV), ad($cv), $gv->CVGEN, $gv->LINE, $gv->FILEGV, $gv->GvFLAGS;
+       NAME            %s
+       STASH           0x%x
+       SV              0x%x
+       GvREFCNT        %d
+       FORM            0x%x
+       AV              0x%x
+       HV              0x%x
+       EGV             0x%x
+       CV              0x%x
+       CVGEN           %d
+       LINE            %d
+       FILEGV          0x%x
+       GvFLAGS         0x%x
+EOT
+    $sv->debug if $sv;
+    $av->debug if $av;
+    $cv->debug if $cv;
+}
+
+sub B::SPECIAL::debug {
+    my $sv = shift;
+    print $specialsv_name[$$sv], "\n";
+}
+
+sub compile {
+    my $order = shift;
+    if ($order eq "exec") {
+        return sub { walkoptree_exec(main_start, "debug") }
+    } else {
+        return sub { walkoptree(main_root, "debug") }
+    }
+}
+
+1;
diff --git a/B/Disassembler.pm b/B/Disassembler.pm
new file mode 100644 (file)
index 0000000..36db354
--- /dev/null
@@ -0,0 +1,144 @@
+#      Disassembler.pm
+#
+#      Copyright (c) 1996 Malcolm Beattie
+#
+#      You may distribute under the terms of either the GNU General Public
+#      License or the Artistic License, as specified in the README file.
+package B::Disassembler::BytecodeStream;
+use FileHandle;
+use Carp;
+use B qw(cstring cast_I32);
+@ISA = qw(FileHandle);
+sub readn {
+    my ($fh, $len) = @_;
+    my $data;
+    read($fh, $data, $len);
+    croak "reached EOF while reading $len bytes" unless length($data) == $len;
+    return $data;
+}
+
+sub GET_U8 {
+    my $fh = shift;
+    my $c = $fh->getc;
+    croak "reached EOF while reading U8" unless defined($c);
+    return ord($c);
+}
+
+sub GET_U16 {
+    my $fh = shift;
+    my $str = $fh->readn(2);
+    croak "reached EOF while reading U16" unless length($str) == 2;
+    return unpack("n", $str);
+}
+
+sub GET_U32 {
+    my $fh = shift;
+    my $str = $fh->readn(4);
+    croak "reached EOF while reading U32" unless length($str) == 4;
+    return unpack("N", $str);
+}
+
+sub GET_I32 {
+    my $fh = shift;
+    my $str = $fh->readn(4);
+    croak "reached EOF while reading I32" unless length($str) == 4;
+    return cast_I32(unpack("N", $str));
+}
+
+sub GET_objindex { 
+    my $fh = shift;
+    my $str = $fh->readn(4);
+    croak "reached EOF while reading objindex" unless length($str) == 4;
+    return unpack("N", $str);
+}
+
+sub GET_strconst {
+    my $fh = shift;
+    my ($str, $c);
+    while (defined($c = $fh->getc) && $c ne "\0") {
+       $str .= $c;
+    }
+    croak "reached EOF while reading strconst" unless defined($c);
+    return cstring($str);
+}
+
+sub GET_pvcontents {}
+
+sub GET_PV {
+    my $fh = shift;
+    my $str;
+    my $len = $fh->GET_U32;
+    if ($len) {
+       read($fh, $str, $len);
+       croak "reached EOF while reading PV" unless length($str) == $len;
+       return cstring($str);
+    } else {
+       return '""';
+    }
+}
+
+sub GET_comment {
+    my $fh = shift;
+    my ($str, $c);
+    while (defined($c = $fh->getc) && $c ne "\n") {
+       $str .= $c;
+    }
+    croak "reached EOF while reading comment" unless defined($c);
+    return cstring($str);
+}
+
+sub GET_double {
+    my $fh = shift;
+    my ($str, $c);
+    while (defined($c = $fh->getc) && $c ne "\0") {
+       $str .= $c;
+    }
+    croak "reached EOF while reading double" unless defined($c);
+    return $str;
+}
+
+sub GET_none {}
+
+sub GET_op_tr_array {
+    my $fh = shift;
+    my @ary = unpack("n256", $fh->readn(256 * 2));
+    return join(",", @ary);
+}
+
+sub GET_IV64 {
+    my $fh = shift;
+    my ($hi, $lo) = unpack("NN", $fh->readn(8));
+    return sprintf("0x%4x%04x", $hi, $lo); # cheat
+}
+
+package B::Disassembler;
+use Exporter;
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(disassemble_fh);
+use Carp;
+use strict;
+
+use B::Asmdata qw(%insn_data @insn_name);
+
+sub disassemble_fh {
+    my ($fh, $out) = @_;
+    my ($c, $getmeth, $insn, $arg);
+    bless $fh, "B::Disassembler::BytecodeStream";
+    while (defined($c = $fh->getc)) {
+       $c = ord($c);
+       $insn = $insn_name[$c];
+       if (!defined($insn) || $insn eq "unused") {
+           my $pos = $fh->tell - 1;
+           die "Illegal instruction code $c at stream offset $pos\n";
+       }
+       $getmeth = $insn_data{$insn}->[2];
+       $arg = $fh->$getmeth();
+       if (defined($arg)) {
+           &$out($insn, $arg);
+       } else {
+           &$out($insn);
+       }
+    }
+}
+
+1;
diff --git a/B/Showlex.pm b/B/Showlex.pm
new file mode 100644 (file)
index 0000000..9cf8ecc
--- /dev/null
@@ -0,0 +1,58 @@
+package B::Showlex;
+use strict;
+use B qw(svref_2object comppadlist class);
+use B::Terse ();
+
+#
+# Invoke as
+#     perl -MO=Showlex,foo bar.pl
+# to see the names of lexical variables used by &foo
+# or as
+#     perl -MO=Showlex bar.pl
+# to see the names of file scope lexicals used by bar.pl
+#    
+
+sub showarray {
+    my ($name, $av) = @_;
+    my @els = $av->ARRAY;
+    my $count = @els;
+    my $i;
+    print "$name has $count entries\n";
+    for ($i = 0; $i < $count; $i++) {
+       print "$i: ";
+       $els[$i]->terse;
+    }
+}
+
+sub showlex {
+    my ($objname, $namesav, $valsav) = @_;
+    showarray("Pad of lexical names for $objname", $namesav);
+    showarray("Pad of lexical values for $objname", $valsav);
+}
+
+sub showlex_obj {
+    my ($objname, $obj) = @_;
+    $objname =~ s/^&main::/&/;
+    showlex($objname, svref_2object($obj)->PADLIST->ARRAY);
+}
+
+sub showlex_main {
+    showlex("comppadlist", comppadlist->ARRAY);
+}
+
+sub compile {
+    my @options = @_;
+    if (@options) {
+       return sub {
+           my $objname;
+           foreach $objname (@options) {
+               $objname = "main::$objname" unless $objname =~ /::/;
+               eval "showlex_obj('&$objname', \\&$objname)";
+           }
+       }
+    } else {
+       return \&showlex_main;
+    }
+}
+
+1;
diff --git a/B/Stackobj.pm b/B/Stackobj.pm
new file mode 100644 (file)
index 0000000..fa9de7d
--- /dev/null
@@ -0,0 +1,280 @@
+#      Stackobj.pm
+#
+#      Copyright (c) 1996 Malcolm Beattie
+#
+#      You may distribute under the terms of either the GNU General Public
+#      License or the Artistic License, as specified in the README file.
+#
+package B::Stackobj;
+use Exporter ();
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(set_callback T_UNKNOWN T_DOUBLE T_INT
+               VALID_INT VALID_DOUBLE VALID_SV REGISTER TEMPORARY);
+%EXPORT_TAGS = (types => [qw(T_UNKNOWN T_DOUBLE T_INT)],
+               flags => [qw(VALID_INT VALID_DOUBLE VALID_SV
+                            REGISTER TEMPORARY)]);
+
+use Carp qw(confess);
+use strict;
+use B qw(class);
+
+# Perl internal constants that I should probably define elsewhere.
+sub SVf_IOK () { 0x10000 }
+sub SVf_NOK () { 0x20000 }
+
+# Types
+sub T_UNKNOWN () { 0 }
+sub T_DOUBLE ()  { 1 }
+sub T_INT ()     { 2 }
+
+# Flags
+sub VALID_INT ()       { 0x01 }
+sub VALID_DOUBLE ()    { 0x02 }
+sub VALID_SV ()                { 0x04 }
+sub REGISTER ()                { 0x08 } # no implicit write-back when calling subs
+sub TEMPORARY ()       { 0x10 } # no implicit write-back needed at all
+
+#
+# Callback for runtime code generation
+#
+my $runtime_callback = sub { confess "set_callback not yet called" };
+sub set_callback (&) { $runtime_callback = shift }
+sub runtime { &$runtime_callback(@_) }
+
+#
+# Methods
+#
+
+sub write_back { confess "stack object does not implement write_back" }
+
+sub invalidate { shift->{flags} &= ~(VALID_INT | VALID_DOUBLE) }
+
+sub as_sv {
+    my $obj = shift;
+    if (!($obj->{flags} & VALID_SV)) {
+       $obj->write_back;
+       $obj->{flags} |= VALID_SV;
+    }
+    return $obj->{sv};
+}
+
+sub as_int {
+    my $obj = shift;
+    if (!($obj->{flags} & VALID_INT)) {
+       $obj->load_int;
+       $obj->{flags} |= VALID_INT;
+    }
+    return $obj->{iv};
+}
+
+sub as_double {
+    my $obj = shift;
+    if (!($obj->{flags} & VALID_DOUBLE)) {
+       $obj->load_double;
+       $obj->{flags} |= VALID_DOUBLE;
+    }
+    return $obj->{nv};
+}
+
+sub as_numeric {
+    my $obj = shift;
+    return $obj->{type} == T_INT ? $obj->as_int : $obj->as_double;
+}
+
+#
+# Debugging methods
+#
+sub peek {
+    my $obj = shift;
+    my $type = $obj->{type};
+    my $flags = $obj->{flags};
+    my @flags;
+    if ($type == T_UNKNOWN) {
+       $type = "T_UNKNOWN";
+    } elsif ($type == T_INT) {
+       $type = "T_INT";
+    } elsif ($type == T_DOUBLE) {
+       $type = "T_DOUBLE";
+    } else {
+       $type = "(illegal type $type)";
+    }
+    push(@flags, "VALID_INT") if $flags & VALID_INT;
+    push(@flags, "VALID_DOUBLE") if $flags & VALID_DOUBLE;
+    push(@flags, "VALID_SV") if $flags & VALID_SV;
+    push(@flags, "REGISTER") if $flags & REGISTER;
+    push(@flags, "TEMPORARY") if $flags & TEMPORARY;
+    @flags = ("none") unless @flags;
+    return sprintf("%s type=$type flags=%s sv=$obj->{sv}",
+                  class($obj), join("|", @flags));
+}
+
+sub minipeek {
+    my $obj = shift;
+    my $type = $obj->{type};
+    my $flags = $obj->{flags};
+    if ($type == T_INT || $flags & VALID_INT) {
+       return $obj->{iv};
+    } elsif ($type == T_DOUBLE || $flags & VALID_DOUBLE) {
+       return $obj->{nv};
+    } else {
+       return $obj->{sv};
+    }
+}
+
+#
+# Caller needs to ensure that set_int, set_double,
+# set_numeric and set_sv are only invoked on legal lvalues.
+#
+sub set_int {
+    my ($obj, $expr) = @_;
+    runtime("$obj->{iv} = $expr;");
+    $obj->{flags} &= ~(VALID_SV | VALID_DOUBLE);
+    $obj->{flags} |= VALID_INT;
+}
+
+sub set_double {
+    my ($obj, $expr) = @_;
+    runtime("$obj->{nv} = $expr;");
+    $obj->{flags} &= ~(VALID_SV | VALID_INT);
+    $obj->{flags} |= VALID_DOUBLE;
+}
+
+sub set_numeric {
+    my ($obj, $expr) = @_;
+    if ($obj->{type} == T_INT) {
+       $obj->set_int($expr);
+    } else {
+       $obj->set_double($expr);
+    }
+}
+
+sub set_sv {
+    my ($obj, $expr) = @_;
+    runtime("SvSetSV($obj->{sv}, $expr);");
+    $obj->invalidate;
+    $obj->{flags} |= VALID_SV;
+}
+
+#
+# Stackobj::Padsv
+#
+
+@B::Stackobj::Padsv::ISA = 'B::Stackobj';
+sub B::Stackobj::Padsv::new {
+    my ($class, $type, $extra_flags, $ix, $iname, $dname) = @_;
+    bless {
+       type => $type,
+       flags => VALID_SV | $extra_flags,
+       sv => "curpad[$ix]",
+       iv => "$iname",
+       nv => "$dname"
+    }, $class;
+}
+
+sub B::Stackobj::Padsv::load_int {
+    my $obj = shift;
+    if ($obj->{flags} & VALID_DOUBLE) {
+       runtime("$obj->{iv} = $obj->{nv};");
+    } else {
+       runtime("$obj->{iv} = SvIV($obj->{sv});");
+    }
+    $obj->{flags} |= VALID_INT;
+}
+
+sub B::Stackobj::Padsv::load_double {
+    my $obj = shift;
+    runtime("$obj->{nv} = SvNV($obj->{sv});");
+    $obj->{flags} |= VALID_DOUBLE;
+}
+
+sub B::Stackobj::Padsv::write_back {
+    my $obj = shift;
+    my $flags = $obj->{flags};
+    return if $flags & VALID_SV;
+    if ($flags & VALID_INT) {
+       runtime("sv_setiv($obj->{sv}, $obj->{iv});");
+    } elsif ($flags & VALID_DOUBLE) {
+       runtime("sv_setnv($obj->{sv}, $obj->{nv});");
+    } else {
+       confess "write_back failed for lexical @{[$obj->peek]}\n";
+    }
+    $obj->{flags} |= VALID_SV;
+}
+
+#
+# Stackobj::Const
+#
+
+@B::Stackobj::Const::ISA = 'B::Stackobj';
+sub B::Stackobj::Const::new {
+    my ($class, $sv) = @_;
+    my $obj = bless {
+       flags => 0,
+       sv => $sv    # holds the SV object until write_back happens
+    }, $class;
+    my $svflags = $sv->FLAGS;
+    if ($svflags & SVf_IOK) {
+       $obj->{flags} = VALID_INT|VALID_DOUBLE;
+       $obj->{type} = T_INT;
+       $obj->{nv} = $obj->{iv} = $sv->IV;
+    } elsif ($svflags & SVf_NOK) {
+       $obj->{flags} = VALID_INT|VALID_DOUBLE;
+       $obj->{type} = T_DOUBLE;
+       $obj->{iv} = $obj->{nv} = $sv->NV;
+    } else {
+       $obj->{type} = T_UNKNOWN;
+    }
+    return $obj;
+}
+
+sub B::Stackobj::Const::write_back {
+    my $obj = shift;
+    return if $obj->{flags} & VALID_SV;
+    # Save the SV object and replace $obj->{sv} by its C source code name
+    $obj->{sv} = $obj->{sv}->save;
+    $obj->{flags} |= VALID_SV|VALID_INT|VALID_DOUBLE;
+}
+
+sub B::Stackobj::Const::load_int {
+    my $obj = shift;
+    $obj->{iv} = int($obj->{sv}->PV);
+    $obj->{flags} |= VALID_INT;
+}
+
+sub B::Stackobj::Const::load_double {
+    my $obj = shift;
+    $obj->{nv} = $obj->{sv}->PV + 0.0;
+    $obj->{flags} |= VALID_DOUBLE;
+}
+
+sub B::Stackobj::Const::invalidate {}
+
+#
+# Stackobj::Bool
+#
+
+@B::Stackobj::Bool::ISA = 'B::Stackobj';
+sub B::Stackobj::Bool::new {
+    my ($class, $preg) = @_;
+    my $obj = bless {
+       type => T_INT,
+       flags => VALID_INT|VALID_DOUBLE,
+       iv => $$preg,
+       nv => $$preg,
+       preg => $preg           # this holds our ref to the pseudo-reg
+    }, $class;
+    return $obj;
+}
+
+sub B::Stackobj::Bool::write_back {
+    my $obj = shift;
+    return if $obj->{flags} & VALID_SV;
+    $obj->{sv} = "($obj->{iv} ? &sv_yes : &sv_no)";
+    $obj->{flags} |= VALID_SV;
+}
+
+# XXX Might want to handle as_double/set_double/load_double?
+
+sub B::Stackobj::Bool::invalidate {}
+
+1;
diff --git a/B/Terse.pm b/B/Terse.pm
new file mode 100644 (file)
index 0000000..eec2b00
--- /dev/null
@@ -0,0 +1,132 @@
+package B::Terse;
+use strict;
+use B qw(peekop class ad walkoptree walkoptree_exec
+        main_start main_root cstring svref_2object);
+use B::Asmdata qw(@specialsv_name);
+
+sub terse {
+    my ($order, $cvref) = @_;
+    my $cv = svref_2object($cvref);
+    if ($order eq "exec") {
+       walkoptree_exec($cv->START, "terse");
+    } else {
+       walkoptree($cv->ROOT, "terse");
+    }
+}
+
+sub compile {
+    my $order = shift;
+    my @options = @_;
+    if (@options) {
+       return sub {
+           my $objname;
+           foreach $objname (@options) {
+               $objname = "main::$objname" unless $objname =~ /::/;
+               eval "terse(\$order, \\&$objname)";
+               die "terse($order, \\&$objname) failed: $@" if $@;
+           }
+       }
+    } else {
+       if ($order eq "exec") {
+           return sub { walkoptree_exec(main_start, "terse") }
+       } else {
+           return sub { walkoptree(main_root, "terse") }
+       }
+    }
+}
+
+sub indent {
+    my $level = shift;
+    return "    " x $level;
+}
+
+sub B::OP::terse {
+    my ($op, $level) = @_;
+    my $targ = $op->targ;
+    $targ = ($targ > 0) ? " [$targ]" : "";
+    print indent($level), peekop($op), $targ, "\n";
+}
+
+sub B::SVOP::terse {
+    my ($op, $level) = @_;
+    print indent($level), peekop($op), "  ";
+    $op->sv->terse(0);
+}
+
+sub B::GVOP::terse {
+    my ($op, $level) = @_;
+    print indent($level), peekop($op), "  ";
+    $op->gv->terse(0);
+}
+
+sub B::PMOP::terse {
+    my ($op, $level) = @_;
+    my $precomp = $op->precomp;
+    print indent($level), peekop($op),
+       defined($precomp) ? " /$precomp/\n" : " (regexp not compiled)\n";
+
+}
+
+sub B::PVOP::terse {
+    my ($op, $level) = @_;
+    print indent($level), peekop($op), " ", cstring($op->pv), "\n";
+}
+
+sub B::COP::terse {
+    my ($op, $level) = @_;
+    my $label = $op->label;
+    if ($label) {
+       $label = " label ".cstring($label);
+    }
+    print indent($level), peekop($op), $label, "\n";
+}
+
+sub B::PV::terse {
+    my ($sv, $level) = @_;
+    print indent($level);
+    printf "%s (0x%lx) %s\n", class($sv), ad($sv), cstring($sv->PV);
+}
+
+sub B::AV::terse {
+    my ($sv, $level) = @_;
+    print indent($level);
+    printf "%s (0x%lx) FILL %d\n", class($sv), ad($sv), $sv->FILL;
+}
+
+sub B::GV::terse {
+    my ($gv, $level) = @_;
+    my $stash = $gv->STASH->NAME;
+    if ($stash eq "main") {
+       $stash = "";
+    } else {
+       $stash = $stash . "::";
+    }
+    print indent($level);
+    printf "%s (0x%lx) *%s%s\n", class($gv), ad($gv), $stash, $gv->NAME;
+}
+
+sub B::IV::terse {
+    my ($sv, $level) = @_;
+    print indent($level);
+    printf "%s (0x%lx) %d\n", class($sv), ad($sv), $sv->IV;
+}
+
+sub B::NV::terse {
+    my ($sv, $level) = @_;
+    print indent($level);
+    printf "%s (0x%lx) %s\n", class($sv), ad($sv), $sv->NV;
+}
+
+sub B::NULL::terse {
+    my ($sv, $level) = @_;
+    print indent($level);
+    printf "%s (0x%lx)\n", class($sv), ad($sv);
+}
+    
+sub B::SPECIAL::terse {
+    my ($sv, $level) = @_;
+    print indent($level);
+    printf "%s #%d %s\n", class($sv), $$sv, $specialsv_name[$$sv];
+}
+
+1;
diff --git a/B/Xref.pm b/B/Xref.pm
new file mode 100644 (file)
index 0000000..8a29ba3
--- /dev/null
+++ b/B/Xref.pm
@@ -0,0 +1,391 @@
+package B::Xref;
+
+=head1 NAME
+
+B::Xref - Generates cross reference reports for Perl programs
+
+=head1 SYNOPSIS
+
+perl -MO=Xref[,OPTIONS] foo.pl
+
+=head1 DESCRIPTION
+
+The B::Xref module is used to generate a cross reference listing of all
+definitions and uses of variables, subroutines and formats in a Perl program.
+It is implemented as a backend for the Perl compiler.
+
+The report generated is in the following format:
+
+    File filename1
+      Subroutine subname1
+       Package package1
+         object1        C<line numbers>
+         object2        C<line numbers>
+         ...
+       Package package2
+       ...
+
+Each B<File> section reports on a single file. Each B<Subroutine> section
+reports on a single subroutine apart from the special cases
+"(definitions)" and "(main)". These report, respectively, on subroutine
+definitions found by the initial symbol table walk and on the main part of
+the program or module external to all subroutines.
+
+The report is then grouped by the B<Package> of each variable,
+subroutine or format with the special case "(lexicals)" meaning
+lexical variables. Each B<object> name (implicitly qualified by its
+containing B<Package>) includes its type character(s) at the beginning
+where possible. Lexical variables are easier to track and even
+included dereferencing information where possible.
+
+The C<line numbers> are a comma separated list of line numbers (some
+preceded by code letters) where that object is used in some way.
+Simple uses aren't preceded by a code letter. Introductions (such as
+where a lexical is first defined with C<my>) are indicated with the
+letter "i". Subroutine and method calls are indicated by the character
+"&".  Subroutine definitions are indicated by "s" and format
+definitions by "f".
+
+=head1 OPTIONS
+
+Option words are separated by commas (not whitespace) and follow the
+usual conventions of compiler backend options.
+
+=over 8
+
+=item C<-oFILENAME>
+
+Directs output to C<FILENAME> instead of standard output.
+
+=item C<-r>
+
+Raw output. Instead of producing a human-readable report, outputs a line
+in machine-readable form for each definition/use of a variable/sub/format.
+
+=item C<-D[tO]>
+
+(Internal) debug options, probably only useful if C<-r> included.
+The C<t> option prints the object on the top of the stack as it's
+being tracked. The C<O> option prints each operator as it's being
+processed in the execution order of the program.
+
+=back
+
+=head1 BUGS
+
+Non-lexical variables are quite difficult to track through a program.
+Sometimes the type of a non-lexical variable's use is impossible to
+determine. Introductions of non-lexical non-scalars don't seem to be
+reported properly.
+
+=head1 AUTHOR
+
+Malcolm Beattie, mbeattie@sable.ox.ac.uk.
+
+=cut
+
+use strict;
+use B qw(peekop class ad comppadlist main_start svref_2object walksymtable);
+
+# Constants (should probably be elsewhere)
+sub OPpLVAL_INTRO () { 128 }
+sub SVf_POK () { 0x40000 }
+
+sub UNKNOWN { ["?", "?", "?"] }
+
+my @pad;                       # lexicals in current pad
+                               # as ["(lexical)", type, name]
+my %done;                      # keyed by $$op: set when each $op is done
+my $top = UNKNOWN;             # shadows top element of stack as
+                               # [pack, type, name] (pack can be "(lexical)")
+my $file;                      # shadows current filename
+my $line;                      # shadows current line number
+my $subname;                   # shadows current sub name
+my %table;                     # Multi-level hash to record all uses etc.
+my @todo = ();                 # List of CVs that need processing
+
+my %code = (intro => "i", used => "",
+           subdef => "s", subused => "&",
+           formdef => "f", meth => "->");
+
+
+# Options
+my ($debug_op, $debug_top, $nodefs, $raw);
+
+sub process {
+    my ($var, $event) = @_;
+    my ($pack, $type, $name) = @$var;
+    if ($type eq "*") {
+       if ($event eq "used") {
+           return;
+       } elsif ($event eq "subused") {
+           $type = "&";
+       }
+    }
+    $type =~ s/(.)\*$/$1/g;
+    if ($raw) {
+       printf "%-16s %-12s %5d %-12s %4s %-16s %s\n",
+           $file, $subname, $line, $pack, $type, $name, $event;
+    } else {
+       # Wheee
+       push(@{$table{$file}->{$subname}->{$pack}->{$type.$name}->{$event}},
+           $line);
+    }
+}
+
+sub load_pad {
+    my $padlist = shift;
+    my ($namelistav, @namelist, $ix);
+    @pad = ();
+    return if class($padlist) eq "SPECIAL";
+    ($namelistav) = $padlist->ARRAY;
+    @namelist = $namelistav->ARRAY;
+    for ($ix = 1; $ix < @namelist; $ix++) {
+       my $namesv = $namelist[$ix];
+       next if class($namesv) eq "SPECIAL";
+       my ($type, $name) = $namesv->PV =~ /^(.)(.*)$/;
+       $pad[$ix] = ["(lexical)", $type, $name];
+    }
+}
+
+sub xref {
+    my $start = shift;
+    my $op;
+    for ($op = $start; $$op; $op = $op->next) {
+       last if $done{$$op}++;
+       warn sprintf("top = [%s, %s, %s]\n", @$top) if $debug_top;
+       warn peekop($op), "\n" if $debug_op;
+       my $ppname = $op->ppaddr;
+       if ($ppname =~ /^pp_(or|and|mapwhile|grepwhile)$/) {
+           xref($op->other);
+       } elsif ($ppname eq "pp_match" || $ppname eq "pp_subst") {
+           xref($op->pmreplstart);
+       } elsif ($ppname eq "pp_substcont") {
+           xref($op->other->pmreplstart);
+           $op = $op->other;
+           redo;
+       } elsif ($ppname eq "pp_cond_expr") {
+           # pp_cond_expr never returns op_next
+           xref($op->true);
+           $op = $op->false;
+           redo;
+       } elsif ($ppname eq "pp_enterloop") {
+           xref($op->redoop);
+           xref($op->nextop);
+           xref($op->lastop);
+       } elsif ($ppname eq "pp_subst") {
+           xref($op->pmreplstart);
+       } else {
+           no strict 'refs';
+           &$ppname($op) if defined(&$ppname);
+       }
+    }
+}
+
+sub xref_cv {
+    my $cv = shift;
+    my $pack = $cv->GV->STASH->NAME;
+    $subname = ($pack eq "main" ? "" : "$pack\::") . $cv->GV->NAME;
+    load_pad($cv->PADLIST);
+    xref($cv->START);
+    $subname = "(main)";
+}
+
+sub xref_object {
+    my $cvref = shift;
+    xref_cv(svref_2object($cvref));
+}
+
+sub xref_main {
+    $subname = "(main)";
+    load_pad(comppadlist);
+    xref(main_start);
+    while (@todo) {
+       xref_cv(shift @todo);
+    }
+}
+
+sub pp_nextstate {
+    my $op = shift;
+    $file = $op->filegv->SV->PV;
+    $line = $op->line;
+    $top = UNKNOWN;
+}
+
+sub pp_padsv {
+    my $op = shift;
+    $top = $pad[$op->targ];
+    process($top, $op->private & OPpLVAL_INTRO ? "intro" : "used");
+}
+
+sub pp_padav { pp_padsv(@_) }
+sub pp_padhv { pp_padsv(@_) }
+
+sub deref {
+    my ($var, $as) = @_;
+    $var->[1] = $as . $var->[1];
+    process($var, "used");
+}
+
+sub pp_rv2cv { deref($top, "&"); }
+sub pp_rv2hv { deref($top, "%"); }
+sub pp_rv2sv { deref($top, "\$"); }
+sub pp_rv2av { deref($top, "\@"); }
+sub pp_rv2gv { deref($top, "*"); }
+
+sub pp_gvsv {
+    my $op = shift;
+    my $gv = $op->gv;
+    $top = [$gv->STASH->NAME, '$', $gv->NAME];
+    process($top, $op->private & OPpLVAL_INTRO ? "intro" : "used");
+}
+
+sub pp_gv {
+    my $op = shift;
+    my $gv = $op->gv;
+    $top = [$gv->STASH->NAME, "*", $gv->NAME];
+    process($top, $op->private & OPpLVAL_INTRO ? "intro" : "used");
+}
+
+sub pp_const {
+    my $op = shift;
+    my $sv = $op->sv;
+    $top = ["?", "", $sv->FLAGS & SVf_POK ? $sv->PV : "?"];
+}
+
+sub pp_method {
+    my $op = shift;
+    $top = ["(method)", "->".$top->[1], $top->[2]];
+}
+
+sub pp_entersub {
+    my $op = shift;
+    if ($top->[1] eq "m") {
+       process($top, "meth");
+    } else {
+       process($top, "subused");
+    }
+    $top = UNKNOWN;
+}
+
+#
+# Stuff for cross referencing definitions of variables and subs
+#
+
+sub B::GV::xref {
+    my $gv = shift;
+    my $cv = $gv->CV;
+    if (ad($cv)) {
+       #return if $done{$$cv}++;
+       $file = $gv->FILEGV->SV->PV;
+       $line = $gv->LINE;
+       process([$gv->STASH->NAME, "&", $gv->NAME], "subdef");
+       push(@todo, $cv);
+    }
+    my $form = $gv->FORM;
+    if (ad($form)) {
+       return if $done{$$form}++;
+       $file = $gv->FILEGV->SV->PV;
+       $line = $gv->LINE;
+       process([$gv->STASH->NAME, "", $gv->NAME], "formdef");
+    }
+}
+
+sub xref_definitions {
+    my ($pack, %exclude);
+    return if $nodefs;
+    $subname = "(definitions)";
+    foreach $pack (qw(B O AutoLoader DynaLoader Config DB VMS
+                     strict vars FileHandle Exporter Carp)) {
+        $exclude{$pack."::"} = 1;
+    }
+    no strict qw(vars refs);
+    walksymtable(\%{"main::"}, "xref", sub { !defined($exclude{$_[0]}) });
+}
+
+sub output {
+    return if $raw;
+    my ($file, $subname, $pack, $name, $ev, $perfile, $persubname,
+       $perpack, $pername, $perev);
+    foreach $file (sort(keys(%table))) {
+       $perfile = $table{$file};
+       print "File $file\n";
+       foreach $subname (sort(keys(%$perfile))) {
+           $persubname = $perfile->{$subname};
+           print "  Subroutine $subname\n";
+           foreach $pack (sort(keys(%$persubname))) {
+               $perpack = $persubname->{$pack};
+               print "    Package $pack\n";
+               foreach $name (sort(keys(%$perpack))) {
+                   $pername = $perpack->{$name};
+                   my @lines;
+                   foreach $ev (qw(intro formdef subdef meth subused used)) {
+                       $perev = $pername->{$ev};
+                       if (defined($perev) && @$perev) {
+                           my $code = $code{$ev};
+                           push(@lines, map("$code$_", @$perev));
+                       }
+                   }
+                   printf "      %-16s  %s\n", $name, join(", ", @lines);
+               }
+           }
+       }
+    }
+}
+
+sub compile {
+    my @options = @_;
+    my ($option, $opt, $arg);
+  OPTION:
+    while ($option = shift @options) {
+       if ($option =~ /^-(.)(.*)/) {
+           $opt = $1;
+           $arg = $2;
+       } else {
+           unshift @options, $option;
+           last OPTION;
+       }
+       if ($opt eq "-" && $arg eq "-") {
+           shift @options;
+           last OPTION;
+       } elsif ($opt eq "o") {
+           $arg ||= shift @options;
+           open(STDOUT, ">$arg") or return "$arg: $!\n";
+       } elsif ($opt eq "d") {
+           $nodefs = 1;
+       } elsif ($opt eq "r") {
+           $raw = 1;
+       } elsif ($opt eq "D") {
+            $arg ||= shift @options;
+           foreach $arg (split(//, $arg)) {
+               if ($arg eq "o") {
+                   B->debug(1);
+               } elsif ($arg eq "O") {
+                   $debug_op = 1;
+               } elsif ($arg eq "t") {
+                   $debug_top = 1;
+               }
+           }
+       }
+    }
+    if (@options) {
+       return sub {
+           my $objname;
+           xref_definitions();
+           foreach $objname (@options) {
+               $objname = "main::$objname" unless $objname =~ /::/;
+               eval "xref_object(\\&$objname)";
+               die "xref_object(\\&$objname) failed: $@" if $@;
+           }
+           output();
+       }
+    } else {
+       return sub {
+           xref_definitions();
+           xref_main();
+           output();
+       }
+    }
+}
+
+1;
diff --git a/Copying b/Copying
new file mode 100644 (file)
index 0000000..3c68f02
--- /dev/null
+++ b/Copying
@@ -0,0 +1,248 @@
+                   GNU GENERAL PUBLIC LICENSE
+                    Version 1, February 1989
+
+ Copyright (C) 1989 Free Software Foundation, Inc.
+                    675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The license agreements of most software companies try to keep users
+at the mercy of those companies.  By contrast, our General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  The
+General Public License applies to the Free Software Foundation's
+software and to any other program whose authors commit to using it.
+You can use it for your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Specifically, the General Public License is designed to make
+sure that you have the freedom to give away or sell copies of free
+software, that you receive source code or can get it if you want it,
+that you can change the software or use pieces of it in new free
+programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of a such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must tell them their rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any program or other work which
+contains a notice placed by the copyright holder saying it may be
+distributed under the terms of this General Public License.  The
+"Program", below, refers to any such program or work, and a "work based
+on the Program" means either the Program or any work containing the
+Program or a portion of it, either verbatim or with modifications.  Each
+licensee is addressed as "you".
+
+  1. You may copy and distribute verbatim copies of the Program's source
+code as you receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice and
+disclaimer of warranty; keep intact all the notices that refer to this
+General Public License and to the absence of any warranty; and give any
+other recipients of the Program a copy of this General Public License
+along with the Program.  You may charge a fee for the physical act of
+transferring a copy.
+
+  2. You may modify your copy or copies of the Program or any portion of
+it, and copy and distribute such modifications under the terms of Paragraph
+1 above, provided that you also do the following:
+
+    a) cause the modified files to carry prominent notices stating that
+    you changed the files and the date of any change; and
+
+    b) cause the whole of any work that you distribute or publish, that
+    in whole or in part contains the Program or any part thereof, either
+    with or without modifications, to be licensed at no charge to all
+    third parties under the terms of this General Public License (except
+    that you may choose to grant warranty protection to some or all
+    third parties, at your option).
+
+    c) If the modified program normally reads commands interactively when
+    run, you must cause it, when started running for such interactive use
+    in the simplest and most usual way, to print or display an
+    announcement including an appropriate copyright notice and a notice
+    that there is no warranty (or else, saying that you provide a
+    warranty) and that users may redistribute the program under these
+    conditions, and telling the user how to view a copy of this General
+    Public License.
+
+    d) You may charge a fee for the physical act of transferring a
+    copy, and you may at your option offer warranty protection in
+    exchange for a fee.
+
+Mere aggregation of another independent work with the Program (or its
+derivative) on a volume of a storage or distribution medium does not bring
+the other work under the scope of these terms.
+\f
+  3. You may copy and distribute the Program (or a portion or derivative of
+it, under Paragraph 2) in object code or executable form under the terms of
+Paragraphs 1 and 2 above provided that you also do one of the following:
+
+    a) accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of
+    Paragraphs 1 and 2 above; or,
+
+    b) accompany it with a written offer, valid for at least three
+    years, to give any third party free (except for a nominal charge
+    for the cost of distribution) a complete machine-readable copy of the
+    corresponding source code, to be distributed under the terms of
+    Paragraphs 1 and 2 above; or,
+
+    c) accompany it with the information you received as to where the
+    corresponding source code may be obtained.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form alone.)
+
+Source code for a work means the preferred form of the work for making
+modifications to it.  For an executable file, complete source code means
+all the source code for all modules it contains; but, as a special
+exception, it need not include source code for modules which are standard
+libraries that accompany the operating system on which the executable
+file runs, or for standard header files or definitions files that
+accompany that operating system.
+
+  4. You may not copy, modify, sublicense, distribute or transfer the
+Program except as expressly provided under this General Public License.
+Any attempt otherwise to copy, modify, sublicense, distribute or transfer
+the Program is void, and will automatically terminate your rights to use
+the Program under this License.  However, parties who have received
+copies, or rights to use copies, from you under this General Public
+License will not have their licenses terminated so long as such parties
+remain in full compliance.
+
+  5. By copying, distributing or modifying the Program (or any work based
+on the Program) you indicate your acceptance of this license to do so,
+and all its terms and conditions.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the original
+licensor to copy, distribute or modify the Program subject to these
+terms and conditions.  You may not impose any further restrictions on the
+recipients' exercise of the rights granted herein.
+\f
+  7. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of the license which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+the license, you may choose any version ever published by the Free Software
+Foundation.
+
+  8. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+\f
+       Appendix: How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to humanity, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these
+terms.
+
+  To do so, attach the following notices to the program.  It is safest to
+attach them to the start of each source file to most effectively convey
+the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) 19yy  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 1, or (at your option)
+    any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) 19xx name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the
+appropriate parts of the General Public License.  Of course, the
+commands you use may be called something other than `show w' and `show
+c'; they could even be mouse-clicks or menu items--whatever suits your
+program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  program `Gnomovision' (a program to direct compilers to make passes
+  at assemblers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+That's all there is to it!
diff --git a/Makefile.PL b/Makefile.PL
new file mode 100644 (file)
index 0000000..5ac4e8e
--- /dev/null
@@ -0,0 +1,44 @@
+use ExtUtils::MakeMaker;
+use Config;
+
+WriteMakefile(
+    NAME       => "B",
+    VERSION    => "a2",
+    MAP_TARGET => "bperl",
+    OBJECT     => "B.o ccop.o byterun.o",
+    depend     => {
+       "B.o"           => "B.c ccop.h bytecode.h byterun.h",
+       "ccop.o"        => "ccop.c ccop.h"
+    },
+    clean      => {
+       FILES           => "bperl byteperl btest btest.c *.o B.c *~"
+    }
+);
+
+sub MY::post_constants {
+    "\nLIBS = $Config{libs}\n"
+}
+
+sub MY::top_targets {
+    my $self = shift;
+    my $targets = $self->MM::top_targets();
+    $targets =~ s/^(all ::.*)$/$1 byteperl/m;
+    return <<'EOT' . $targets;
+#
+# byterun.h, byterun.c and Asmdata.pm are auto-generated. If any of the
+# files are missing or if you change bytecode.pl (which is what generates
+# them all) then you can "make regen_headers" to regenerate them.
+#
+regen_headers:
+       $(PERL) bytecode.pl
+       $(MV) Asmdata.pm B
+#
+# byteperl is *not* a standard perl+XSUB executable. It's a special
+# program for running standalone bytecode executables. It isn't an XSUB
+# at the moment because a standlone Perl program needs to set up curpad
+# which is overwritten on exit from an XSUB.
+#
+byteperl: byteperl.o B.o ccop.o byterun.o
+       $(CC) -o byteperl byteperl.o B.o ccop.o byterun.o $(LDFLAGS) -L$(PERL_ARCHLIB)/CORE -lperl $(LIBS)
+EOT
+}
diff --git a/NOTES b/NOTES
new file mode 100644 (file)
index 0000000..52910d2
--- /dev/null
+++ b/NOTES
@@ -0,0 +1,155 @@
+C backend invocation
+       If there are any non-option arguments, they are taken to be
+       names of objects to be saved (probably doesn't work properly yet).
+       Without extra arguments, it saves the main program.
+       -ofilename      Output to filename instead of STDOUT
+       -v              Verbose (currently gives a few compilation statistics)
+       --              Force end of options
+       -uPackname      Force apparently unused subs from package Packname to
+                       be compiled. This allows programs to use eval "foo()"
+                       even when sub foo is never seen to be used at compile
+                       time. The down side is that any subs which really are
+                       never used also have code generated. This option is
+                       necessary, for example, if you have a signal handler
+                       foo which you initialise with $SIG{BAR} = "foo".
+                       A better fix, though, is just to change it to
+                       $SIG{BAR} = \&foo. You can have multiple -u options.
+       -D              Debug options (concat or separate flags like perl -D)
+               o       OPs, prints each OP as it's processed
+               c       COPs, prints COPs as processed (incl. file & line num)
+               A       prints AV information on saving
+               C       prints CV information on saving
+               M       prints MAGIC information on saving
+       -f              Force optimisations on or off one at a time.
+               cog     Copy-on-grow: PVs declared and initialised statically
+               no-cog  No copy-on-grow
+       -On             Optimisation level (n = 0, 1, 2, ...). -O means -O1.
+                       Currently, -O1 and higher set -fcog.
+
+Examples
+       perl -MO=C foo.pl > foo.c
+       perl cc_harness -o foo foo.c
+
+       perl -MO=C,-v,-DcA bar.pl > /dev/null
+
+CC backend invocation
+       If there are any non-option arguments, they are taken to be names of
+       subs to be saved. Without extra arguments, it saves the main program.
+       -ofilename      Output to filename instead of STDOUT
+       --              Force end of options
+       -uPackname      Force apparently unused subs from package Packname to
+                       be compiled. This allows programs to use eval "foo()"
+                       even when sub foo is never seen to be used at compile
+                       time. The down side is that any subs which really are
+                       never used also have code generated. This option is
+                       necessary, for example, if you have a signal handler
+                       foo which you initialise with $SIG{BAR} = "foo".
+                       A better fix, though, is just to change it to
+                       $SIG{BAR} = \&foo. You can have multiple -u options.
+       -D              Debug options (concat or separate flags like perl -D)
+               r       Writes debugging output to STDERR just as it's about
+                       to write to the program's runtime (otherwise writes
+                       debugging info as comments in its C output).
+               O       Outputs each OP as it's compiled
+               s       Outputs the contents of the shadow stack at each OP
+               p       Outputs the contents of the shadow pad of lexicals as
+                       it's loaded for each sub or the main program.
+               q       Outputs the name of each fake PP function in the queue
+                       as it's about to processes.
+               l       Output the filename and line number of each original
+                       line of Perl code as it's processed (pp_nextstate).
+               t       Outputs timing information of compilation stages
+       -f              Force optimisations on or off one at a time.
+               [
+               cog     Copy-on-grow: PVs declared and initialised statically
+               no-cog  No copy-on-grow
+               These two not in CC yet.
+               ]
+               freetmps-each-bblock    Delays FREETMPS from the end of each
+                                       statement to the end of the each basic
+                                       block.
+               freetmps-each-loop      Delays FREETMPS from the end of each
+                                       statement to the end of the group of
+                                       basic blocks forming a loop. At most
+                                       one of the freetmps-each-* options can
+                                       be used.
+               omit-taint              Omits generating code for handling
+                                       perl's tainting mechanism.
+       -On             Optimisation level (n = 0, 1, 2, ...). -O means -O1.
+                       Currently, -O1 sets -ffreetmps-each-bblock and -O2
+                       sets -ffreetmps-each-loop.
+
+Example
+       perl -MO=CC,-O2,-ofoo.c foo.pl
+       perl cc_harness -o foo foo.c
+
+
+Bytecode backend invocation
+
+       If there are any non-option arguments, they are taken to be
+       names of objects to be saved (probably doesn't work properly yet).
+       Without extra arguments, it saves the main program.
+       -ofilename      Output to filename instead of STDOUT.
+       --              Force end of options.
+       -f              Force optimisations on or off one at a time.
+                       Each can be preceded by no- to turn the option off.
+               compress-nullops
+                       Only fills in the necessary fields of ops which have
+                       been optimised away by perl's internal compiler.
+               omit-sequence-numbers
+                       Leaves out code to fill in the op_seq field of all ops
+                       which is only used by perl's internal compiler.
+               bypass-nullops
+                       If op->op_next ever points to a NULLOP, replaces the
+                       op_next field with the first non-NULLOP in the path
+                       of execution.
+               strip-syntax-tree
+                       Leaves out code to fill in the pointers which link the
+                       internal syntax tree together. They're not needed at
+                       run-time but leaving them out will make it impossible
+                       to recompile or disassemble the resulting program.
+                       It will also stop "goto label" statements from working.
+       -On             Optimisation level (n = 0, 1, 2, ...). -O means -O1.
+                       -O1 sets -fcompress-nullops -fomit-sequence numbers.
+                       -O6 adds -fstrip-syntax-tree.
+       -D              Debug options (concat or separate flags like perl -D)
+               o       OPs, prints each OP as it's processed.
+               a       tells the assembler to include source assembler lines
+                       in its output as bytecode comments.
+               C       prints each CV taken from the final symbol tree walk.
+       -S              Output assembler source rather than piping it
+                       through the assembler and outputting bytecode.
+       -m              Compile as a module rather than a standalone program.
+                       Currently this just means that the bytecodes for
+                       initialising main_start, main_root and curpad are
+                       omitted.
+
+Example
+       perl -MO=Bytecode,-O6,-o,foo.plc foo.pl
+
+       perl -MO=Bytecode,-S foo.pl > foo.S
+       assemble foo.S > foo.plc
+       byteperl foo.plc
+
+       perl -MO=Bytecode,-m,-oFoo.pmc Foo.pm
+
+Backends for debugging
+       perl -MO=Terse,exec foo.pl
+       perl -MO=Debug bar.pl
+
+O module
+       Used with "perl -MO=Backend,foo,bar prog.pl" to invoke the backend
+       B::Backend with options foo and bar. O invokes the sub
+       B::Backend::compile() with arguments foo and bar at BEGIN time.
+       That compile() sub must do any inital argument processing replied.
+       If unsuccessful, it should return a string which O arranges to be
+       printed as an error message followed by a clean error exit. In the
+       normal case where any option processing in compile() is successful,
+       it should return a sub ref (usually a closure) to perform the
+       actual compilation. When O regains control, it ensures that the
+       "-c" option is forced (so that the program being compiled doesn't
+       end up running) and registers an END block to call back the sub ref
+       returned from the backend's compile(). Perl then continues by
+       parsing prog.pl (just as it would with "perl -c prog.pl") and after
+       doing so, assuming there are no parse-time errors, the END block
+       of O gets called and the actual backend compilation happens. Phew.
diff --git a/O.pm b/O.pm
new file mode 100644 (file)
index 0000000..cc9f7f9
--- /dev/null
+++ b/O.pm
@@ -0,0 +1,23 @@
+package O;
+use B qw(minus_c);
+use Carp;    
+
+my $compilesub;
+
+sub import {
+    my ($class, $backend, @options) = @_;
+    eval "use B::$backend ()";
+    if ($@) {
+       croak "use of backend $backend failed: $@";
+    }
+    $compilesub = &{"B::${backend}::compile"}(@options);
+    if (ref($compilesub) eq "CODE") {
+       minus_c;
+       eval 'END { &$compilesub() }';
+    } else {
+       die $compilesub;
+    }
+}
+
+1;
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..b1bffce
--- /dev/null
+++ b/README
@@ -0,0 +1,317 @@
+                 Perl Compiler Kit, Version alpha3
+
+                Copyright (c) 1996, Malcolm Beattie
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of either:
+
+       a) the GNU General Public License as published by the Free
+       Software Foundation; either version 1, or (at your option) any
+       later version, or
+
+       b) the "Artistic License" which comes with this kit.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See either
+    the GNU General Public License or the Artistic License for more details.
+
+    You should have received a copy of the Artistic License with this kit,
+    in the file named "Artistic".  If not, you can get one from the Perl
+    distribution. You should also have received a copy of the GNU General
+    Public License, in the file named "Copying". If not, you can get one
+    from the Perl distribution or else write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+CHANGES
+
+New since alpha2
+    CC backend now supports ".." and s//e.
+    Xref backend generates cross-reference reports
+    Cleanups to fix benign but irritating "-w" warnings
+    Minor cxstack fix
+New since alpha1
+    Working CC backend
+    Shared globs and pre-initialised hash support
+    Some XSUB support
+    Assorted bug fixes
+
+INSTALLATION
+
+(1) You need perl5.002 or perl5.003.
+
+(2) If you want to compile and run programs with the C or CC backends
+which undefine (or redefine) subroutines, then you need to apply a
+one-line patch to perl itself. One or two of the programs in perl's
+own test suite do this. The patch is in file op.patch. It prevents
+perl from calling free() on OPs with the magic sequence number (U16)-1.
+The compiler declares all OPs as static structures and uses that magic
+sequence number.
+
+(3) Type
+    perl Makefile.PL
+to write a personalised Makefile for your system. If you want the
+bytecode modules to support reading bytecode from strings (instead of
+just from files) then add the option
+    -DINDIRECT_BGET_MACROS
+into the middle of the definition of the CCCMD macro in the Makefile.
+Your C compiler may need to be able to cope with Standard C for this.
+I haven't tested this option yet with an old pre-Standard compiler.
+
+(4) If your platform supports dynamic loading then just type
+    make
+and you can then use
+    perl -Iblib/arch -MO=foo bar baz
+to use the compiler modules (see later for details).
+If you need/want instead to make a statically linked perl which
+contains the appropriate modules, then type
+    make bperl
+    make byteperl
+and you can then use
+    ./bperl -MO=foo bar baz
+to use the compiler modules.    
+In both cases, the byteperl executable is required for running standalone
+bytecode programs. It is *not* a standard perl+XSUB perl executable.
+
+USAGE
+
+As of the alpha3 release, the Bytecode, C and CC backends are now all
+functional enough to compile almost the whole of the main perl test
+suite. In the case of the CC backend, any failures are all due to
+differences and/or known bugs documented below. See the file TESTS.
+In the following examples, you'll need to replace "perl" by
+    perl -Iblib/arch
+if you have built the extensions for a dynamic loading platform but
+haven't installed the extensions completely. You'll need to replace
+"perl" by
+    ./bperl
+if you have built the extensions into a statically linked perl binary.
+
+(1) To compile perl program foo.pl with the C backend, do
+    perl -MO=C,-ofoo.c foo.pl
+Then use the cc_harness perl program to compile the resulting C source:
+    perl cc_harness -O2 -o foo foo.c
+
+If you are using a non-ANSI pre-Standard C compiler that can't handle
+pre-declaring static arrays, then add -DBROKEN_STATIC_REDECL to the
+options you use:
+    perl cc_harness -O2 -o foo -DBROKEN_STATIC_REDECL foo.c
+If you are using a non-ANSI pre-Standard C compiler that can't handle
+static initialisation of structures with union members then add
+-DBROKEN_UNION_INIT to the options you use. If you want command line
+arguments passed to your executable to be interpreted by perl (e.g. -Dx)
+then compile foo.c with -DALLOW_PERL_OPTIONS. Otherwise, all command line
+arguments passed to foo will appear directly in @ARGV.  The resulting
+executable foo is the compiled version of foo.pl. See the file NOTES for
+extra options you can pass to -MO=C.
+
+There are some constraints on the contents on foo.pl if you want to be
+able to compile it successfully. Some problems can be fixed fairly easily
+by altering foo.pl; some problems with the compiler are known to be
+straightforward to solve and I'll do so soon. The file Todo lists a
+number of known problems. See the XSUB section lower down for information
+about compiling programs which use XSUBs.
+
+(2) To compile foo.pl with the CC backend (which generates actual
+optimised C code for the execution path of your perl program), use
+    perl -MO=CC,-ofoo.c foo.pl
+
+and proceed just as with the C backend. You should almost certainly
+use an option such as -O2 with the subsequent cc_harness invocation
+so that your C compiler uses optimisation. The C code generated by
+the Perl compiler's CC backend looks ugly to humans but is easily
+optimised by C compilers.
+
+To make the most of this compiler backend, you need to tell the
+compiler when you're using int or double variables so that it can
+optimise appropriately (although this part of the compiler is the most
+buggy). You currently do that by naming lexical variables ending in
+"_i" for ints, "_d" for doubles, "_ir" for int "register" variables or
+"_dr" for double "register" variables. Here "register" is a promise
+that you won't pass a reference to the variable into a sub which then
+modifies the variable. The compiler ought to catch attempts to use
+"\$i" just as C compilers catch attempts to do "&i" for a register int
+i but it doesn't at the moment. Bugs in the CC backend may make your
+program fail in mysterious ways and give wrong answers rather than just
+crash in boring ways. But, hey, this is an alpha release so you knew
+that anyway. See the XSUB section lower down for information about
+compiling programs which use XSUBs.
+
+If your program uses classes which define methods (or other subs which
+are not exported and not apparently used until runtime) then you'll
+need to use -u compile-time options (see the NOTES file) to force the
+subs to be compiled. Future releases will probably default the other
+way, do more auto-detection and provide more fine-grained control.
+
+Since compiled executables need linking with libperl, you may want
+to turn libperl.a into a shared library if your platform supports
+it. For example, with Digital UNIX, do something like
+    ld -shared -o libperl.so -all libperl.a -none -lc
+and with Linux/ELF, rebuild the perl .c files with -fPIC (and I
+also suggest -fomit-frame-pointer for Linux on Intel architetcures),
+do "Make libperl.a" and then do
+    gcc -shared -Wl,-soname,libperl.so.5 -o libperl.so.5.3 `ar t libperl.a`
+and then
+    # cp libperl.so.5.3 /usr/lib
+    # cd /usr/lib
+    # ln -s libperl.so.5.3 libperl.so.5
+    # ln -s libperl.so.5 libperl.so
+    # ldconfig
+When you compile perl executables with cc_harness, append -L/usr/lib
+otherwise the -L for the perl source directory will override it. For
+example,
+    perl -Iblib/arch -MO=CC,-O2,-ofoo3.c foo3.bench
+    perl cc_harness -o foo3 -O2 foo3.c -L/usr/lib
+    ls -l foo3
+    -rwxr-xr-x   1 mbeattie xzdg        11218 Jul  1 15:28 foo3
+You'll probably also want to link your main perl executable against
+libperl.so; it's nice having an 11K perl executable.
+
+(3) To compile foo.pl into bytecode do
+    perl -MO=Bytecode,-ofoo foo.pl
+To run the resulting bytecode file foo as a standalone program, you
+use the program byteperl which should have been built along with the
+extensions.
+    ./byteperl foo
+Any extra arguments are passed in as @ARGV; they are not interpreted
+as perl options. If you want to load chunks of bytecode into an already
+running perl program then use the -m option and investigate the
+byteload_fh and byteload_string functions exported by the B module.
+See the NOTES file for details of these and other options (including
+optimisation options and ways of getting at the intermediate "assembler"
+code that the Bytecode backend uses).
+
+(3) There are little Bourne shell scripts and perl programs to aid with
+some common operations: assemble, disassemble, run_bytecode_test,
+run_test, cc_harness, test_harness, test_harness_bytecode.
+
+(4) Walk the op tree in execution order printing terse info about each op
+    perl -MO=Terse,exec foo.pl
+
+(5) Walk the op tree in syntax order printing lengthier debug info about
+each op. You can also append ",exec" to walk in execution order, but the
+formatting is designed to look nice with Terse rather than Debug.
+    perl -MO=Debug foo.pl
+
+(6) Produce a cross-reference report of the line numbers at which all
+variables, subs and formats are defined and used.
+    perl -MO=Xref foo.pl
+
+XSUBS
+
+The C and CC backends can successfully compile some perl programs which
+make use of XSUB extensions. [I'll add more detail to this section in a
+later release.] As a prerequisite, such extensions must not need to do
+anything in their BOOT: section which needs to be done at runtime rather
+than compile time. Normally, the only code in the boot_Foo() function is
+a list of newXS() calls which xsubpp puts there and the compiler handles
+saving those XS subs itself. For each XSUB used, the C and CC compiler
+will generate an initialiser in their C output which refers to the name
+of the relevant C function (XS_Foo_somesub). What is not yet automated
+is the necessary commands and cc command-line options (e.g. via
+"perl cc_harness") which link against the extension libraries. For now,
+you need the XSUB extension to have installed files in the right format
+for using as C libraries (e.g. Foo.a or Foo.so). As the Foo.so files (or
+your platform's version) aren't suitable for linking against, you will
+have to reget the extension source and rebuild it as a static extension
+to force the generation of a suitable Foo.a file. Then you need to make
+a symlink (or copy or rename) of that file into a libFoo.a suitable for
+cc linking. Then add the appropriate -L and -l options to your
+"perl cc_harness" command line to find and link against those libraries.
+You may also need to fix up some platform-dependent environment variable
+to ensure that linked-against .so files are found at runtime too.
+
+DIFFERENCES
+
+The result of running a compiled Perl program can sometimes be different
+from running the same program with standard perl. Think of the compiler
+as having a slightly different implementation of the language Perl.
+Unfortunately, since Perl has had a single implementation until now,
+there are no formal standards or documents defining what behaviour is
+guaranteed of Perl the language and what just "happens to work".
+Some of the differences below are almost impossible to change because of
+the way the compiler works. Others can be changed to produce "standard"
+perl behaviour if it's deemed proper and the resulting performance hit
+is accepted. I'll use "standard perl" to mean the result of running a
+Perl program using the perl executable from the perl distribution.
+I'll use "compiled Perl program" to mean running an executable produced
+by this compiler kit ("the compiler") with the CC backend.
+
+Loops
+    Standard perl calculates the target of "next", "last", and "redo"
+    at run-time. The compiler calculates the targets at compile-time.
+    For example, the program
+
+        sub skip_on_odd { next NUMBER if $_[0] % 2 }
+        NUMBER: for ($i = 0; $i < 5; $i++) {
+            skip_on_odd($i);
+            print $i;
+        }
+
+    produces the output
+        024
+    with standard perl but gives a compile-time error with the compiler.
+
+Context of ".."
+    The context (scalar or array) of the ".." operator determines whether
+    it behaves as a range or a flip/flop. Standard perl delays until
+    runtime the decision of which context it is in but the compiler needs
+    to know the context at compile-time. For example,
+       @a = (4,6,1,0,0,1);
+       sub range { (shift @a)..(shift @a) }
+       print range();
+       while (@a) { print scalar(range()) }
+    generates the output
+        456123E0
+    with standard Perl but gives a compile-time error with compiled Perl.
+
+Arithmetic
+    Compiled Perl programs use native C arithemtic much more frequently
+    than standard perl. Operations on large numbers or on boundary
+    cases may produce different behaviour.
+
+Deprecated features
+    Features of standard perl such as $[ which have been deprecated
+    in standard perl since version 5 was released have not been
+    implemented in the compiler.
+
+Others
+    I'll add to this list as I remember what they are.
+
+BUGS
+
+Here are some things which may cause the compiler problems.
+
+The following render the compiler useless (without serious hacking):
+* Use of the DATA filehandle (via __END__ or __DATA__ tokens)
+* Operator overloading with %OVERLOAD
+* The (deprecated) magic array-offset variable $[ does not work
+* The following operators are not yet implemented for CC
+    goto
+    sort with a non-default comparison (i.e. a named sub or inline block)
+* You can't use "last" to exit from a non-loop block.
+
+The following may give significant problems:
+* BEGIN blocks containing complex initialisation code
+* Code which is only ever referred to at runtime (e.g. via eval "..." or
+  via method calls): see the -u option for the C and CC backends.
+* Run-time lookups of lexical variables in "outside" closures
+
+The following may cause problems (not thoroughly tested):
+* Dependencies on whether values of some "magic" Perl variables are
+  determined at compile-time or runtime.
+* For the C and CC backends: compile-time strings which are longer than
+  your C compiler can cope with in a single line or definition.
+* Reliance on intimate details of global destruction
+* For the Bytecode backend: high -On optimisation numbers with code
+  that has complex flow of control.
+* Any "-w" option in the first line of your perl program is seen and
+  acted on by perl itself before the compiler starts. The compiler
+  itself then runs with warnings turned on. This may cause perl to
+  print out warnings about the compiler itself since I haven't tested
+  it thoroughly with warnings turned on.
+
+There is a terser but more complete list in the Todo file.
+
+Malcolm Beattie
+2 September 1996
diff --git a/TESTS b/TESTS
new file mode 100644 (file)
index 0000000..bf5d20d
--- /dev/null
+++ b/TESTS
@@ -0,0 +1,78 @@
+Test results from compiling t/*/*.t
+               C               Bytecode        CC
+
+base/cond.t    OK              OK              OK
+base/if.t      OK              OK              OK
+base/lex.t     OK              OK              OK
+base/pat.t     OK              OK              OK
+base/term.t    OK              OK              OK
+cmd/elsif.t    OK              OK              OK
+cmd/for.t      OK              OK              OK
+cmd/mod.t      OK              OK              OK
+cmd/subval.t   OK              OK              1..34, not ok 27,28 (simply
+                                               because filename changes).
+cmd/switch.t   OK              OK              OK
+cmd/while.t    OK              OK              OK
+io/argv.t      OK              OK              OK
+io/dup.t       OK              OK              OK
+io/fs.t                OK              OK              OK
+io/inplace.t   OK              OK              OK
+io/pipe.t      OK with -umain  OK              OK with -umain
+io/print.t     OK              OK              OK
+io/tell.t      OK              OK              OK
+op/append.t    OK              OK              OK
+op/array.t     OK              OK              1..36, not ok 7,10 (no $[)
+op/auto.t      OK              OK              OK
+op/chop.t      OK              OK              OK
+op/cond.t      OK              OK              OK
+op/delete.t    OK              OK              OK
+op/do.t                OK              OK              OK
+op/each.t      OK              OK              OK
+op/eval.t      OK              OK              OK
+op/exec.t      OK              OK              OK
+op/exp.t       OK              OK              OK
+op/flip.t      OK              OK              OK
+op/fork.t      OK              OK              OK
+op/glob.t      OK              OK              OK
+op/goto.t      OK              OK              1..9, Can't find label label1.
+op/groups.t    OK (s/ucb/bin/ under Linux)     OK 1..0 for now.
+op/index.t     OK              OK              OK
+op/int.t       OK              OK              OK
+op/join.t      OK              OK              OK
+op/list.t      OK              OK              OK
+op/local.t     OK              OK              OK
+op/magic.t     OK              OK              OK with -umain
+op/misc.t      no DATA filehandle so succeeds trivially with 1..0
+op/mkdir.t     OK              OK              OK
+op/my.t                OK              OK              OK
+op/oct.t       OK              OK              OK (C large const warnings)
+op/ord.t       OK              OK              OK
+op/overload.t  Mostly not ok   Mostly not ok   C errors.
+op/pack.t      OK              OK              OK
+op/pat.t       OK              OK              OK
+op/push.t      OK              OK              OK
+op/quotemeta.t OK              OK              OK
+op/rand.t      OK              OK              OK
+op/range.t     OK              OK              OK
+op/read.t      OK              OK              OK
+op/readdir.t   OK              OK              OK
+op/ref.t       omits "ok 40" (lex destruction) OK (Bytecode)
+                                               CC: need -u for OBJ,BASEOBJ,
+                                               MYHASH,UNIVERSAL,WHATEVER,main
+                                               FINALE. 1..41, ok1-33,36-38,
+                                               then ok 41, ok 39.DESTROY probs
+op/regexp.t    OK              OK              OK (trivially all eval'd)
+op/repeat.t    OK              OK              OK
+op/sleep.t     OK              OK              OK
+op/sort.t      OK              OK              1..10, ok 1, Out of memory!
+op/split.t     OK              OK              OK
+op/sprintf.t   OK              OK              OK
+op/stat.t      OK              OK              OK
+op/study.t     OK              OK              OK
+op/subst.t     OK              OK              OK
+op/substr.t    OK              OK              ok1-22 except 7-9,11 (all $[)
+op/time.t      OK              OK              OK
+op/undef.t     OK              OK              OK
+op/unshift.t   OK              OK              OK
+op/vec.t       OK              OK              OK
+op/write.t     not ok 3 (no CvOUTSIDE lex from runtime eval). CC: 1..3, hang
diff --git a/TESTS.alpha2 b/TESTS.alpha2
new file mode 100644 (file)
index 0000000..b7ecfc1
--- /dev/null
@@ -0,0 +1,78 @@
+Test results from compiling t/*/*.t
+               C               Bytecode        CC
+
+base/cond.t    OK              OK              OK
+base/if.t      OK              OK              OK
+base/lex.t     OK              OK              OK
+base/pat.t     OK              OK              OK
+base/term.t    OK              OK              OK
+cmd/elsif.t    OK              OK              OK
+cmd/for.t      OK              OK              C label used but not defined
+cmd/mod.t      OK              OK              OK
+cmd/subval.t   OK              OK              1..34, not ok 27,28 (simply
+                                               because filename changes).
+cmd/switch.t   OK              OK              OK
+cmd/while.t    OK              OK              OK
+io/argv.t      OK              OK              OK
+io/dup.t       OK              OK              OK
+io/fs.t                OK              OK              OK
+io/inplace.t   OK              OK              OK
+io/pipe.t      OK with -umain  OK              OK with -umain
+io/print.t     OK              OK              OK
+io/tell.t      OK              OK              OK
+op/append.t    OK              OK              OK
+op/array.t     OK              OK              1..36, not ok 7,10 (no $[)
+op/auto.t      OK              OK              OK
+op/chop.t      OK              OK              OK
+op/cond.t      OK              OK              OK
+op/delete.t    OK              OK              OK
+op/do.t                OK              OK              OK
+op/each.t      OK              OK              OK
+op/eval.t      OK              OK              OK
+op/exec.t      OK              OK              OK
+op/exp.t       OK              OK              OK
+op/flip.t      OK              OK              1..8, not ok 6, 7, nothing else
+op/fork.t      OK              OK              OK
+op/glob.t      OK              OK              OK
+op/goto.t      OK              OK              1..9, Can't find label label1.
+op/groups.t    OK (s/ucb/bin/ under Linux)     OK 1..0 for now.
+op/index.t     OK              OK              OK
+op/int.t       OK              OK              OK
+op/join.t      OK              OK              OK
+op/list.t      OK              OK              OK
+op/local.t     OK              OK              OK
+op/magic.t     OK              OK              OK with -umain
+op/misc.t      no DATA filehandle so succeeds trivially with 1..0
+op/mkdir.t     OK              OK              OK
+op/my.t                OK              OK              OK
+op/oct.t       OK              OK              OK (C large const warnings)
+op/ord.t       OK              OK              OK
+op/overload.t  Mostly not ok   Mostly not ok   C errors.
+op/pack.t      OK              OK              OK
+op/pat.t       OK              OK              OK
+op/push.t      OK              OK              OK
+op/quotemeta.t OK              OK              OK
+op/rand.t      OK              OK              OK
+op/range.t     OK              OK              ok 1,3-8. not ok 2 (no pp_flip)
+op/read.t      OK              OK              OK
+op/readdir.t   OK              OK              OK
+op/ref.t       omits "ok 40" (lex destruction) OK (Bytecode)
+                                               CC: need -u for OBJ,BASEOBJ,
+                                               MYHASH,UNIVERSAL,WHATEVER,main
+                                               FINALE. 1..41, ok1-33,36-38,
+                                               then ok 41, ok 39.DESTROY probs
+op/regexp.t    OK              OK              OK (trivially all eval'd)
+op/repeat.t    OK              OK              OK
+op/sleep.t     OK              OK              OK
+op/sort.t      OK              OK              1..10, ok 1, Out of memory!
+op/split.t     OK              OK              OK
+op/sprintf.t   OK              OK              OK
+op/stat.t      OK              OK              OK
+op/study.t     OK              OK              OK
+op/subst.t     OK              OK              C errors (s//e broken).
+op/substr.t    OK              OK              ok1-22 except 7-9,11 (all $[)
+op/time.t      OK              OK              OK
+op/undef.t     OK              OK              OK
+op/unshift.t   OK              OK              OK
+op/vec.t       OK              OK              OK
+op/write.t     not ok 3 (no CvOUTSIDE lex from runtime eval). CC: 1..3, hang
diff --git a/Todo b/Todo
new file mode 100644 (file)
index 0000000..495be2e
--- /dev/null
+++ b/Todo
@@ -0,0 +1,37 @@
+* Fixes
+
+CC backend: goto, sort with non-default comparison. last for non-loop blocks.
+Version checking
+improve XSUB handling (both static and dynamic)
+sv_magic can do SvREFCNT_inc(obj) which messes up precalculated refcounts
+allocation of XPV[INAHC]V structures needs fixing: Perl tries to free
+them whereas the compiler expects them to be linked to a xpv[inahc]v_root
+list the same as X[IPR]V structures.
+ref counts
+perl_parse replacement
+fix cstring for long strings
+compile-time initialisation of AvARRAYs
+signed/unsigned problems with NV (and IV?) initialisation and elsewhere?
+CvOUTSIDE for ordinary subs
+DATA filehandle for standalone Bytecode program (easy)
+DATA filehandle for multiple bytecode-compiled modules (harder)
+DATA filehandle for C-compiled program (yet harder)
+
+* Features
+
+type checking
+compile time v. runtime initialisation
+save PMOPs in compiled form
+selection of what to dump
+options for cutting out line info etc.
+comment output
+shared constants
+module dependencies
+
+* Optimisations
+collapse LISTOPs to UNOPs or BASEOPs
+compile-time qw(), constant subs
+global analysis of variables, type hints etc.
+demand-loaded bytecode (leader of each basic block replaced by an op
+which loads in bytecode for its block)
+fast sub calls for CC backend
diff --git a/assemble b/assemble
new file mode 100755 (executable)
index 0000000..491210e
--- /dev/null
+++ b/assemble
@@ -0,0 +1,24 @@
+#!./bperl
+use B::Assembler qw(assemble_fh);
+use FileHandle;
+
+my ($filename, $fh);
+
+if ($ARGV[0] eq "-d") {
+    B::Assembler::debug(1);
+    shift;
+}
+
+if (@ARGV == 0) {
+    $fh = \*STDIN;
+    $filename = "-";
+} elsif (@ARGV == 1) {
+    $filename = $ARGV[0];
+    $fh = new FileHandle "<$filename";
+} else {
+    die "Usage: assemble [filename]\n";
+}
+
+$SIG{__WARN__} = sub { warn "$filename:@_" };
+$SIG{__DIE__} = sub { die "$filename: @_" };
+assemble_fh($fh, sub { print @_ });
diff --git a/bytecode.h b/bytecode.h
new file mode 100644 (file)
index 0000000..2cdc028
--- /dev/null
@@ -0,0 +1,163 @@
+typedef char *pvcontents;
+typedef char *strconst;
+typedef U32 PV;
+typedef char *op_tr_array;
+typedef int comment;
+typedef SV *svindex;
+typedef OP *opindex;
+typedef IV IV64;
+
+EXT int iv_overflows INIT(0);
+void *bset_obj_store _((void *, I32));
+void freadpv _((U32, void *));
+
+EXT SV *sv;
+EXT OP *op;
+EXT XPV pv;
+
+EXT void **obj_list;
+EXT I32 obj_list_fill INIT(-1);
+
+#ifdef INDIRECT_BGET_MACROS
+#define FREAD(argp, len, nelem) bs.fread((char*)(argp),(len),(nelem),bs.data)
+#define FGETC() bs.fgetc(bs.data)
+#else
+#define FREAD(argp, len, nelem) fread((argp), (len), (nelem), fp)
+#define FGETC() getc(fp)
+#endif /* INDIRECT_BGET_MACROS */
+
+#define BGET_U32(arg)  FREAD(&arg, sizeof(U32), 1); arg = ntohl((U32)arg)
+#define BGET_I32(arg)  FREAD(&arg, sizeof(I32), 1); arg = (I32)ntohl((U32)arg)
+#define BGET_U16(arg)  FREAD(&arg, sizeof(U16), 1); arg = ntohs((U16)arg)
+#define BGET_U8(arg)   arg = FGETC()
+
+#if INDIRECT_BGET_MACROS
+#define BGET_PV(arg)   do {            \
+       BGET_U32(arg);                  \
+       if (arg)                        \
+           bs.freadpv(arg, bs.data);   \
+       else {                          \
+           pv.xpv_pv = 0;              \
+           pv.xpv_len = 0;             \
+           pv.xpv_cur = 0;             \
+       }                               \
+    } while (0)
+#else
+#define BGET_PV(arg)   do {                    \
+       BGET_U32(arg);                          \
+       if (arg) {                              \
+           New(666, pv.xpv_pv, arg, char);     \
+           fread(pv.xpv_pv, 1, arg, fp);       \
+           pv.xpv_len = arg;                   \
+           pv.xpv_cur = arg - 1;               \
+       } else {                                \
+           pv.xpv_pv = 0;                      \
+           pv.xpv_len = 0;                     \
+           pv.xpv_cur = 0;                     \
+       }                                       \
+    } while (0)
+#endif /* INDIRECT_BGET_MACROS */
+
+#define BGET_comment(arg) \
+       do { arg = FGETC(); } while (arg != '\n' && arg != EOF)
+
+/*
+ * In the following, sizeof(IV)*4 is just a way of encoding 32 on 64-bit-IV
+ * machines such that 32-bit machine compilers don't whine about the shift
+ * count being too high even though the code is never reached there.
+ */
+#define BGET_IV64(arg) do {                            \
+       U32 hi, lo;                                     \
+       BGET_U32(hi);                                   \
+       BGET_U32(lo);                                   \
+       if (sizeof(IV) == 8)                            \
+           arg = (IV) (hi << (sizeof(IV)*4) | lo);     \
+       else if (((I32)hi == -1 && (I32)lo < 0)         \
+                || ((I32)hi == 0 && (I32)lo >= 0)) {   \
+           arg = (I32)lo;                              \
+       }                                               \
+       else {                                          \
+           iv_overflows++;                             \
+           arg = 0;                                    \
+       }                                               \
+    } while (0)
+
+#define BGET_op_tr_array(arg) do {     \
+       unsigned short *ary;            \
+       int i;                          \
+       New(666, ary, 256, unsigned short); \
+       FREAD(ary, 256, 2);             \
+       for (i = 0; i < 256; i++)       \
+           ary[i] = ntohs(ary[i]);     \
+       arg = (char *) ary;             \
+    } while (0)
+
+#define BGET_pvcontents(arg)   arg = pv.xpv_pv
+#define BGET_strconst(arg)     do {    \
+       for (arg = tokenbuf; (*arg = FGETC()); arg++) /* nothing */;    \
+       arg = tokenbuf;                 \
+    } while (0)
+
+#define BGET_double(arg)       do {    \
+       char *str;                      \
+       BGET_strconst(str);             \
+       arg = atof(str);                \
+    } while (0)
+
+#define BGET_objindex(arg) do {        \
+       U32 ix;                 \
+       BGET_U32(ix);           \
+       arg = obj_list[ix];     \
+    } while (0)
+
+#define BSET_ldspecsv(sv, arg) sv = specialsv_list[arg]
+                                   
+#define BSET_sv_refcnt_add(svrefcnt, arg)      svrefcnt += arg
+#define BSET_gp_refcnt_add(gprefcnt, arg)      gprefcnt += arg
+#define BSET_gp_share(sv, arg) do {    \
+       gp_free((GV*)sv);               \
+       GvGP(sv) = GvGP(arg);           \
+    } while (0)
+
+#define BSET_gv_fetchpv(sv, arg)       sv = (SV*)gv_fetchpv(arg, TRUE, SVt_PV)
+#define BSET_gv_stashpv(sv, arg)       sv = (SV*)gv_stashpv(arg, TRUE)
+#define BSET_sv_magic(sv, arg)         sv_magic(sv, Nullsv, arg, 0, 0)
+#define BSET_mg_pv(mg, arg)    mg->mg_ptr = arg; mg->mg_len = pv.xpv_cur
+#define BSET_sv_upgrade(sv, arg)       (void)SvUPGRADE(sv, arg)
+#define BSET_xpv(sv)   do {    \
+       SvPV_set(sv, pv.xpv_pv);        \
+       SvCUR_set(sv, pv.xpv_cur);      \
+       SvLEN_set(sv, pv.xpv_len);      \
+    } while (0)
+#define BSET_av_extend(sv, arg)        av_extend((AV*)sv, arg)
+
+#define BSET_av_push(sv, arg)  av_push((AV*)sv, arg)
+#define BSET_hv_store(sv, arg) \
+       hv_store((HV*)sv, pv.xpv_pv, pv.xpv_cur, arg, 0)
+#define BSET_pv_free(pv)       Safefree(pv.xpv_pv)
+#define BSET_pregcomp(op, arg) \
+       cPMOP->op_pmregexp = arg ? pregcomp(arg, arg + pv.xpv_cur, cPMOP) : 0
+#define BSET_newsv(sv, arg)    sv = NEWSV(666,0); SvUPGRADE(sv, arg)
+#define BSET_newop(op, arg)    op = (OP*)safemalloc(optype_size[arg])
+#define BSET_newopn(op, arg)   do {    \
+       OP *oldop = op;                 \
+       BSET_newop(op, arg);            \
+       oldop->op_next = op;            \
+    } while (0)
+
+#define BSET_ret(foo) return
+
+/*
+ * Kludge special-case workaround for OP_MAPSTART
+ * which needs the ppaddr for OP_GREPSTART. Blech.
+ */
+#define BSET_op_type(op, arg)  do {    \
+       op->op_type = arg;              \
+       op->op_ppaddr = (arg != OP_MAPSTART) ? ppaddr[arg] : pp_grepstart; \
+    } while (0)
+#define BSET_op_ppaddr(op, arg) croak("op_ppaddr not yet implemented")
+#define BSET_curpad(pad, arg) pad = AvARRAY(arg)
+
+#define BSET_OBJ_STORE(obj, ix)                \
+       (I32)ix > obj_list_fill ?       \
+       bset_obj_store(obj, (I32)ix) : (obj_list[ix] = obj)
diff --git a/bytecode.pl b/bytecode.pl
new file mode 100644 (file)
index 0000000..359110d
--- /dev/null
@@ -0,0 +1,370 @@
+use strict;
+my %alias_to = (
+    U32 => [qw(PADOFFSET STRLEN)],
+    I32 => [qw(SSize_t long)],
+    U16 => [qw(OPCODE line_t short)],
+    U8 => [qw(char)],
+    objindex => [qw(svindex opindex)]          
+);
+
+my @optype= qw(OP UNOP BINOP LOGOP CONDOP LISTOP PMOP SVOP GVOP PVOP LOOP COP);
+
+# Nullsv *must* come first in the following so that the condition
+# ($$sv == 0) can continue to be used to test (sv == Nullsv).
+my @specialsv = qw(Nullsv &sv_undef &sv_yes &sv_no);
+
+my (%alias_from, $from, $tos);
+while (($from, $tos) = each %alias_to) {
+    map { $alias_from{$_} = $from } @$tos;
+}
+
+my $c_header = <<'EOT';
+/*
+ *      Copyright (c) 1996 Malcolm Beattie
+ *
+ *      You may distribute under the terms of either the GNU General Public
+ *      License or the Artistic License, as specified in the README file.
+ *
+ */
+/*
+ * This file is autogenerated from bytecode.pl. Changes made here will be lost.
+ */
+EOT
+
+my $perl_header;
+($perl_header = $c_header) =~ s{[/ ]?\*/?}{#}g;
+
+if (-f "byterun.c") {
+    rename("byterun.c", "byterun.c.old");
+}
+if (-f "byterun.h") {
+    rename("byterun.h", "byterun.h.old");
+}
+if (-f "Asmdata.pm") {
+    rename("Asmdata.pm", "Asmdata.pm.old");
+}
+
+#
+# Start with boilerplate for Asmdata.pm
+#
+open(ASMDATA_PM, ">Asmdata.pm") or die "Asmdata.pm: $!";
+print ASMDATA_PM $perl_header, <<'EOT';
+package B::Asmdata;
+use Exporter;
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(%insn_data @insn_name @optype @specialsv_name);
+use vars qw(%insn_data @insn_name @optype @specialsv_name);
+
+EOT
+print ASMDATA_PM <<"EOT";
+\@optype = qw(@optype);
+\@specialsv_name = qw(@specialsv);
+
+# XXX insn_data is initialised this way because with a large
+# %insn_data = (foo => [...], bar => [...], ...) initialiser
+# I get a hard-to-track-down stack underflow and segfault.
+EOT
+
+#
+# Boilerplate for byterun.c
+#
+open(BYTERUN_C, ">byterun.c") or die "byterun.c: $!";
+print BYTERUN_C $c_header, <<'EOT';
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "bytecode.h"
+#include "byterun.h"
+
+#ifdef INDIRECT_BGET_MACROS
+void byterun(bs)
+struct bytestream bs;
+#else
+void byterun(fp)
+FILE *fp;
+#endif /* INDIRECT_BGET_MACROS */
+{
+    int insn;
+    while ((insn = FGETC()) != EOF) {
+       switch (insn) {
+EOT
+
+
+my (@insn_name, $insn_num, $insn, $lvalue, $argtype, $flags, $fundtype);
+
+while (<DATA>) {
+    chop;
+    s/#.*//;                   # remove comments
+    next unless length;
+    if (/^%number\s+(.*)/) {
+       $insn_num = $1;
+       next;
+    } elsif (/%enum\s+(.*?)\s+(.*)/) {
+       create_enum($1, $2);    # must come before instructions
+       next;
+    }
+    ($insn, $lvalue, $argtype, $flags) = split;
+    $insn_name[$insn_num] = $insn;
+    $fundtype = $alias_from{$argtype} || $argtype;
+
+    #
+    # Add the case statement and code for the bytecode interpreter in byterun.c
+    #
+    printf BYTERUN_C "\t  case INSN_%s:\t\t/* %d */\n\t    {\n",
+       uc($insn), $insn_num;
+    my $optarg = $argtype eq "none" ? "" : ", arg";
+    if ($optarg) {
+       printf BYTERUN_C "\t\t$argtype arg;\n\t\tBGET_%s(arg);\n", $fundtype;
+    }
+    if ($flags =~ /x/) {
+       print BYTERUN_C "\t\tBSET_$insn($lvalue$optarg);\n";
+    } elsif ($flags =~ /s/) {
+       # Store instructions store to obj_list[arg]. "lvalue" field is rvalue.
+       print BYTERUN_C "\t\tBSET_OBJ_STORE($lvalue$optarg);\n";
+    }
+    elsif ($optarg && $lvalue ne "none") {
+       print BYTERUN_C "\t\t$lvalue = arg;\n";
+    }
+    print BYTERUN_C "\t\tbreak;\n\t    }\n";
+
+    #
+    # Add the initialiser line for %insn_data in Asmdata.pm
+    #
+    print ASMDATA_PM <<"EOT";
+\$insn_data{$insn} = [$insn_num, \\&PUT_$fundtype, "GET_$fundtype"];
+EOT
+
+    # Find the next unused instruction number
+    do { $insn_num++ } while $insn_name[$insn_num];
+}
+
+#
+# Finish off byterun.c
+#
+print BYTERUN_C <<'EOT';
+         default:
+           croak("Illegal bytecode instruction %d\n", insn);
+           /* NOTREACHED */
+       }
+    }
+}
+EOT
+
+#
+# Write the instruction and optype enum constants into byterun.h
+#
+open(BYTERUN_H, ">byterun.h") or die "byterun.h: $!";
+print BYTERUN_H $c_header, <<'EOT';
+#ifdef INDIRECT_BGET_MACROS
+struct bytestream {
+    void *data;
+    int (*fgetc)(void *);
+    int (*fread)(char *, size_t, size_t, void*);
+    void (*freadpv)(U32, void*);
+};
+void freadpv _((U32, void *));
+void byterun _((struct bytestream));
+#else
+void byterun _((FILE *));
+#endif /* INDIRECT_BGET_MACROS */
+
+enum {
+EOT
+
+my $i = 0;
+my $add_enum_value = 0;
+my $max_insn;
+for ($i = 0; $i < @insn_name; $i++) {
+    $insn = uc($insn_name[$i]);
+    if (defined($insn)) {
+       $max_insn = $i;
+       if ($add_enum_value) {
+           print BYTERUN_H "    INSN_$insn = $i,\t\t\t/* $i */\n";
+           $add_enum_value = 0;
+       } else {
+           print BYTERUN_H "    INSN_$insn,\t\t\t/* $i */\n";
+       }
+    } else {
+       $add_enum_value = 1;
+    }
+}
+
+print BYTERUN_H "    MAX_INSN = $max_insn\n};\n";
+
+print BYTERUN_H "\nenum {\n";
+for ($i = 0; $i < @optype - 1; $i++) {
+    printf BYTERUN_H "    OPt_%s,\t\t/* %d */\n", $optype[$i], $i;
+}
+printf BYTERUN_H "    OPt_%s\t\t/* %d */\n};\n\n", $optype[$i], $i;
+print BYTERUN_H <<'EOT';
+EXT int optype_size[]
+#ifdef DOINIT
+= {
+EOT
+for ($i = 0; $i < @optype - 1; $i++) {
+    printf BYTERUN_H "    sizeof(%s),\n", $optype[$i], $i;
+}
+printf BYTERUN_H "    sizeof(%s)\n}\n", $optype[$i], $i;
+print BYTERUN_H <<'EOT';
+#endif /* DOINIT */
+;
+
+EOT
+
+printf BYTERUN_H <<'EOT', scalar(@specialsv);
+EXT SV * specialsv_list[%d]
+#ifdef DOINIT
+EOT
+print BYTERUN_H "= { ", join(", ", @specialsv), " }\n";
+print BYTERUN_H <<'EOT';
+#endif /* DOINIT */
+;
+EOT
+
+#
+# Finish off insn_data and create array initialisers in Asmdata.pm
+#
+print ASMDATA_PM <<'EOT';
+
+my ($insn_name, $insn_data);
+while (($insn_name, $insn_data) = each %insn_data) {
+    $insn_name[$insn_data->[0]] = $insn_name;
+}
+# Fill in any gaps
+@insn_name = map($_ || "unused", @insn_name);
+
+1;
+EOT
+
+__END__
+# First set instruction ord("#") to read comment to end-of-line (sneaky)
+%number 35
+comment                arg                     comment
+# Then make ord("\n") into a no-op
+%number 10
+nop            none                    none
+# Now for the rest of the ordinary ones, beginning with \0 which is
+# ret so that \0-terminated strings can be read properly as bytecode.
+%number 0
+#
+#opcode                lvalue                  argtype         flags   
+#
+ret            none                    none            x
+ldsv           sv                      svindex
+ldop           op                      opindex
+stsv           sv                      U32             s
+stop           op                      U32             s
+ldspecsv       sv                      U8              x
+newsv          sv                      U8              x
+newop          op                      U8              x
+newopn         op                      U8              x
+newpv          none                    PV
+pv_cur         pv.xpv_cur              STRLEN
+pv_free                pv                      none            x
+sv_upgrade     sv                      char            x
+sv_refcnt      SvREFCNT(sv)            U32
+sv_refcnt_add  SvREFCNT(sv)            I32             x
+sv_flags       SvFLAGS(sv)             U32
+xrv            SvRV(sv)                svindex
+xpv            sv                      none            x
+xiv32          SvIVX(sv)               I32
+xiv64          SvIVX(sv)               IV64
+xnv            SvNVX(sv)               double
+xlv_targoff    LvTARGOFF(sv)           STRLEN
+xlv_targlen    LvTARGLEN(sv)           STRLEN
+xlv_targ       LvTARG(sv)              svindex
+xlv_type       LvTYPE(sv)              char
+xbm_useful     BmUSEFUL(sv)            I32
+xbm_previous   BmPREVIOUS(sv)          U16
+xbm_rare       BmRARE(sv)              U8
+xfm_lines      FmLINES(sv)             I32
+xio_lines      IoLINES(sv)             long
+xio_page       IoPAGE(sv)              long
+xio_page_len   IoPAGE_LEN(sv)          long
+xio_lines_left IoLINES_LEFT(sv)        long
+xio_top_name   IoTOP_NAME(sv)          pvcontents
+xio_top_gv     IoTOP_GV(sv)            svindex
+xio_fmt_name   IoFMT_NAME(sv)          pvcontents
+xio_fmt_gv     IoFMT_GV(sv)            svindex
+xio_bottom_name        IoBOTTOM_NAME(sv)       pvcontents
+xio_bottom_gv  IoBOTTOM_GV(sv)         svindex
+xio_subprocess IoSUBPROCESS(sv)        short
+xio_type       IoTYPE(sv)              char
+xio_flags      IoFLAGS(sv)             char
+xcv_stash      *(SV**)&CvSTASH(sv)     svindex
+xcv_start      CvSTART(sv)             opindex
+xcv_root       CvROOT(sv)              opindex
+xcv_gv         CvGV(sv)                svindex
+xcv_filegv     CvFILEGV(sv)            svindex
+xcv_depth      CvDEPTH(sv)             long
+xcv_padlist    *(SV**)&CvPADLIST(sv)   svindex
+xcv_outside    *(SV**)&CvOUTSIDE(sv)   svindex
+xcv_flags      CvFLAGS(sv)             U8
+av_extend      sv                      SSize_t         x
+av_push                sv                      svindex         x
+xav_fill       AvFILL(sv)              SSize_t
+xav_max                AvMAX(sv)               SSize_t
+xav_flags      AvFLAGS(sv)             U8
+xhv_riter      HvRITER(sv)             I32
+xhv_name       HvNAME(sv)              pvcontents
+hv_store       sv                      svindex         x
+sv_magic       sv                      char            x
+mg_obj         SvMAGIC(sv)->mg_obj     svindex
+mg_private     SvMAGIC(sv)->mg_private U16
+mg_flags       SvMAGIC(sv)->mg_flags   U8
+mg_pv          SvMAGIC(sv)             pvcontents      x
+xmg_stash      *(SV**)&SvSTASH(sv)     svindex
+gv_fetchpv     sv                      strconst        x
+gv_stashpv     sv                      strconst        x
+gp_sv          GvSV(sv)                svindex
+gp_refcnt      GvREFCNT(sv)            U32
+gp_refcnt_add  GvREFCNT(sv)            I32             x
+gp_av          *(SV**)&GvAV(sv)        svindex
+gp_hv          *(SV**)&GvHV(sv)        svindex
+gp_cv          *(SV**)&GvCV(sv)        svindex
+gp_filegv      *(SV**)&GvFILEGV(sv)    svindex
+gp_io          *(SV**)&GvIOp(sv)       svindex
+gp_form                *(SV**)&GvFORM(sv)      svindex
+gp_cvgen       GvCVGEN(sv)             U32
+gp_line                GvLINE(sv)              line_t
+gp_share       sv                      svindex         x
+xgv_flags      GvFLAGS(sv)             U8
+op_next                op->op_next             opindex
+op_sibling     op->op_sibling          opindex
+op_ppaddr      op->op_ppaddr           strconst        x
+op_targ                op->op_targ             PADOFFSET
+op_type                op                      OPCODE          x
+op_seq         op->op_seq              U16
+op_flags       op->op_flags            U8
+op_private     op->op_private          U8
+op_first       cUNOP->op_first         opindex
+op_last                cBINOP->op_last         opindex
+op_other       cLOGOP->op_other        opindex
+op_true                cCONDOP->op_true        opindex
+op_false       cCONDOP->op_false       opindex
+op_children    cLISTOP->op_children    U32
+op_pmreplroot  cPMOP->op_pmreplroot    opindex
+op_pmreplrootgv        *(SV**)&cPMOP->op_pmreplroot    svindex
+op_pmreplstart cPMOP->op_pmreplstart   opindex
+op_pmnext      *(OP**)&cPMOP->op_pmnext        opindex
+pregcomp       op                      pvcontents      x
+op_pmshort     cPMOP->op_pmshort       svindex
+op_pmflags     cPMOP->op_pmflags       U16
+op_pmpermflags cPMOP->op_pmpermflags   U16
+op_pmslen      cPMOP->op_pmslen        char
+op_sv          cSVOP->op_sv            svindex
+op_gv          cGVOP->op_gv            svindex
+op_pv          cPVOP->op_pv            pvcontents
+op_pv_tr       cPVOP->op_pv            op_tr_array
+op_redoop      cLOOP->op_redoop        opindex
+op_nextop      cLOOP->op_nextop        opindex
+op_lastop      cLOOP->op_lastop        opindex
+cop_label      cCOP->cop_label         pvcontents
+cop_stash      *(SV**)&cCOP->cop_stash         svindex
+cop_filegv     cCOP->cop_filegv        svindex
+cop_seq                cCOP->cop_seq           U32
+cop_arybase    cCOP->cop_arybase       I32
+cop_line       cCOP->cop_line          line_t
+main_start     main_start              opindex
+main_root      main_root               opindex
+curpad         curpad                  svindex         x
diff --git a/byteperl.c b/byteperl.c
new file mode 100644 (file)
index 0000000..e81a45b
--- /dev/null
@@ -0,0 +1,104 @@
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "byterun.h"
+
+#ifdef __cplusplus
+}
+#  define EXTERN_C extern "C"
+#else
+#  define EXTERN_C extern
+#endif
+
+static void xs_init _((void));
+static PerlInterpreter *my_perl;
+
+int
+#ifndef CAN_PROTOTYPE
+main(argc, argv, env)
+int argc;
+char **argv;
+char **env;
+#else  /* def(CAN_PROTOTYPE) */
+main(int argc, char **argv, char **env)
+#endif  /* def(CAN_PROTOTYPE) */
+{
+    int exitstatus;
+    int i;
+    char **fakeargv;
+    FILE *fp;
+#ifdef INDIRECT_BGET_MACROS
+    struct bytestream bs;
+#endif /* INDIRECT_BGET_MACROS */
+
+    PERL_SYS_INIT(&argc,&argv);
+#if PATCHLEVEL > 3 || (PATCHLEVEL == 3 && SUBVERSION >= 1)
+    perl_init_i18nl10n(1);
+#else
+    perl_init_i18nl14n(1);
+#endif
+
+    if (!do_undump) {
+       my_perl = perl_alloc();
+       if (!my_perl)
+           exit(1);
+       perl_construct( my_perl );
+    }
+
+    if (!cshlen) 
+      cshlen = strlen(cshname);
+
+    if (argc < 2)
+       fp = stdin;
+    else {
+       fp = fopen(argv[1], "r");
+       if (!fp) {
+           perror(argv[1]);
+           exit(1);
+       }
+       argv++;
+       argc--;
+    }
+    New(666, fakeargv, argc + 4, char *);
+    fakeargv[0] = argv[0];
+    fakeargv[1] = "-e";
+    fakeargv[2] = "";
+    fakeargv[3] = "--";
+    for (i = 1; i < argc; i++)
+       fakeargv[i + 3] = argv[i];
+    fakeargv[argc + 3] = 0;
+    
+    exitstatus = perl_parse(my_perl, xs_init, argc + 3, fakeargv, NULL);
+    if (exitstatus)
+       exit( exitstatus );
+
+    sv_setpv(GvSV(gv_fetchpv("0", TRUE, SVt_PV)), argv[0]);
+    main_cv = compcv;
+    compcv = 0;
+
+#ifdef INDIRECT_BGET_MACROS
+    bs.data = fp;
+    bs.fgetc = (int(*) _((void*)))fgetc;
+    bs.fread = (int(*) _((char*,size_t,size_t,void*)))fread;
+    bs.freadpv = freadpv;
+    byterun(bs);
+#else    
+    byterun(fp);
+#endif /* INDIRECT_BGET_MACROS */
+    
+    exitstatus = perl_run( my_perl );
+
+    perl_destruct( my_perl );
+    perl_free( my_perl );
+
+    exit( exitstatus );
+}
+
+static void
+xs_init()
+{
+}
diff --git a/byterun.c b/byterun.c
new file mode 100644 (file)
index 0000000..1ff3239
--- /dev/null
+++ b/byterun.c
@@ -0,0 +1,870 @@
+/*
+ *      Copyright (c) 1996 Malcolm Beattie
+ *
+ *      You may distribute under the terms of either the GNU General Public
+ *      License or the Artistic License, as specified in the README file.
+ *
+ */
+/*
+ * This file is autogenerated from bytecode.pl. Changes made here will be lost.
+ */
+
+#include "EXTERN.h"
+#include "perl.h"
+#include "bytecode.h"
+#include "byterun.h"
+
+#ifdef INDIRECT_BGET_MACROS
+void byterun(bs)
+struct bytestream bs;
+#else
+void byterun(fp)
+FILE *fp;
+#endif /* INDIRECT_BGET_MACROS */
+{
+    int insn;
+    while ((insn = FGETC()) != EOF) {
+       switch (insn) {
+         case INSN_COMMENT:            /* 35 */
+           {
+               comment arg;
+               BGET_comment(arg);
+               arg = arg;
+               break;
+           }
+         case INSN_NOP:                /* 10 */
+           {
+               break;
+           }
+         case INSN_RET:                /* 0 */
+           {
+               BSET_ret(none);
+               break;
+           }
+         case INSN_LDSV:               /* 1 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               sv = arg;
+               break;
+           }
+         case INSN_LDOP:               /* 2 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               op = arg;
+               break;
+           }
+         case INSN_STSV:               /* 3 */
+           {
+               U32 arg;
+               BGET_U32(arg);
+               BSET_OBJ_STORE(sv, arg);
+               break;
+           }
+         case INSN_STOP:               /* 4 */
+           {
+               U32 arg;
+               BGET_U32(arg);
+               BSET_OBJ_STORE(op, arg);
+               break;
+           }
+         case INSN_LDSPECSV:           /* 5 */
+           {
+               U8 arg;
+               BGET_U8(arg);
+               BSET_ldspecsv(sv, arg);
+               break;
+           }
+         case INSN_NEWSV:              /* 6 */
+           {
+               U8 arg;
+               BGET_U8(arg);
+               BSET_newsv(sv, arg);
+               break;
+           }
+         case INSN_NEWOP:              /* 7 */
+           {
+               U8 arg;
+               BGET_U8(arg);
+               BSET_newop(op, arg);
+               break;
+           }
+         case INSN_NEWOPN:             /* 8 */
+           {
+               U8 arg;
+               BGET_U8(arg);
+               BSET_newopn(op, arg);
+               break;
+           }
+         case INSN_NEWPV:              /* 9 */
+           {
+               PV arg;
+               BGET_PV(arg);
+               break;
+           }
+         case INSN_PV_CUR:             /* 11 */
+           {
+               STRLEN arg;
+               BGET_U32(arg);
+               pv.xpv_cur = arg;
+               break;
+           }
+         case INSN_PV_FREE:            /* 12 */
+           {
+               BSET_pv_free(pv);
+               break;
+           }
+         case INSN_SV_UPGRADE:         /* 13 */
+           {
+               char arg;
+               BGET_U8(arg);
+               BSET_sv_upgrade(sv, arg);
+               break;
+           }
+         case INSN_SV_REFCNT:          /* 14 */
+           {
+               U32 arg;
+               BGET_U32(arg);
+               SvREFCNT(sv) = arg;
+               break;
+           }
+         case INSN_SV_REFCNT_ADD:              /* 15 */
+           {
+               I32 arg;
+               BGET_I32(arg);
+               BSET_sv_refcnt_add(SvREFCNT(sv), arg);
+               break;
+           }
+         case INSN_SV_FLAGS:           /* 16 */
+           {
+               U32 arg;
+               BGET_U32(arg);
+               SvFLAGS(sv) = arg;
+               break;
+           }
+         case INSN_XRV:                /* 17 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               SvRV(sv) = arg;
+               break;
+           }
+         case INSN_XPV:                /* 18 */
+           {
+               BSET_xpv(sv);
+               break;
+           }
+         case INSN_XIV32:              /* 19 */
+           {
+               I32 arg;
+               BGET_I32(arg);
+               SvIVX(sv) = arg;
+               break;
+           }
+         case INSN_XIV64:              /* 20 */
+           {
+               IV64 arg;
+               BGET_IV64(arg);
+               SvIVX(sv) = arg;
+               break;
+           }
+         case INSN_XNV:                /* 21 */
+           {
+               double arg;
+               BGET_double(arg);
+               SvNVX(sv) = arg;
+               break;
+           }
+         case INSN_XLV_TARGOFF:                /* 22 */
+           {
+               STRLEN arg;
+               BGET_U32(arg);
+               LvTARGOFF(sv) = arg;
+               break;
+           }
+         case INSN_XLV_TARGLEN:                /* 23 */
+           {
+               STRLEN arg;
+               BGET_U32(arg);
+               LvTARGLEN(sv) = arg;
+               break;
+           }
+         case INSN_XLV_TARG:           /* 24 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               LvTARG(sv) = arg;
+               break;
+           }
+         case INSN_XLV_TYPE:           /* 25 */
+           {
+               char arg;
+               BGET_U8(arg);
+               LvTYPE(sv) = arg;
+               break;
+           }
+         case INSN_XBM_USEFUL:         /* 26 */
+           {
+               I32 arg;
+               BGET_I32(arg);
+               BmUSEFUL(sv) = arg;
+               break;
+           }
+         case INSN_XBM_PREVIOUS:               /* 27 */
+           {
+               U16 arg;
+               BGET_U16(arg);
+               BmPREVIOUS(sv) = arg;
+               break;
+           }
+         case INSN_XBM_RARE:           /* 28 */
+           {
+               U8 arg;
+               BGET_U8(arg);
+               BmRARE(sv) = arg;
+               break;
+           }
+         case INSN_XFM_LINES:          /* 29 */
+           {
+               I32 arg;
+               BGET_I32(arg);
+               FmLINES(sv) = arg;
+               break;
+           }
+         case INSN_XIO_LINES:          /* 30 */
+           {
+               long arg;
+               BGET_I32(arg);
+               IoLINES(sv) = arg;
+               break;
+           }
+         case INSN_XIO_PAGE:           /* 31 */
+           {
+               long arg;
+               BGET_I32(arg);
+               IoPAGE(sv) = arg;
+               break;
+           }
+         case INSN_XIO_PAGE_LEN:               /* 32 */
+           {
+               long arg;
+               BGET_I32(arg);
+               IoPAGE_LEN(sv) = arg;
+               break;
+           }
+         case INSN_XIO_LINES_LEFT:             /* 33 */
+           {
+               long arg;
+               BGET_I32(arg);
+               IoLINES_LEFT(sv) = arg;
+               break;
+           }
+         case INSN_XIO_TOP_NAME:               /* 34 */
+           {
+               pvcontents arg;
+               BGET_pvcontents(arg);
+               IoTOP_NAME(sv) = arg;
+               break;
+           }
+         case INSN_XIO_TOP_GV:         /* 36 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               IoTOP_GV(sv) = arg;
+               break;
+           }
+         case INSN_XIO_FMT_NAME:               /* 37 */
+           {
+               pvcontents arg;
+               BGET_pvcontents(arg);
+               IoFMT_NAME(sv) = arg;
+               break;
+           }
+         case INSN_XIO_FMT_GV:         /* 38 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               IoFMT_GV(sv) = arg;
+               break;
+           }
+         case INSN_XIO_BOTTOM_NAME:            /* 39 */
+           {
+               pvcontents arg;
+               BGET_pvcontents(arg);
+               IoBOTTOM_NAME(sv) = arg;
+               break;
+           }
+         case INSN_XIO_BOTTOM_GV:              /* 40 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               IoBOTTOM_GV(sv) = arg;
+               break;
+           }
+         case INSN_XIO_SUBPROCESS:             /* 41 */
+           {
+               short arg;
+               BGET_U16(arg);
+               IoSUBPROCESS(sv) = arg;
+               break;
+           }
+         case INSN_XIO_TYPE:           /* 42 */
+           {
+               char arg;
+               BGET_U8(arg);
+               IoTYPE(sv) = arg;
+               break;
+           }
+         case INSN_XIO_FLAGS:          /* 43 */
+           {
+               char arg;
+               BGET_U8(arg);
+               IoFLAGS(sv) = arg;
+               break;
+           }
+         case INSN_XCV_STASH:          /* 44 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               *(SV**)&CvSTASH(sv) = arg;
+               break;
+           }
+         case INSN_XCV_START:          /* 45 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               CvSTART(sv) = arg;
+               break;
+           }
+         case INSN_XCV_ROOT:           /* 46 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               CvROOT(sv) = arg;
+               break;
+           }
+         case INSN_XCV_GV:             /* 47 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               CvGV(sv) = arg;
+               break;
+           }
+         case INSN_XCV_FILEGV:         /* 48 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               CvFILEGV(sv) = arg;
+               break;
+           }
+         case INSN_XCV_DEPTH:          /* 49 */
+           {
+               long arg;
+               BGET_I32(arg);
+               CvDEPTH(sv) = arg;
+               break;
+           }
+         case INSN_XCV_PADLIST:                /* 50 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               *(SV**)&CvPADLIST(sv) = arg;
+               break;
+           }
+         case INSN_XCV_OUTSIDE:                /* 51 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               *(SV**)&CvOUTSIDE(sv) = arg;
+               break;
+           }
+         case INSN_XCV_FLAGS:          /* 52 */
+           {
+               U8 arg;
+               BGET_U8(arg);
+               CvFLAGS(sv) = arg;
+               break;
+           }
+         case INSN_AV_EXTEND:          /* 53 */
+           {
+               SSize_t arg;
+               BGET_I32(arg);
+               BSET_av_extend(sv, arg);
+               break;
+           }
+         case INSN_AV_PUSH:            /* 54 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               BSET_av_push(sv, arg);
+               break;
+           }
+         case INSN_XAV_FILL:           /* 55 */
+           {
+               SSize_t arg;
+               BGET_I32(arg);
+               AvFILL(sv) = arg;
+               break;
+           }
+         case INSN_XAV_MAX:            /* 56 */
+           {
+               SSize_t arg;
+               BGET_I32(arg);
+               AvMAX(sv) = arg;
+               break;
+           }
+         case INSN_XAV_FLAGS:          /* 57 */
+           {
+               U8 arg;
+               BGET_U8(arg);
+               AvFLAGS(sv) = arg;
+               break;
+           }
+         case INSN_XHV_RITER:          /* 58 */
+           {
+               I32 arg;
+               BGET_I32(arg);
+               HvRITER(sv) = arg;
+               break;
+           }
+         case INSN_XHV_NAME:           /* 59 */
+           {
+               pvcontents arg;
+               BGET_pvcontents(arg);
+               HvNAME(sv) = arg;
+               break;
+           }
+         case INSN_HV_STORE:           /* 60 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               BSET_hv_store(sv, arg);
+               break;
+           }
+         case INSN_SV_MAGIC:           /* 61 */
+           {
+               char arg;
+               BGET_U8(arg);
+               BSET_sv_magic(sv, arg);
+               break;
+           }
+         case INSN_MG_OBJ:             /* 62 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               SvMAGIC(sv)->mg_obj = arg;
+               break;
+           }
+         case INSN_MG_PRIVATE:         /* 63 */
+           {
+               U16 arg;
+               BGET_U16(arg);
+               SvMAGIC(sv)->mg_private = arg;
+               break;
+           }
+         case INSN_MG_FLAGS:           /* 64 */
+           {
+               U8 arg;
+               BGET_U8(arg);
+               SvMAGIC(sv)->mg_flags = arg;
+               break;
+           }
+         case INSN_MG_PV:              /* 65 */
+           {
+               pvcontents arg;
+               BGET_pvcontents(arg);
+               BSET_mg_pv(SvMAGIC(sv), arg);
+               break;
+           }
+         case INSN_XMG_STASH:          /* 66 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               *(SV**)&SvSTASH(sv) = arg;
+               break;
+           }
+         case INSN_GV_FETCHPV:         /* 67 */
+           {
+               strconst arg;
+               BGET_strconst(arg);
+               BSET_gv_fetchpv(sv, arg);
+               break;
+           }
+         case INSN_GV_STASHPV:         /* 68 */
+           {
+               strconst arg;
+               BGET_strconst(arg);
+               BSET_gv_stashpv(sv, arg);
+               break;
+           }
+         case INSN_GP_SV:              /* 69 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               GvSV(sv) = arg;
+               break;
+           }
+         case INSN_GP_REFCNT:          /* 70 */
+           {
+               U32 arg;
+               BGET_U32(arg);
+               GvREFCNT(sv) = arg;
+               break;
+           }
+         case INSN_GP_REFCNT_ADD:              /* 71 */
+           {
+               I32 arg;
+               BGET_I32(arg);
+               BSET_gp_refcnt_add(GvREFCNT(sv), arg);
+               break;
+           }
+         case INSN_GP_AV:              /* 72 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               *(SV**)&GvAV(sv) = arg;
+               break;
+           }
+         case INSN_GP_HV:              /* 73 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               *(SV**)&GvHV(sv) = arg;
+               break;
+           }
+         case INSN_GP_CV:              /* 74 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               *(SV**)&GvCV(sv) = arg;
+               break;
+           }
+         case INSN_GP_FILEGV:          /* 75 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               *(SV**)&GvFILEGV(sv) = arg;
+               break;
+           }
+         case INSN_GP_IO:              /* 76 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               *(SV**)&GvIOp(sv) = arg;
+               break;
+           }
+         case INSN_GP_FORM:            /* 77 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               *(SV**)&GvFORM(sv) = arg;
+               break;
+           }
+         case INSN_GP_CVGEN:           /* 78 */
+           {
+               U32 arg;
+               BGET_U32(arg);
+               GvCVGEN(sv) = arg;
+               break;
+           }
+         case INSN_GP_LINE:            /* 79 */
+           {
+               line_t arg;
+               BGET_U16(arg);
+               GvLINE(sv) = arg;
+               break;
+           }
+         case INSN_GP_SHARE:           /* 80 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               BSET_gp_share(sv, arg);
+               break;
+           }
+         case INSN_XGV_FLAGS:          /* 81 */
+           {
+               U8 arg;
+               BGET_U8(arg);
+               GvFLAGS(sv) = arg;
+               break;
+           }
+         case INSN_OP_NEXT:            /* 82 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               op->op_next = arg;
+               break;
+           }
+         case INSN_OP_SIBLING:         /* 83 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               op->op_sibling = arg;
+               break;
+           }
+         case INSN_OP_PPADDR:          /* 84 */
+           {
+               strconst arg;
+               BGET_strconst(arg);
+               BSET_op_ppaddr(op->op_ppaddr, arg);
+               break;
+           }
+         case INSN_OP_TARG:            /* 85 */
+           {
+               PADOFFSET arg;
+               BGET_U32(arg);
+               op->op_targ = arg;
+               break;
+           }
+         case INSN_OP_TYPE:            /* 86 */
+           {
+               OPCODE arg;
+               BGET_U16(arg);
+               BSET_op_type(op, arg);
+               break;
+           }
+         case INSN_OP_SEQ:             /* 87 */
+           {
+               U16 arg;
+               BGET_U16(arg);
+               op->op_seq = arg;
+               break;
+           }
+         case INSN_OP_FLAGS:           /* 88 */
+           {
+               U8 arg;
+               BGET_U8(arg);
+               op->op_flags = arg;
+               break;
+           }
+         case INSN_OP_PRIVATE:         /* 89 */
+           {
+               U8 arg;
+               BGET_U8(arg);
+               op->op_private = arg;
+               break;
+           }
+         case INSN_OP_FIRST:           /* 90 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               cUNOP->op_first = arg;
+               break;
+           }
+         case INSN_OP_LAST:            /* 91 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               cBINOP->op_last = arg;
+               break;
+           }
+         case INSN_OP_OTHER:           /* 92 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               cLOGOP->op_other = arg;
+               break;
+           }
+         case INSN_OP_TRUE:            /* 93 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               cCONDOP->op_true = arg;
+               break;
+           }
+         case INSN_OP_FALSE:           /* 94 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               cCONDOP->op_false = arg;
+               break;
+           }
+         case INSN_OP_CHILDREN:                /* 95 */
+           {
+               U32 arg;
+               BGET_U32(arg);
+               cLISTOP->op_children = arg;
+               break;
+           }
+         case INSN_OP_PMREPLROOT:              /* 96 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               cPMOP->op_pmreplroot = arg;
+               break;
+           }
+         case INSN_OP_PMREPLROOTGV:            /* 97 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               *(SV**)&cPMOP->op_pmreplroot = arg;
+               break;
+           }
+         case INSN_OP_PMREPLSTART:             /* 98 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               cPMOP->op_pmreplstart = arg;
+               break;
+           }
+         case INSN_OP_PMNEXT:          /* 99 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               *(OP**)&cPMOP->op_pmnext = arg;
+               break;
+           }
+         case INSN_PREGCOMP:           /* 100 */
+           {
+               pvcontents arg;
+               BGET_pvcontents(arg);
+               BSET_pregcomp(op, arg);
+               break;
+           }
+         case INSN_OP_PMSHORT:         /* 101 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               cPMOP->op_pmshort = arg;
+               break;
+           }
+         case INSN_OP_PMFLAGS:         /* 102 */
+           {
+               U16 arg;
+               BGET_U16(arg);
+               cPMOP->op_pmflags = arg;
+               break;
+           }
+         case INSN_OP_PMPERMFLAGS:             /* 103 */
+           {
+               U16 arg;
+               BGET_U16(arg);
+               cPMOP->op_pmpermflags = arg;
+               break;
+           }
+         case INSN_OP_PMSLEN:          /* 104 */
+           {
+               char arg;
+               BGET_U8(arg);
+               cPMOP->op_pmslen = arg;
+               break;
+           }
+         case INSN_OP_SV:              /* 105 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               cSVOP->op_sv = arg;
+               break;
+           }
+         case INSN_OP_GV:              /* 106 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               cGVOP->op_gv = arg;
+               break;
+           }
+         case INSN_OP_PV:              /* 107 */
+           {
+               pvcontents arg;
+               BGET_pvcontents(arg);
+               cPVOP->op_pv = arg;
+               break;
+           }
+         case INSN_OP_PV_TR:           /* 108 */
+           {
+               op_tr_array arg;
+               BGET_op_tr_array(arg);
+               cPVOP->op_pv = arg;
+               break;
+           }
+         case INSN_OP_REDOOP:          /* 109 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               cLOOP->op_redoop = arg;
+               break;
+           }
+         case INSN_OP_NEXTOP:          /* 110 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               cLOOP->op_nextop = arg;
+               break;
+           }
+         case INSN_OP_LASTOP:          /* 111 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               cLOOP->op_lastop = arg;
+               break;
+           }
+         case INSN_COP_LABEL:          /* 112 */
+           {
+               pvcontents arg;
+               BGET_pvcontents(arg);
+               cCOP->cop_label = arg;
+               break;
+           }
+         case INSN_COP_STASH:          /* 113 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               *(SV**)&cCOP->cop_stash = arg;
+               break;
+           }
+         case INSN_COP_FILEGV:         /* 114 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               cCOP->cop_filegv = arg;
+               break;
+           }
+         case INSN_COP_SEQ:            /* 115 */
+           {
+               U32 arg;
+               BGET_U32(arg);
+               cCOP->cop_seq = arg;
+               break;
+           }
+         case INSN_COP_ARYBASE:                /* 116 */
+           {
+               I32 arg;
+               BGET_I32(arg);
+               cCOP->cop_arybase = arg;
+               break;
+           }
+         case INSN_COP_LINE:           /* 117 */
+           {
+               line_t arg;
+               BGET_U16(arg);
+               cCOP->cop_line = arg;
+               break;
+           }
+         case INSN_MAIN_START:         /* 118 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               main_start = arg;
+               break;
+           }
+         case INSN_MAIN_ROOT:          /* 119 */
+           {
+               opindex arg;
+               BGET_objindex(arg);
+               main_root = arg;
+               break;
+           }
+         case INSN_CURPAD:             /* 120 */
+           {
+               svindex arg;
+               BGET_objindex(arg);
+               BSET_curpad(curpad, arg);
+               break;
+           }
+         default:
+           croak("Illegal bytecode instruction %d\n", insn);
+           /* NOTREACHED */
+       }
+    }
+}
diff --git a/byterun.h b/byterun.h
new file mode 100644 (file)
index 0000000..fee8eda
--- /dev/null
+++ b/byterun.h
@@ -0,0 +1,187 @@
+/*
+ *      Copyright (c) 1996 Malcolm Beattie
+ *
+ *      You may distribute under the terms of either the GNU General Public
+ *      License or the Artistic License, as specified in the README file.
+ *
+ */
+/*
+ * This file is autogenerated from bytecode.pl. Changes made here will be lost.
+ */
+#ifdef INDIRECT_BGET_MACROS
+struct bytestream {
+    void *data;
+    int (*fgetc)(void *);
+    int (*fread)(char *, size_t, size_t, void*);
+    void (*freadpv)(U32, void*);
+};
+void freadpv _((U32, void *));
+void byterun _((struct bytestream));
+#else
+void byterun _((FILE *));
+#endif /* INDIRECT_BGET_MACROS */
+
+enum {
+    INSN_RET,                  /* 0 */
+    INSN_LDSV,                 /* 1 */
+    INSN_LDOP,                 /* 2 */
+    INSN_STSV,                 /* 3 */
+    INSN_STOP,                 /* 4 */
+    INSN_LDSPECSV,                     /* 5 */
+    INSN_NEWSV,                        /* 6 */
+    INSN_NEWOP,                        /* 7 */
+    INSN_NEWOPN,                       /* 8 */
+    INSN_NEWPV,                        /* 9 */
+    INSN_NOP,                  /* 10 */
+    INSN_PV_CUR,                       /* 11 */
+    INSN_PV_FREE,                      /* 12 */
+    INSN_SV_UPGRADE,                   /* 13 */
+    INSN_SV_REFCNT,                    /* 14 */
+    INSN_SV_REFCNT_ADD,                        /* 15 */
+    INSN_SV_FLAGS,                     /* 16 */
+    INSN_XRV,                  /* 17 */
+    INSN_XPV,                  /* 18 */
+    INSN_XIV32,                        /* 19 */
+    INSN_XIV64,                        /* 20 */
+    INSN_XNV,                  /* 21 */
+    INSN_XLV_TARGOFF,                  /* 22 */
+    INSN_XLV_TARGLEN,                  /* 23 */
+    INSN_XLV_TARG,                     /* 24 */
+    INSN_XLV_TYPE,                     /* 25 */
+    INSN_XBM_USEFUL,                   /* 26 */
+    INSN_XBM_PREVIOUS,                 /* 27 */
+    INSN_XBM_RARE,                     /* 28 */
+    INSN_XFM_LINES,                    /* 29 */
+    INSN_XIO_LINES,                    /* 30 */
+    INSN_XIO_PAGE,                     /* 31 */
+    INSN_XIO_PAGE_LEN,                 /* 32 */
+    INSN_XIO_LINES_LEFT,                       /* 33 */
+    INSN_XIO_TOP_NAME,                 /* 34 */
+    INSN_COMMENT,                      /* 35 */
+    INSN_XIO_TOP_GV,                   /* 36 */
+    INSN_XIO_FMT_NAME,                 /* 37 */
+    INSN_XIO_FMT_GV,                   /* 38 */
+    INSN_XIO_BOTTOM_NAME,                      /* 39 */
+    INSN_XIO_BOTTOM_GV,                        /* 40 */
+    INSN_XIO_SUBPROCESS,                       /* 41 */
+    INSN_XIO_TYPE,                     /* 42 */
+    INSN_XIO_FLAGS,                    /* 43 */
+    INSN_XCV_STASH,                    /* 44 */
+    INSN_XCV_START,                    /* 45 */
+    INSN_XCV_ROOT,                     /* 46 */
+    INSN_XCV_GV,                       /* 47 */
+    INSN_XCV_FILEGV,                   /* 48 */
+    INSN_XCV_DEPTH,                    /* 49 */
+    INSN_XCV_PADLIST,                  /* 50 */
+    INSN_XCV_OUTSIDE,                  /* 51 */
+    INSN_XCV_FLAGS,                    /* 52 */
+    INSN_AV_EXTEND,                    /* 53 */
+    INSN_AV_PUSH,                      /* 54 */
+    INSN_XAV_FILL,                     /* 55 */
+    INSN_XAV_MAX,                      /* 56 */
+    INSN_XAV_FLAGS,                    /* 57 */
+    INSN_XHV_RITER,                    /* 58 */
+    INSN_XHV_NAME,                     /* 59 */
+    INSN_HV_STORE,                     /* 60 */
+    INSN_SV_MAGIC,                     /* 61 */
+    INSN_MG_OBJ,                       /* 62 */
+    INSN_MG_PRIVATE,                   /* 63 */
+    INSN_MG_FLAGS,                     /* 64 */
+    INSN_MG_PV,                        /* 65 */
+    INSN_XMG_STASH,                    /* 66 */
+    INSN_GV_FETCHPV,                   /* 67 */
+    INSN_GV_STASHPV,                   /* 68 */
+    INSN_GP_SV,                        /* 69 */
+    INSN_GP_REFCNT,                    /* 70 */
+    INSN_GP_REFCNT_ADD,                        /* 71 */
+    INSN_GP_AV,                        /* 72 */
+    INSN_GP_HV,                        /* 73 */
+    INSN_GP_CV,                        /* 74 */
+    INSN_GP_FILEGV,                    /* 75 */
+    INSN_GP_IO,                        /* 76 */
+    INSN_GP_FORM,                      /* 77 */
+    INSN_GP_CVGEN,                     /* 78 */
+    INSN_GP_LINE,                      /* 79 */
+    INSN_GP_SHARE,                     /* 80 */
+    INSN_XGV_FLAGS,                    /* 81 */
+    INSN_OP_NEXT,                      /* 82 */
+    INSN_OP_SIBLING,                   /* 83 */
+    INSN_OP_PPADDR,                    /* 84 */
+    INSN_OP_TARG,                      /* 85 */
+    INSN_OP_TYPE,                      /* 86 */
+    INSN_OP_SEQ,                       /* 87 */
+    INSN_OP_FLAGS,                     /* 88 */
+    INSN_OP_PRIVATE,                   /* 89 */
+    INSN_OP_FIRST,                     /* 90 */
+    INSN_OP_LAST,                      /* 91 */
+    INSN_OP_OTHER,                     /* 92 */
+    INSN_OP_TRUE,                      /* 93 */
+    INSN_OP_FALSE,                     /* 94 */
+    INSN_OP_CHILDREN,                  /* 95 */
+    INSN_OP_PMREPLROOT,                        /* 96 */
+    INSN_OP_PMREPLROOTGV,                      /* 97 */
+    INSN_OP_PMREPLSTART,                       /* 98 */
+    INSN_OP_PMNEXT,                    /* 99 */
+    INSN_PREGCOMP,                     /* 100 */
+    INSN_OP_PMSHORT,                   /* 101 */
+    INSN_OP_PMFLAGS,                   /* 102 */
+    INSN_OP_PMPERMFLAGS,                       /* 103 */
+    INSN_OP_PMSLEN,                    /* 104 */
+    INSN_OP_SV,                        /* 105 */
+    INSN_OP_GV,                        /* 106 */
+    INSN_OP_PV,                        /* 107 */
+    INSN_OP_PV_TR,                     /* 108 */
+    INSN_OP_REDOOP,                    /* 109 */
+    INSN_OP_NEXTOP,                    /* 110 */
+    INSN_OP_LASTOP,                    /* 111 */
+    INSN_COP_LABEL,                    /* 112 */
+    INSN_COP_STASH,                    /* 113 */
+    INSN_COP_FILEGV,                   /* 114 */
+    INSN_COP_SEQ,                      /* 115 */
+    INSN_COP_ARYBASE,                  /* 116 */
+    INSN_COP_LINE,                     /* 117 */
+    INSN_MAIN_START,                   /* 118 */
+    INSN_MAIN_ROOT,                    /* 119 */
+    INSN_CURPAD,                       /* 120 */
+    MAX_INSN = 120
+};
+
+enum {
+    OPt_OP,            /* 0 */
+    OPt_UNOP,          /* 1 */
+    OPt_BINOP,         /* 2 */
+    OPt_LOGOP,         /* 3 */
+    OPt_CONDOP,                /* 4 */
+    OPt_LISTOP,                /* 5 */
+    OPt_PMOP,          /* 6 */
+    OPt_SVOP,          /* 7 */
+    OPt_GVOP,          /* 8 */
+    OPt_PVOP,          /* 9 */
+    OPt_LOOP,          /* 10 */
+    OPt_COP            /* 11 */
+};
+
+EXT int optype_size[]
+#ifdef DOINIT
+= {
+    sizeof(OP),
+    sizeof(UNOP),
+    sizeof(BINOP),
+    sizeof(LOGOP),
+    sizeof(CONDOP),
+    sizeof(LISTOP),
+    sizeof(PMOP),
+    sizeof(SVOP),
+    sizeof(GVOP),
+    sizeof(PVOP),
+    sizeof(LOOP),
+    sizeof(COP)
+}
+#endif /* DOINIT */
+;
+
+EXT SV * specialsv_list[4]
+#ifdef DOINIT
+= { Nullsv, &sv_undef, &sv_yes, &sv_no }
+#endif /* DOINIT */
+;
diff --git a/cc_harness b/cc_harness
new file mode 100644 (file)
index 0000000..6db623a
--- /dev/null
@@ -0,0 +1,11 @@
+use Config;
+
+$libdir = "$Config{installarchlib}/CORE";
+
+if (!grep(/^-[cS]$/, @ARGV)) {
+    $linkargs = sprintf("%s -L$libdir -lperl %s", @Config{qw(ldflags libs)});
+}
+
+$cccmd = "$Config{cc} $Config{ccflags} -I$libdir @ARGV $linkargs";
+print "$cccmd\n";
+exec $cccmd;
diff --git a/cc_runtime.h b/cc_runtime.h
new file mode 100644 (file)
index 0000000..b7658a9
--- /dev/null
@@ -0,0 +1,75 @@
+#define DOOP(ppname) PUTBACK; op = ppname(); SPAGAIN
+
+#define PP_LIST(g) do {                        \
+       dMARK;                          \
+       if (g != G_ARRAY) {             \
+           if (++MARK <= SP)           \
+               *MARK = *SP;            \
+           else                        \
+               *MARK = &sv_undef;      \
+           SP = MARK;                  \
+       }                               \
+   } while (0)
+
+#define MAYBE_TAINT_SASSIGN_SRC(sv) \
+    if (tainting && tainted && (!SvGMAGICAL(left) || !SvSMAGICAL(left) || \
+                                !((mg=mg_find(left, 't')) && mg->mg_len & 1)))\
+        TAINT_NOT
+
+#define PP_PREINC(sv) do {     \
+       if (SvIOK(sv)) {        \
+            ++SvIVX(sv);       \
+           SvFLAGS(sv) &= ~(SVf_NOK|SVf_POK|SVp_NOK|SVp_POK); \
+       }                       \
+       else                    \
+           sv_inc(sv);         \
+       SvSETMAGIC(sv);         \
+    } while (0)
+
+#define PP_UNSTACK do {                \
+       TAINT_NOT;              \
+       stack_sp = stack_base + cxstack[cxstack_ix].blk_oldsp;  \
+       FREETMPS;               \
+       oldsave = scopestack[scopestack_ix - 1]; \
+       LEAVE_SCOPE(oldsave);   \
+       SPAGAIN;                \
+    } while(0)
+
+#include "patchlevel.h"
+#if PATCHLEVEL < 3
+#define RUN() run()
+#else
+#define RUN() runops()
+#endif
+
+/* Anyone using eval "" deserves this mess */
+#define PP_EVAL(ppaddr, nxt) do {              \