Bytecode mustn't save the NVX for hashes now that the NVX is a union
[p5sagit/p5-mst-13.2.git] / ext / B / B / Bytecode.pm
index ef59c4a..a6cf550 100644 (file)
-#      Bytecode.pm
-#
-#      Copyright (c) 1996-1998 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.
-#
+# B::Bytecode.pm
+# Copyright (c) 2003 Enache Adrian. All rights reserved.
+# This module is free software; you can redistribute and/or modify
+# it under the same terms as Perl itself.
+
+# Based on the original Bytecode.pm module written by Malcolm Beattie.
+
 package B::Bytecode;
 
+our $VERSION = '1.01';
+
 use strict;
-use Carp;
-use B qw(main_cv main_root main_start comppadlist
-        class peekop walkoptree svref_2object cstring walksymtable
-        init_av begin_av end_av
-        SVf_POK SVp_POK SVf_IOK SVp_IOK SVf_NOK SVp_NOK
-        SVf_READONLY GVf_IMPORTED_AV GVf_IMPORTED_CV GVf_IMPORTED_HV
-        GVf_IMPORTED_SV SVTYPEMASK
-       );
-use B::Asmdata qw(@optype @specialsv_name);
-use B::Assembler qw(newasm endasm assemble);
-
-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 () { SVf_POK|SVp_POK }
-
-# Following is SVf_IOK|SVp_IOK
-# XXX Shouldn't be hardwired
-sub IOK () { SVf_IOK|SVp_IOK }
-
-# Following is SVf_NOK|SVp_NOK
-# XXX Shouldn't be hardwired
-sub NOK () { SVf_NOK|SVp_NOK }
-
-# nonexistant flags (see B::GV::bytecode for usage)
-sub GVf_IMPORTED_IO () { 0; }
-sub GVf_IMPORTED_FORM () { 0; }
-
-my ($verbose, $no_assemble, $debug_bc, $debug_cv);
-my @packages;  # list of packages to compile
-
-sub asm (@) {  # print replacement that knows about assembling
-    if ($no_assemble) {
-       print @_;
-    } else {
-       my $buf = join '', @_;
-       assemble($_) for (split /\n/, $buf);
-    }
+use Config;
+use B qw(class main_cv main_root main_start cstring comppadlist
+       defstash curstash begin_av init_av end_av inc_gv warnhook diehook
+       dowarn SVt_PVGV SVt_PVHV OPf_SPECIAL OPf_STACKED OPf_MOD
+       OPpLVAL_INTRO SVf_FAKE SVf_READONLY);
+use B::Asmdata qw(@specialsv_name);
+use B::Assembler qw(asm newasm endasm);
+
+#################################################
+
+my ($varix, $opix, $savebegins, %walked, %files, @cloop);
+my %strtab = (0,0);
+my %svtab = (0,0);
+my %optab = (0,0);
+my %spectab = (0,0);
+my $tix = 1;
+sub asm;
+sub nice ($) { }
+
+BEGIN {
+    my $ithreads = $Config{'useithreads'} eq 'define';
+    eval qq{
+       sub ITHREADS() { $ithreads }
+       sub VERSION() { $] }
+    }; die $@ if $@;
+}
+
+#################################################
+
+sub pvstring {
+    my $pv = shift;
+    defined($pv) ? cstring ($pv."\0") : "\"\"";
 }
 
-sub asmf (@) { # printf replacement that knows about assembling
-    if ($no_assemble) {
-       printf shift(), @_;
-    } else {
-       my $format = shift;
-       my $buf = sprintf $format, @_;
-       assemble($_) for (split /\n/, $buf);
+sub pvix {
+    my $str = pvstring shift;
+    my $ix = $strtab{$str};
+    defined($ix) ? $ix : do {
+       asm "newpv", $str;
+       asm "stpv", $strtab{$str} = $tix;
+       $tix++;
     }
 }
 
-# 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 ($compress_nullops, $omit_seq, $bypass_nullops);
-my %optimise = (compress_nullops       => \$compress_nullops,
-               omit_sequence_numbers   => \$omit_seq,
-               bypass_nullops          => \$bypass_nullops);
-
-my $strip_syntree;     # this is left here in case stripping the
-                       # syntree ever becomes safe again
-                       #       -- BKS, June 2000
-
-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 %strtable;  # maps shared strings to object indices
-               # Filled in at allocation (pvix) time
-
-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) {
-       asm "ldsv $ix\n";
-       $svix = $ix;
+sub B::OP::ix {
+    my $op = shift;
+    my $ix = $optab{$$op};
+    defined($ix) ? $ix : do {
+       nice "[".$op->name." $tix]";
+       asm "newopx", $op->size | $op->type <<7;
+       $optab{$$op} = $opix = $ix = $tix++;
+       $op->bsave($ix);
+       $ix;
     }
 }
 
-sub stsv {
-    my $ix = shift;
-    asm "stsv $ix\n";
-    $svix = $ix;
+sub B::SPECIAL::ix {
+    my $spec = shift;
+    my $ix = $spectab{$$spec};
+    defined($ix) ? $ix : do {
+       nice '['.$specialsv_name[$$spec].']';
+       asm "ldspecsvx", $$spec;
+       $spectab{$$spec} = $varix = $tix++;
+    }
 }
 
-sub set_svix {
-    $svix = shift;
+sub B::SV::ix {
+    my $sv = shift;
+    my $ix = $svtab{$$sv};
+    defined($ix) ? $ix : do {
+       nice '['.class($sv).']';
+       asm "newsvx", $sv->FLAGS;
+       $svtab{$$sv} = $varix = $ix = $tix++;
+       $sv->bsave($ix);
+       $ix;
+    }
+}
+
+sub B::GV::ix {
+    my ($gv,$desired) = @_;
+    my $ix = $svtab{$$gv};
+    defined($ix) ? $ix : do {
+       if ($gv->GP) {
+           my ($svix, $avix, $hvix, $cvix, $ioix, $formix);
+           nice "[GV]";
+           my $name = $gv->STASH->NAME . "::" . $gv->NAME;
+           asm "gv_fetchpvx", cstring $name;
+           $svtab{$$gv} = $varix = $ix = $tix++;
+           asm "sv_flags", $gv->FLAGS;
+           asm "sv_refcnt", $gv->REFCNT;
+           asm "xgv_flags", $gv->GvFLAGS;
+
+           asm "gp_refcnt", $gv->GvREFCNT;
+           asm "load_glob", $ix if $name eq "CORE::GLOBAL::glob";
+           return $ix
+                   unless $desired || desired $gv;
+           $svix = $gv->SV->ix;
+           $avix = $gv->AV->ix;
+           $hvix = $gv->HV->ix;
+
+    # XXX {{{{
+           my $cv = $gv->CV;
+           $cvix = $$cv && defined $files{$cv->FILE} ? $cv->ix : 0;
+           my $form = $gv->FORM;
+           $formix = $$form && defined $files{$form->FILE} ? $form->ix : 0;
+
+           $ioix = $name !~ /STDOUT$/ ? $gv->IO->ix : 0;       
+                                                           # }}}} XXX
+
+           nice "-GV-",
+           asm "ldsv", $varix = $ix unless $ix == $varix;
+           asm "gp_sv", $svix;
+           asm "gp_av", $avix;
+           asm "gp_hv", $hvix;
+           asm "gp_cv", $cvix;
+           asm "gp_io", $ioix;
+           asm "gp_cvgen", $gv->CVGEN;
+           asm "gp_form", $formix;
+           asm "gp_file", pvix $gv->FILE;
+           asm "gp_line", $gv->LINE;
+           asm "formfeed", $svix if $name eq "main::\cL";
+       } else {
+           nice "[GV]";
+           asm "newsvx", $gv->FLAGS;
+           $svtab{$$gv} = $varix = $ix = $tix++;
+           my $stashix = $gv->STASH->ix;
+           $gv->B::PVMG::bsave($ix);
+           asm "xgv_flags", $gv->GvFLAGS;
+           asm "xgv_stash", $stashix;
+       }
+       $ix;
+    }
 }
 
-sub ldop {
-    my $ix = shift;
-    if ($ix != $opix) {
-       asm "ldop $ix\n";
-       $opix = $ix;
+sub B::HV::ix {
+    my $hv = shift;
+    my $ix = $svtab{$$hv};
+    defined($ix) ? $ix : do {
+       my ($ix,$i,@array);
+       my $name = $hv->NAME;
+       if ($name) {
+           nice "[STASH]";
+           asm "gv_stashpvx", cstring $name;
+           asm "sv_flags", $hv->FLAGS;
+           $svtab{$$hv} = $varix = $ix = $tix++;
+           asm "xhv_name", pvix $name;
+           # my $pmrootix = $hv->PMROOT->ix;   # XXX
+           asm "ldsv", $varix = $ix unless $ix == $varix;
+           # asm "xhv_pmroot", $pmrootix;      # XXX
+       } else {
+           nice "[HV]";
+           asm "newsvx", $hv->FLAGS;
+           $svtab{$$hv} = $varix = $ix = $tix++;
+           my $stashix = $hv->SvSTASH->ix;
+           for (@array = $hv->ARRAY) {
+               next if $i = not $i;
+               $_ = $_->ix;
+           }
+           nice "-HV-",
+           asm "ldsv", $varix = $ix unless $ix == $varix;
+           ($i = not $i) ? asm ("newpv", pvstring $_) : asm("hv_store", $_)
+               for @array;
+           asm "xmg_stash", $stashix;
+           asm "xhv_riter", $hv->RITER;
+       }
+       asm "sv_refcnt", $hv->REFCNT;
+       $ix;
     }
 }
 
-sub stop {
-    my $ix = shift;
-    asm "stop $ix\n";
-    $opix = $ix;
+sub B::NULL::ix {
+    my $sv = shift;
+    $$sv ? $sv->B::SV::ix : 0;
 }
 
-sub set_opix {
-    $opix = shift;
-}
+sub B::NULL::opwalk { 0 }
 
-sub pvstring {
-    my $str = shift;
-    if (defined($str)) {
-       return cstring($str . "\0");
-    } else {
-       return '""';
-    }
-}
+#################################################
+
+sub B::NULL::bsave {
+    my ($sv,$ix) = @_;
 
-sub nv {
-    # print full precision
-    my $str = sprintf "%.40f", $_[0];
-    $str =~ s/0+$//;           # remove trailing zeros
-    $str =~ s/\.$/.0/;
-    return $str;
+    nice '-'.class($sv).'-',
+    asm "ldsv", $varix = $ix unless $ix == $varix;
+    asm "sv_refcnt", $sv->REFCNT;
 }
 
-sub saved { $saved{${$_[0]}} }
-sub mark_saved { $saved{${$_[0]}} = 1 }
-sub unmark_saved { $saved{${$_[0]}} = 0 }
+sub B::SV::bsave;
+    *B::SV::bsave = *B::NULL::bsave;
 
-sub debug { $debug_bc = shift }
+sub B::RV::bsave {
+    my ($sv,$ix) = @_;
+    my $rvix = $sv->RV->ix;
+    $sv->B::NULL::bsave($ix);
+    asm "xrv", $rvix;
+}
 
-sub pvix {     # save a shared PV (mainly for COPs)
-    return $strtable{$_[0]} if defined($strtable{$_[0]});
-    asmf "newpv %s\n", pvstring($_[0]);
-    my $ix = $nextix++;
-    $strtable{$_[0]} = $ix;
-    asmf "stpv %d\n", $ix;
-    return $ix;
+sub B::PV::bsave {
+    my ($sv,$ix) = @_;
+    $sv->B::NULL::bsave($ix);
+    asm "newpv", pvstring $sv->PVBM;
+    asm "xpv";
 }
 
-sub B::OBJECT::nyi {
-    my $obj = shift;
-    warn sprintf("bytecode save method for %s (0x%x) not yet implemented\n",
-                class($obj), $$obj);
+sub B::IV::bsave {
+    my ($sv,$ix) = @_;
+    $sv->B::NULL::bsave($ix);
+    asm "xiv", $sv->IVX;
 }
 
-#
-# 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::NV::bsave {
+    my ($sv,$ix) = @_;
+    $sv->B::NULL::bsave($ix);
+    asm "xnv", sprintf "%.40g", $sv->NVX;
 }
 
-sub B::SV::newix {
-    my ($sv, $ix) = @_;
-    asmf "newsv %d\t# %s\n", $sv->FLAGS & SVTYPEMASK, class($sv);
-    stsv($ix);    
+sub B::PVIV::bsave {
+    my ($sv,$ix) = @_;
+    $sv->POK ?
+       $sv->B::PV::bsave($ix):
+    $sv->ROK ?
+       $sv->B::RV::bsave($ix):
+       $sv->B::NULL::bsave($ix);
+    asm "xiv", !ITHREADS && $sv->FLAGS & (SVf_FAKE|SVf_READONLY) ?
+       "0 but true" : $sv->IVX;
 }
 
-sub B::GV::newix {
-    my ($gv, $ix) = @_;
-    my $gvname = $gv->NAME;
-    my $name = cstring($gv->STASH->NAME . "::" . $gvname);
-    asm "gv_fetchpv $name\n";
-    stsv($ix);
+sub B::PVNV::bsave {
+    my ($sv,$ix) = @_;
+    $sv->B::PVIV::bsave($ix);
+    asm "xnv", sprintf "%.40g", $sv->NVX;
 }
 
-sub B::HV::newix {
-    my ($hv, $ix) = @_;
-    my $name = $hv->NAME;
-    if ($name) {
-       # It's a stash
-       asmf "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::PVMG::domagic {
+    my ($sv,$ix) = @_;
+    nice '-MAGICAL-';
+    my @mglist = $sv->MAGIC;
+    my (@mgix, @namix);
+    for (@mglist) {
+       push @mgix, $_->OBJ->ix;
+       push @namix, $_->PTR->ix if $_->LENGTH == B::HEf_SVKEY;
     }
-}
 
-sub B::SPECIAL::newix {
-    my ($sv, $ix) = @_;
-    # Special case. $$sv is not the address of the SV but an
-    # index into svspecialsv_list.
-    asmf "ldspecsv $$sv\t# %s\n", $specialsv_name[$$sv];
-    stsv($ix);
+    nice '-'.class($sv).'-',
+    asm "ldsv", $varix = $ix unless $ix == $varix;
+    for (@mglist) {
+       asm "sv_magic", cstring $_->TYPE;
+       asm "mg_obj", shift @mgix;
+       my $length = $_->LENGTH;
+       if ($length == B::HEf_SVKEY) {
+           asm "mg_namex", shift @namix;
+       } elsif ($length) {
+           asm "newpv", pvstring $_->PTR;
+           asm "mg_name";
+       }
+    }
 }
 
-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);
-    asm "newop $typenum\t# $class\n";
-    stop($ix);
+sub B::PVMG::bsave {
+    my ($sv,$ix) = @_;
+    my $stashix = $sv->SvSTASH->ix;
+    $sv->B::PVNV::bsave($ix);
+    asm "xmg_stash", $stashix;
+    $sv->domagic($ix) if $sv->MAGICAL;
+}
+
+sub B::PVLV::bsave {
+    my ($sv,$ix) = @_;
+    my $targix = $sv->TARG->ix;
+    $sv->B::PVMG::bsave($ix);
+    asm "xlv_targ", $targix;
+    asm "xlv_targoff", $sv->TARGOFF;
+    asm "xlv_targlen", $sv->TARGLEN;
+    asm "xlv_type", $sv->TYPE;
+
+}
+
+sub B::BM::bsave {
+    my ($sv,$ix) = @_;
+    $sv->B::PVMG::bsave($ix);
+    asm "xpv_cur", $sv->CUR;
+    asm "xbm_useful", $sv->USEFUL;
+    asm "xbm_previous", $sv->PREVIOUS;
+    asm "xbm_rare", $sv->RARE;
+}
+
+sub B::IO::bsave {
+    my ($io,$ix) = @_;
+    my $topix = $io->TOP_GV->ix;
+    my $fmtix = $io->FMT_GV->ix;
+    my $bottomix = $io->BOTTOM_GV->ix;
+    $io->B::PVMG::bsave($ix);
+    asm "xio_lines", $io->LINES;
+    asm "xio_page", $io->PAGE;
+    asm "xio_page_len", $io->PAGE_LEN;
+    asm "xio_lines_left", $io->LINES_LEFT;
+    asm "xio_top_name", pvix $io->TOP_NAME;
+    asm "xio_top_gv", $topix;
+    asm "xio_fmt_name", pvix $io->FMT_NAME;
+    asm "xio_fmt_gv", $fmtix;
+    asm "xio_bottom_name", pvix $io->BOTTOM_NAME;
+    asm "xio_bottom_gv", $bottomix;
+    asm "xio_subprocess", $io->SUBPROCESS;
+    asm "xio_type", ord $io->IoTYPE;
+    # asm "xio_flags", ord($io->IoFLAGS) & ~32;                # XXX XXX
+}
+
+sub B::CV::bsave {
+    my ($cv,$ix) = @_;
+    my $stashix = $cv->STASH->ix;
+    my $gvix = $cv->GV->ix;
+    my $padlistix = $cv->PADLIST->ix;
+    my $outsideix = $cv->OUTSIDE->ix;
+    my $constix = $cv->CONST ? $cv->XSUBANY->ix : 0;
+    my $startix = $cv->START->opwalk;
+    my $rootix = $cv->ROOT->ix;
+
+    $cv->B::PVMG::bsave($ix);
+    asm "xcv_stash", $stashix;
+    asm "xcv_start", $startix;
+    asm "xcv_root", $rootix;
+    asm "xcv_xsubany", $constix;
+    asm "xcv_gv", $gvix;
+    asm "xcv_file", pvix $cv->FILE if $cv->FILE;       # XXX AD
+    asm "xcv_padlist", $padlistix;
+    asm "xcv_outside", $outsideix;
+    asm "xcv_flags", $cv->CvFLAGS;
+    asm "xcv_outside_seq", $cv->OUTSIDE_SEQ;
+    asm "xcv_depth", $cv->DEPTH;
+}
+
+sub B::FM::bsave {
+    my ($form,$ix) = @_;
+
+    $form->B::CV::bsave($ix);
+    asm "xfm_lines", $form->LINES;
+}
+
+sub B::AV::bsave {
+    my ($av,$ix) = @_;
+    return $av->B::PVMG::bsave($ix) if $av->MAGICAL;
+    my @array = $av->ARRAY;
+    $_ = $_->ix for @array;
+    my $stashix = $av->SvSTASH->ix;
+
+    nice "-AV-",
+    asm "ldsv", $varix = $ix unless $ix == $varix;
+    asm "av_extend", $av->MAX if $av->MAX >= 0;
+    asm "av_pushx", $_ for @array;
+    asm "sv_refcnt", $av->REFCNT;
+    asm "xmg_stash", $stashix;
+}
+
+sub B::GV::desired {
+    my $gv = shift;
+    my ($cv, $form);
+    $files{$gv->FILE} && $gv->LINE
+    || ${$cv = $gv->CV} && $files{$cv->FILE}
+    || ${$form = $gv->FORM} && $files{$form->FILE}
 }
 
-sub B::OP::walkoptree_debug {
-    my $op = shift;
-    warn(sprintf("walkoptree: %s\n", peekop($op)));
+sub B::HV::bwalk {
+    my $hv = shift;
+    return if $walked{$$hv}++;
+    my %stash = $hv->ARRAY;
+    while (my($k,$v) = each %stash) {
+       if ($v->SvTYPE == SVt_PVGV) {
+           my $hash = $v->HV;
+           if ($$hash && $hash->NAME) {
+               $hash->bwalk;
+           } 
+           $v->ix(1) if desired $v;
+       } else {
+           nice "[prototype]";
+           asm "gv_fetchpvx", cstring $hv->NAME . "::$k";
+           $svtab{$$v} = $varix = $tix;
+           $v->bsave($tix++);
+           asm "sv_flags", $v->FLAGS;
+       }
+    }
 }
 
-sub B::OP::bytecode {
-    my $op = shift;
-    my $next = $op->next;
-    my $nextix;
-    my $sibix = $op->sibling->objix unless $strip_syntree;
-    my $ix = $op->objix;
-    my $type = $op->type;
+######################################################
 
-    if ($bypass_nullops) {
-       $next = $next->next while $$next && $next->type == 0;
-    }
-    $nextix = $next->objix;
-
-    asmf "# %s\n", peekop($op) if $debug_bc;
-    ldop($ix);
-    asm "op_next $nextix\n";
-    asm "op_sibling $sibix\n" unless $strip_syntree;
-    asmf "op_type %s\t# %d\n", "pp_" . $op->name, $type;
-    asmf("op_seq %d\n", $op->seq) unless $omit_seq;
-    if ($type || !$compress_nullops) {
-       asmf "op_targ %d\nop_flags 0x%x\nop_private 0x%x\n",
-           $op->targ, $op->flags, $op->private;
+
+sub B::OP::bsave_thin {
+    my ($op, $ix) = @_;
+    my $next = $op->next;
+    my $nextix = $optab{$$next};
+    $nextix = 0, push @cloop, $op unless defined $nextix;
+    if ($ix != $opix) {
+       nice '-'.$op->name.'-',
+       asm "ldop", $opix = $ix;
     }
+    asm "op_next", $nextix;
+    asm "op_targ", $op->targ if $op->type;             # tricky
+    asm "op_flags", $op->flags;
+    asm "op_private", $op->private;
 }
 
-sub B::UNOP::bytecode {
-    my $op = shift;
-    my $firstix = $op->first->objix unless $strip_syntree;
-    $op->B::OP::bytecode;
-    if (($op->type || !$compress_nullops) && !$strip_syntree) {
-       asm "op_first $firstix\n";
+sub B::OP::bsave;
+    *B::OP::bsave = *B::OP::bsave_thin;
+
+sub B::UNOP::bsave {
+    my ($op, $ix) = @_;
+    my $name = $op->name;
+    my $flags = $op->flags;
+    my $first = $op->first;
+    my $firstix = 
+       $name =~ /fl[io]p/
+                       # that's just neat
+    || (!ITHREADS && $name eq 'regcomp')
+                       # trick for /$a/o in pp_regcomp
+    || $name eq 'rv2sv'
+           && $op->flags & OPf_MOD     
+           && $op->private & OPpLVAL_INTRO
+                       # change #18774 made my life hard
+    ?  $first->ix
+    :  0;
+
+    $op->B::OP::bsave($ix);
+    asm "op_first", $firstix;
+}
+
+sub B::BINOP::bsave {
+    my ($op, $ix) = @_;
+    if ($op->name eq 'aassign' && $op->private & B::OPpASSIGN_HASH()) {
+       my $last = $op->last;
+       my $lastix = do {
+           local *B::OP::bsave = *B::OP::bsave_fat;
+           local *B::UNOP::bsave = *B::UNOP::bsave_fat;
+           $last->ix;
+       };
+       asm "ldop", $lastix unless $lastix == $opix;
+       asm "op_targ", $last->targ;
+       $op->B::OP::bsave($ix);
+       asm "op_last", $lastix;
+    } else {
+       $op->B::OP::bsave($ix);
     }
 }
 
-sub B::LOGOP::bytecode {
-    my $op = shift;
-    my $otherix = $op->other->objix;
-    $op->B::UNOP::bytecode;
-    asm "op_other $otherix\n";
-}
+# not needed if no pseudohashes
 
-sub B::SVOP::bytecode {
-    my $op = shift;
-    my $sv = $op->sv;
-    my $svix = $sv->objix;
-    $op->B::OP::bytecode;
-    asm "op_sv $svix\n";
-    $sv->bytecode;
-}
+*B::BINOP::bsave = *B::OP::bsave if VERSION >= 5.009;
 
-sub B::PADOP::bytecode {
-    my $op = shift;
-    my $padix = $op->padix;
-    $op->B::OP::bytecode;
-    asm "op_padix $padix\n";
-}
+# deal with sort / formline 
 
-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->name eq "trans") {
-       my @shorts = unpack("s256", $pv); # assembler handles endianness
-       asm "op_pv_tr ", join(",", @shorts), "\n";
+sub B::LISTOP::bsave {
+    my ($op, $ix) = @_;
+    my $name = $op->name;
+    sub blocksort() { OPf_SPECIAL|OPf_STACKED }
+    if ($name eq 'sort' && ($op->flags & blocksort) == blocksort) {
+       my $first = $op->first;
+       my $pushmark = $first->sibling;
+       my $rvgv = $pushmark->first;
+       my $leave = $rvgv->first;
+
+       my $leaveix = $leave->ix;
+
+       my $rvgvix = $rvgv->ix;
+       asm "ldop", $rvgvix unless $rvgvix == $opix;
+       asm "op_first", $leaveix;
+
+       my $pushmarkix = $pushmark->ix;
+       asm "ldop", $pushmarkix unless $pushmarkix == $opix;
+       asm "op_first", $rvgvix;
+
+       my $firstix = $first->ix;
+       asm "ldop", $firstix unless $firstix == $opix;
+       asm "op_sibling", $pushmarkix;
+
+       $op->B::OP::bsave($ix);
+       asm "op_first", $firstix;
+    } elsif ($name eq 'formline') {
+       $op->B::UNOP::bsave_fat($ix);
     } else {
-       asmf "newpv %s\nop_pv\n", pvstring($pv);
+       $op->B::OP::bsave($ix);
     }
 }
 
-sub B::BINOP::bytecode {
-    my $op = shift;
-    my $lastix = $op->last->objix unless $strip_syntree;
-    $op->B::UNOP::bytecode;
-    if (($op->type || !$compress_nullops) && !$strip_syntree) {
-       asm "op_last $lastix\n";
-    }
-}
+# fat versions
 
-sub B::LISTOP::bytecode {
-    my $op = shift;
-    my $children = $op->children unless $strip_syntree;
-    $op->B::BINOP::bytecode;
-    if (($op->type || !$compress_nullops) && !$strip_syntree) {
-       asm "op_children $children\n";
-    }
-}
+sub B::OP::bsave_fat {
+    my ($op, $ix) = @_;
+    my $siblix = $op->sibling->ix;
 
-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;
-    asm "op_redoop $redoopix\nop_nextop $nextopix\nop_lastop $lastopix\n";
+    $op->B::OP::bsave_thin($ix);
+    asm "op_sibling", $siblix;
+    # asm "op_seq", -1;                        XXX don't allocate OPs piece by piece
 }
 
-sub B::COP::bytecode {
-    my $op = shift;
-    my $file = $op->file;
-    my $line = $op->line;
-    if ($debug_bc) { # do this early to aid debugging
-       asmf "# line %s:%d\n", $file, $line;
-    }
-    my $stashpv = $op->stashpv;
-    my $warnings = $op->warnings;
-    my $warningsix = $warnings->objix;
-    my $labelix = pvix($op->label);
-    my $stashix = pvix($stashpv);
-    my $fileix = pvix($file);
-    $warnings->bytecode;
-    $op->B::OP::bytecode;
-    asmf <<"EOT", $labelix, $stashix, $op->cop_seq, $fileix, $op->arybase;
-cop_label %d
-cop_stashpv %d
-cop_seq %d
-cop_file %d
-cop_arybase %d
-cop_line $line
-cop_warnings $warningsix
-EOT
-}
-
-sub B::PMOP::bytecode {
-    my $op = shift;
-    my $replroot = $op->pmreplroot;
-    my $replrootix = $replroot->objix;
-    my $replstartix = $op->pmreplstart->objix;
-    my $opname = $op->name;
-    # pmnext is corrupt in some PMOPs (see misc.t for example)
-    #my $pmnextix = $op->pmnext->objix;
-
-    if ($$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 ($opname eq "pushre") {
-           $replroot->bytecode;
-       } else {
-           walkoptree($replroot, "bytecode");
-       }
-    }
-    $op->B::LISTOP::bytecode;
-    if ($opname eq "pushre") {
-       asmf "op_pmreplrootgv $replrootix\n";
-    } else {
-       asm "op_pmreplroot $replrootix\nop_pmreplstart $replstartix\n";
-    }
-    my $re = pvstring($op->precomp);
-    # op_pmnext omitted since a perl bug means it's sometime corrupt
-    asmf <<"EOT", $op->pmflags, $op->pmpermflags;
-op_pmflags 0x%x
-op_pmpermflags 0x%x
-newpv $re
-pregcomp
-EOT
-}
+sub B::UNOP::bsave_fat {
+    my ($op,$ix) = @_;
+    my $firstix = $op->first->ix;
 
-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);
-    asm "sv_refcnt $refcnt\nsv_flags $flags\n";
-    mark_saved($sv);
+    $op->B::OP::bsave($ix);
+    asm "op_first", $firstix;
 }
 
-sub B::PV::bytecode {
-    my $sv = shift;
-    return if saved($sv);
-    $sv->B::SV::bytecode;
-    asmf("newpv %s\nxpv\n", pvstring($sv->PV)) if $sv->FLAGS & POK;
-}
+sub B::BINOP::bsave_fat {
+    my ($op,$ix) = @_;
+    my $last = $op->last;
+    my $lastix = $op->last->ix;
+    if (VERSION < 5.009 && $op->name eq 'aassign' && $last->name eq 'null') {
+       asm "ldop", $lastix unless $lastix == $opix;
+       asm "op_targ", $last->targ;
+    }
 
-sub B::IV::bytecode {
-    my $sv = shift;
-    return if saved($sv);
-    my $iv = $sv->IVX;
-    $sv->B::SV::bytecode;
-    asmf "%s $iv\n", $sv->needs64bits ? "xiv64" : "xiv32" if $sv->FLAGS & IOK; # could be PVNV
+    $op->B::UNOP::bsave($ix);
+    asm "op_last", $lastix;
 }
 
-sub B::NV::bytecode {
-    my $sv = shift;
-    return if saved($sv);
-    $sv->B::SV::bytecode;
-    asmf "xnv %s\n", nv($sv->NVX);
-}
+sub B::LOGOP::bsave {
+    my ($op,$ix) = @_;
+    my $otherix = $op->other->ix;
 
-sub B::RV::bytecode {
-    my $sv = shift;
-    return if saved($sv);
-    my $rv = $sv->RV;
-    my $rvix = $rv->objix;
-    $rv->bytecode;
-    $sv->B::SV::bytecode;
-    asm "xrv $rvix\n";
+    $op->B::UNOP::bsave($ix);
+    asm "op_other", $otherix;
 }
 
-sub B::PVIV::bytecode {
-    my $sv = shift;
-    return if saved($sv);
-    my $iv = $sv->IVX;
-    $sv->B::PV::bytecode;
-    asmf "%s $iv\n", $sv->needs64bits ? "xiv64" : "xiv32";
-}
+sub B::PMOP::bsave {
+    my ($op,$ix) = @_;
+    my ($rrop, $rrarg, $rstart);
 
-sub B::PVNV::bytecode {
-    my $sv = shift;
-    my $flag = shift || 0;
-    # 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;
-       asmf "xnv %s\n", nv($sv->NVX);
-       if ($flag == 1) {
-           $pv .= "\0" . $sv->TABLE;
-           asmf "newpv %s\npv_cur %d\nxpv\n", pvstring($pv),length($pv)-257;
-       } else {
-           asmf("newpv %s\nxpv\n", pvstring($pv)) if $sv->FLAGS & POK;
+    # my $pmnextix = $op->pmnext->ix;  # XXX
+
+    if (ITHREADS) {
+       if ($op->name eq 'subst') {
+           $rrop = "op_pmreplroot";
+           $rrarg = $op->pmreplroot->ix;
+           $rstart = $op->pmreplstart->ix;
+       } elsif ($op->name eq 'pushre') {
+           $rrop = "op_pmreplrootpo";
+           $rrarg = $op->pmreplroot;
        }
+       $op->B::BINOP::bsave($ix);
+       asm "op_pmstashpv", pvix $op->pmstashpv;
+    } else {
+       $rrop = "op_pmreplrootgv";
+       $rrarg = $op->pmreplroot->ix;
+       $rstart = $op->pmreplstart->ix if $op->name eq 'subst';
+       my $stashix = $op->pmstash->ix;
+       $op->B::BINOP::bsave($ix);
+       asm "op_pmstash", $stashix;
     }
-}
 
-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);
-    asm "xmg_stash $stashix\n";
-    foreach $mg (@mgchain) {
-       asmf "sv_magic %s\nmg_obj %d\nnewpv %s\nmg_pv\n",
-           cstring($mg->TYPE), shift(@mgobjix), pvstring($mg->PTR);
-    }
-}
+    asm $rrop, $rrarg if $rrop;
+    asm "op_pmreplstart", $rstart if $rstart;
 
-sub B::PVLV::bytecode {
-    my $sv = shift;
-    return if saved($sv);
-    $sv->B::PVMG::bytecode;
-    asmf <<'EOT', $sv->TARGOFF, $sv->TARGLEN, cstring($sv->TYPE);
-xlv_targoff %d
-xlv_targlen %d
-xlv_type %s
-EOT
+    asm "op_pmflags", $op->pmflags;
+    asm "op_pmpermflags", $op->pmpermflags;
+    asm "op_pmdynflags", $op->pmdynflags;
+    # asm "op_pmnext", $pmnextix;      # XXX
+    asm "newpv", pvstring $op->precomp;
+    asm "pregcomp";
 }
 
-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);
-    asmf "xbm_useful %d\nxbm_previous %d\nxbm_rare %d\n",
-       $sv->USEFUL, $sv->PREVIOUS, $sv->RARE;
-}
+sub B::SVOP::bsave {
+    my ($op,$ix) = @_;
+    my $svix = $op->sv->ix;
 
-sub empty_gv { # is a GV empty except for imported stuff?
-    my $gv = shift;
-
-    return 0 if ($gv->SV->FLAGS & SVTYPEMASK); # sv not SVt_NULL
-    my @subfield_names = qw(AV HV CV FORM IO);
-    @subfield_names = grep {;
-                               no strict 'refs';
-                               !($gv->GvFLAGS & ${\"GVf_IMPORTED_$_"}->()) && ${$gv->$_()};
-                       } @subfield_names;
-    return scalar @subfield_names;
+    $op->B::OP::bsave($ix);
+    asm "op_sv", $svix;
 }
 
-sub B::GV::bytecode {
-    my $gv = shift;
-    return if saved($gv);
-    return unless grep { $_ eq $gv->STASH->NAME; } @packages;
-    return if $gv->NAME =~ m/^\(/;     # ignore overloads - they'll be rebuilt
-    my $ix = $gv->objix;
-    mark_saved($gv);
-    ldsv($ix);
-    asmf <<"EOT", $gv->FLAGS, $gv->GvFLAGS;
-sv_flags 0x%x
-xgv_flags 0x%x
-EOT
-    my $refcnt = $gv->REFCNT;
-    asmf("sv_refcnt_add %d\n", $refcnt - 1) if $refcnt > 1;
-    return if $gv->is_empty;
-    asmf <<"EOT", $gv->LINE, pvix($gv->FILE);
-gp_line %d
-gp_file %d
-EOT
-    my $gvname = $gv->NAME;
-    my $name = cstring($gv->STASH->NAME . "::" . $gvname);
-    my $egv = $gv->EGV;
-    my $egvix = $egv->objix;
-    my $gvrefcnt = $gv->GvREFCNT;
-    asmf("gp_refcnt_add %d\n", $gvrefcnt - 1) if $gvrefcnt > 1;
-    if ($gvrefcnt > 1 &&  $ix != $egvix) {
-       asm "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 FORM IO);
-           @subfield_names = grep {;
-                                       no strict 'refs';
-                                       !($gv->GvFLAGS & ${\"GVf_IMPORTED_$_"}->());
-                               } @subfield_names;
-           my @subfields = map($gv->$_(), @subfield_names);
-           my @ixes = map($_->objix, @subfields);
-           # Reset sv register for $gv
-           ldsv($ix);
-           for ($i = 0; $i < @ixes; $i++) {
-               asmf "gp_%s %d\n", lc($subfield_names[$i]), $ixes[$i];
-           }
-           # Now save all the subfields
-           my $sv;
-           foreach $sv (@subfields) {
-               $sv->bytecode;
-           }
-       }
-    }
-}
+sub B::PADOP::bsave {
+    my ($op,$ix) = @_;
 
-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.
-       my @contents = $hv->ARRAY;
-       my ($i, @ixes);
-       for ($i = 1; $i < @contents; $i += 2) {
-           push(@ixes, $contents[$i]->objix);
-       }
-       for ($i = 1; $i < @contents; $i += 2) {
-           $contents[$i]->bytecode;
-       }
-       ldsv($ix);
-       for ($i = 0; $i < @contents; $i += 2) {
-           asmf("newpv %s\nhv_store %d\n",
-                  pvstring($contents[$i]), $ixes[$i / 2]);
-       }
-       asmf "sv_refcnt %d\nsv_flags 0x%x\n", $hv->REFCNT, $hv->FLAGS;
-    }
+    $op->B::OP::bsave($ix);
+    asm "op_padix", $op->padix;
 }
 
-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);
-    asmf "sv_flags 0x%x\n", $av->FLAGS & ~SVf_READONLY; # SvREADONLY_off($av) in case PADCONST
-    asmf "xav_flags 0x%x\nxav_max -1\nxav_fill -1\n", $av->AvFLAGS;
-    if ($fill > -1) {
-       my $elix;
-       foreach $elix (@ixes) {
-           asm "av_push $elix\n";
-       }
+sub B::PVOP::bsave {
+    my ($op,$ix) = @_;
+    $op->B::OP::bsave($ix);
+    return unless my $pv = $op->pv;
+
+    if ($op->name eq 'trans') {
+        asm "op_pv_tr", join ',', length($pv)/2, unpack("s*", $pv);
     } else {
-       if ($max > -1) {
-           asm "av_extend $max\n";
-       }
-    }
-    asmf "sv_flags 0x%x\n", $av->FLAGS; # restore flags from above
-}
-
-sub B::CV::bytecode {
-    my $cv = shift;
-    return if saved($cv);
-    return if ${$cv->GV} && ($cv->GV->GvFLAGS & GVf_IMPORTED_CV);
-    my $fileix = pvix($cv->FILE);
-    my $ix = $cv->objix;
-    $cv->B::PVMG::bytecode;
-    my $i;
-    my @subfield_names = qw(ROOT START STASH GV 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 ($$root) {
-       walkoptree($root, "bytecode");
-    }
-    # Reset sv register for $cv (since above ->objix calls stomped on it)
-    ldsv($ix);
-    for ($i = 0; $i < @ixes; $i++) {
-       asmf "xcv_%s %d\n", lc($subfield_names[$i]), $ixes[$i];
-    }
-    asmf "xcv_depth %d\nxcv_flags 0x%x\n", $cv->DEPTH, $cv->CvFLAGS;
-    asmf "xcv_file %d\n", $fileix;
-    # 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;
+        asm "newpv", pvstring $pv;
+        asm "op_pv";
     }
 }
 
-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);
-    asm "xio_top_gv $top_gvix\n";
-    asm "xio_fmt_gv $fmt_gvix\n";
-    asm "xio_bottom_gv $bottom_gvix\n";
-    my $field;
-    foreach $field (qw(TOP_NAME FMT_NAME BOTTOM_NAME)) {
-       asmf "newpv %s\nxio_%s\n", pvstring($io->$field()), lc($field);
-    }
-    foreach $field (qw(LINES PAGE PAGE_LEN LINES_LEFT SUBPROCESS)) {
-       asmf "xio_%s %d\n", lc($field), $io->$field();
-    }
-    asmf "xio_type %s\nxio_flags 0x%x\n", cstring($io->IoTYPE), $io->IoFLAGS;
-    $top_gv->bytecode;
-    $fmt_gv->bytecode;
-    $bottom_gv->bytecode;
-}
+sub B::LOOP::bsave {
+    my ($op,$ix) = @_;
+    my $nextix = $op->nextop->ix;
+    my $lastix = $op->lastop->ix;
+    my $redoix = $op->redoop->ix;
 
-sub B::SPECIAL::bytecode {
-    # nothing extra needs doing
+    $op->B::BINOP::bsave($ix);
+    asm "op_redoop", $redoix;
+    asm "op_nextop", $nextix;
+    asm "op_lastop", $lastix;
 }
 
-sub bytecompile_object {
-    for my $sv (@_) {
-       svref_2object($sv)->bytecode;
+sub B::COP::bsave {
+    my ($cop,$ix) = @_;
+    my $warnix = $cop->warnings->ix;
+    my $ioix = $cop->io->ix;
+    if (ITHREADS) {
+       $cop->B::OP::bsave($ix);
+       asm "cop_stashpv", pvix $cop->stashpv;
+       asm "cop_file", pvix $cop->file;
+    } else {
+       my $stashix = $cop->stash->ix;
+       my $fileix = $cop->filegv->ix(1);
+       $cop->B::OP::bsave($ix);
+       asm "cop_stash", $stashix;
+       asm "cop_filegv", $fileix;
     }
+    asm "cop_label", pvix $cop->label if $cop->label;  # XXX AD
+    asm "cop_seq", $cop->cop_seq;
+    asm "cop_arybase", $cop->arybase;
+    asm "cop_line", $cop->line;
+    asm "cop_warnings", $warnix;
+    asm "cop_io", $ioix;
 }
 
-sub B::GV::bytecodecv {
-    my $gv = shift;
-    my $cv = $gv->CV;
-    if ($$cv && !saved($cv) && !($gv->FLAGS & GVf_IMPORTED_CV)) {
-       if ($debug_cv) {
-           warn sprintf("saving extra CV &%s::%s (0x%x) from GV 0x%x\n",
-                        $gv->STASH->NAME, $gv->NAME, $$cv, $$gv);
+sub B::OP::opwalk {
+    my $op = shift;
+    my $ix = $optab{$$op};
+    defined($ix) ? $ix : do {
+       my $ix;
+       my @oplist = $op->oplist;
+       push @cloop, undef;
+       $ix = $_->ix while $_ = pop @oplist;
+       while ($_ = pop @cloop) {
+           asm "ldop", $optab{$$_};
+           asm "op_next", $optab{${$_->next}};
        }
-       $gv->bytecode;
+       $ix;
     }
 }
 
-sub save_call_queues {
-    if (begin_av()->isa("B::AV")) {    # this is just to save 'use Foo;' calls
-       for my $cv (begin_av()->ARRAY) {
-           next unless grep { $_ eq $cv->STASH->NAME; } @packages;
-           my $op = $cv->START;
-OPLOOP:
-           while ($$op) {
-               if ($op->name eq 'require') { # save any BEGIN that does a require
-                   $cv->bytecode;
-                   asmf "push_begin %d\n", $cv->objix;
-                   last OPLOOP;
+#################################################
+
+sub save_cq {
+    my $av;
+    if (($av=begin_av)->isa("B::AV")) {
+       if ($savebegins) {
+           for ($av->ARRAY) {
+               next unless $_->FILE eq $0;
+               asm "push_begin", $_->ix;
+           }
+       } else {
+           for ($av->ARRAY) {
+               next unless $_->FILE eq $0;
+               # XXX BEGIN { goto A while 1; A: }
+               for (my $op = $_->START; $$op; $op = $op->next) {
+                   next unless $op->name eq 'require' || 
+                       # this kludge needed for tests
+                       $op->name eq 'gv' && do {
+                           my $gv = class($op) eq 'SVOP' ?
+                               $op->gv :
+                               (($_->PADLIST->ARRAY)[1]->ARRAY)[$op->padix];
+                           $$gv && $gv->NAME =~ /use_ok|plan/
+                       };
+                   asm "push_begin", $_->ix;
+                   last;
                }
-               $op = $op->next;
            }
        }
     }
-    if (init_av()->isa("B::AV")) {
-       for my $cv (init_av()->ARRAY) {
-           next unless grep { $_ eq $cv->STASH->NAME; } @packages;
-           $cv->bytecode;
-           asmf "push_init %d\n", $cv->objix;
+    if (($av=init_av)->isa("B::AV")) {
+       for ($av->ARRAY) {
+           next unless $_->FILE eq $0;
+           asm "push_init", $_->ix;
        }
     }
-    if (end_av()->isa("B::AV")) {
-       for my $cv (end_av()->ARRAY) {
-           next unless grep { $_ eq $cv->STASH->NAME; } @packages;
-           $cv->bytecode;
-           asmf "push_end %d\n", $cv->objix;
+    if (($av=end_av)->isa("B::AV")) {
+       for ($av->ARRAY) {
+           next unless $_->FILE eq $0;
+           asm "push_end", $_->ix;
        }
     }
 }
 
-sub symwalk {
-    no strict 'refs';
-    my $ok = 1 if grep { (my $name = $_[0]) =~ s/::$//; $_ eq $name;} @packages;
-    if (grep { /^$_[0]/; } @packages) {
-       walksymtable(\%{"$_[0]"}, "bytecodecv", \&symwalk, $_[0]);
-    }
-    warn "considering $_[0] ... " . ($ok ? "accepted\n" : "rejected\n")
-       if $debug_bc;
-    $ok;
-}
-
-sub bytecompile_main {
-    my $curpad = (comppadlist->ARRAY)[1];
-    my $curpadix = $curpad->objix;
-    $curpad->bytecode;
-    save_call_queues();
-    walkoptree(main_root, "bytecode") unless ref(main_root) eq "B::NULL";
-    warn "done main program, now walking symbol table\n" if $debug_bc;
-    if (@packages) {
-       no strict qw(refs);
-       walksymtable(\%{"main::"}, "bytecodecv", \&symwalk);
-    } else {
-       die "No packages requested for compilation!\n";
-    }
-    asmf "main_root %d\n", main_root->objix;
-    asmf "main_start %d\n", main_start->objix;
-    asmf "curpad $curpadix\n";
-    # XXX Do min_intro_pending and max_intro_pending matter?
-}
-
 sub compile {
-    my @options = @_;
-    my ($option, $opt, $arg);
-    open(OUT, ">&STDOUT");
-    binmode OUT;
-    select OUT;
-  OPTION:
-    while ($option = shift @options) {
-       if ($option =~ /^-(.)(.*)/) {
-           $opt = $1;
-           $arg = $2;
+    my ($head, $scan, $T_inhinc, $keep_syn);
+    my $cwd = '';
+    $files{$0} = 1;
+    sub keep_syn {
+       $keep_syn = 1;
+       *B::OP::bsave = *B::OP::bsave_fat;
+       *B::UNOP::bsave = *B::UNOP::bsave_fat;
+       *B::BINOP::bsave = *B::BINOP::bsave_fat;
+       *B::LISTOP::bsave = *B::LISTOP::bsave_fat;
+    }
+    sub bwarn { print STDERR "Bytecode.pm: @_\n" }
+
+    for (@_) {
+       if (/^-S/) {
+           *newasm = *endasm = sub { };
+           *asm = sub { print "    @_\n" };
+           *nice = sub ($) { print "\n@_\n" };
+       } elsif (/^-H/) {
+           require ByteLoader;
+           $head = "#! $^X\nuse ByteLoader $ByteLoader::VERSION;\n";
+       } elsif (/^-k/) {
+           keep_syn;
+       } elsif (/^-o(.*)$/) {
+           open STDOUT, ">$1" or die "open $1: $!";
+       } elsif (/^-f(.*)$/) {
+           $files{$1} = 1;
+       } elsif (/^-s(.*)$/) {
+           $scan = length($1) ? $1 : $0;
+       } elsif (/^-b/) {
+           $savebegins = 1;
+    # this is here for the testsuite
+       } elsif (/^-TI/) {
+           $T_inhinc = 1;
+       } elsif (/^-TF(.*)/) {
+           my $thatfile = $1;
+           *B::COP::file = sub { $thatfile };
        } else {
-           unshift @options, $option;
-           last OPTION;
+           bwarn "Ignoring '$_' option";
        }
-       if ($opt eq "-" && $arg eq "-") {
-           shift @options;
-           last OPTION;
-       } elsif ($opt eq "o") {
-           $arg ||= shift @options;
-           open(OUT, ">$arg") or return "$arg: $!\n";
-           binmode OUT;
-       } elsif ($opt eq "a") {
-           $arg ||= shift @options;
-           open(OUT, ">>$arg") or return "$arg: $!\n";
-           binmode OUT;
-       } elsif ($opt eq "D") {
-           $arg ||= shift @options;
-           foreach $arg (split(//, $arg)) {
-               if ($arg eq "b") {
-                   $| = 1;
-                   debug(1);
-               } elsif ($arg eq "o") {
-                   B->debug(1);
-               } elsif ($arg eq "a") {
-                   B::Assembler::debug(1);
-               } elsif ($arg eq "C") {
-                   $debug_cv = 1;
+    }
+    if ($scan) {
+       my $f;
+       if (open $f, $scan) {
+           while (<$f>) {
+               /^#\s*line\s+\d+\s+("?)(.*)\1/ and $files{$2} = 1;
+               /^#/ and next;
+               if (/\bgoto\b\s*[^&]/ && !$keep_syn) {
+                   bwarn "keeping the syntax tree: \"goto\" op found";
+                   keep_syn;
                }
            }
-       } elsif ($opt eq "v") {
-           $verbose = 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 {
+           bwarn "cannot rescan '$scan'";
+       }
+       close $f;
+    }
+    binmode STDOUT;
+    return sub {
+       print $head if $head;
+       newasm sub { print @_ };
+
+       defstash->bwalk;
+       asm "main_start", main_start->opwalk;
+       asm "main_root", main_root->ix;
+       asm "main_cv", main_cv->ix;
+       asm "curpad", (comppadlist->ARRAY)[1]->ix;
+
+       asm "signal", cstring "__WARN__"                # XXX
+           if warnhook->ix;
+       asm "incav", inc_gv->AV->ix if $T_inhinc;
+       save_cq;
+       asm "incav", inc_gv->AV->ix if $T_inhinc;
+       asm "dowarn", dowarn;
+
+       {
+           no strict 'refs';
+           nice "<DATA>";
+           my $dh = *{defstash->NAME."::DATA"};
+           unless (eof $dh) {
+               local undef $/;
+               asm "data", ord 'D';
+               print <$dh>;
            } 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) {
-               $bypass_nullops = 1;
+               asm "ret";
            }
-           if ($arg >= 1) {
-               $compress_nullops = 1;
-               $omit_seq = 1;
-           }
-       } elsif ($opt eq "P") {
-           $arg ||= shift @options;
-           push @packages, $arg;
-       } else {
-           warn qq(ignoring unknown option "$opt$arg"\n);
        }
-    }
-    if (! @packages) {
-       warn "No package specified for compilation, assuming main::\n";
-       @packages = qw(main);
-    }
-    if (@options) {
-       die "Extraneous options left on B::Bytecode commandline: @options\n";
-    } else {
-       return sub { 
-           newasm(\&apr) unless $no_assemble;
-           bytecompile_main();
-           endasm() unless $no_assemble;
-       };
+
+       endasm;
     }
 }
 
-sub apr { print @_; }
-
 1;
 
-__END__
-
 =head1 NAME
 
 B::Bytecode - Perl compiler's bytecode backend
 
 =head1 SYNOPSIS
 
-       perl -MO=Bytecode[,OPTIONS] foo.pl
+B<perl -MO=Bytecode>[B<,-H>][B<,-o>I<script.plc>] I<script.pl>
 
 =head1 DESCRIPTION
 
-This compiler backend takes Perl source and generates a
-platform-independent bytecode encapsulating code to load the
-internal structures perl uses to run your program. When the
-generated bytecode is loaded in, your program is ready to run,
-reducing the time which perl would have taken to load and parse
-your program into its internal semi-compiled form. That means that
-compiling with this backend will not help improve the runtime
-execution speed of your program but may improve the start-up time.
-Depending on the environment in which your program runs this may
-or may not be a help.
-
-The resulting bytecode can be run with a special byteperl executable
-or (for non-main programs) be loaded via the C<byteload_fh> function
-in the F<B> module.
-
-=head1 OPTIONS
-
-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.
-
-=over 4
-
-=item B<-ofilename>
-
-Output to filename instead of STDOUT.
+Compiles a Perl script into a bytecode format that could be loaded
+later by the ByteLoader module and executed as a regular Perl script.
 
-=item B<-afilename>
+=head1 EXAMPLE
 
-Append output to filename.
+    $ perl -MO=Bytecode,-H,-ohi -e 'print "hi!\n"'
+    $ perl hi
+    hi!
 
-=item B<-->
-
-Force end of options.
+=head1 OPTIONS
 
-=item B<-f>
+=over 4
 
-Force optimisations on or off one at a time. Each can be preceded
-by B<no-> to turn the option off (e.g. B<-fno-compress-nullops>).
+=item B<-b>
 
-=item B<-fcompress-nullops>
+Save all the BEGIN blocks. Normally only BEGIN blocks that C<require>
+other files (ex. C<use Foo;>) are saved.
 
-Only fills in the necessary fields of ops which have
-been optimised away by perl's internal compiler.
+=item B<-H>
 
-=item B<-fomit-sequence-numbers>
+prepend a C<use ByteLoader VERSION;> line to the produced bytecode.
 
-Leaves out code to fill in the op_seq field of all ops
-which is only used by perl's internal compiler.
+=item B<-k>
 
-=item B<-fbypass-nullops>
+keep the syntax tree - it is stripped by default.
 
-If op->op_next ever points to a NULLOP, replaces the op_next field
-with the first non-NULLOP in the path of execution.
+=item B<-o>I<outfile>
 
-=item B<-On>
+put the bytecode in <outfile> instead of dumping it to STDOUT.
 
-Optimisation level (n = 0, 1, 2, ...). B<-O> means B<-O1>.
-B<-O1> sets B<-fcompress-nullops> B<-fomit-sequence numbers>.
-B<-O2> adds B<-fbypass-nullops>.
+=item B<-s>
 
-=item B<-D>
+scan the script for C<# line ..> directives and for <goto LABEL>
+expressions. When gotos are found keep the syntax tree.
 
-Debug options (concatenated or separate flags like C<perl -D>).
+=back
 
-=item B<-Do>
+=head1 KNOWN BUGS
 
-Prints each OP as it's processed.
+=over 4
 
-=item B<-Db>
+=item *
 
-Print debugging information about bytecompiler progress.
+C<BEGIN { goto A: while 1; A: }> won't even compile.
 
-=item B<-Da>
+=item *
 
-Tells the (bytecode) assembler to include source assembler lines
-in its output as bytecode comments.
+C<?...?> and C<reset> do not work as expected.
 
-=item B<-DC>
+=item *
 
-Prints each CV taken from the final symbol tree walk.
+variables in C<(?{ ... })> constructs are not properly scoped.
 
-=item B<-S>
+=item *
 
-Output (bytecode) assembler source rather than piping it
-through the assembler and outputting bytecode.
+scripts that use source filters will fail miserably. 
 
-=item B<-Ppackage>
-  
-Stores package in the output.
-  
 =back
 
-=head1 EXAMPLES
+=head1 NOTICE
 
-    perl -MO=Bytecode,-O6,-ofoo.plc,-Pmain foo.pl
+There are also undocumented bugs and options.
 
-    perl -MO=Bytecode,-S,-Pmain foo.pl > foo.S
-    assemble foo.S > foo.plc
-
-Note that C<assemble> lives in the C<B> subdirectory of your perl
-library directory. The utility called perlcc may also be used to 
-help make use of this compiler.
-
-    perl -MO=Bytecode,-PFoo,-oFoo.pmc Foo.pm
-
-=head1 BUGS
-
-Output is still huge and there are still occasional crashes during
-either compilation or ByteLoading. Current status: experimental.
+THIS CODE IS HIGHLY EXPERIMENTAL. USE AT YOUR OWN RISK.
 
 =head1 AUTHORS
 
-Malcolm Beattie, C<mbeattie@sable.ox.ac.uk>
-Benjamin Stuhl, C<sho_pi@hotmail.com>
+Originally written by Malcolm Beattie <mbeattie@sable.ox.ac.uk> and
+modified by Benjamin Stuhl <sho_pi@hotmail.com>.
+
+Rewritten by Enache Adrian <enache@rdslink.ro>, 2003 a.d.
 
 =cut