This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Consolidate any single pad ops after a padrange
authorDavid Mitchell <davem@iabyn.com>
Fri, 2 Nov 2012 16:18:20 +0000 (16:18 +0000)
committerDavid Mitchell <davem@iabyn.com>
Sat, 10 Nov 2012 13:39:33 +0000 (13:39 +0000)
Given something like
    my ($a,$b); my $c; my $d;
then after having detected that we can create a padrange op for $a,$b,
extend it to include $c,$d too.

Together with the previous commit that consolidates adjacent padrange
ops, this means that any contiguous sequence of void 'my' declarations
that starts with a list (i.e. my ($x,..) rather than my $x) will
all be compressed into a single padrange op. For example

    my ($a,$b);
    my @c;
    my %d;
    my ($e,@f);

becomes the two ops

    padrange[$a;$b;@c;%d;$e;@f]
    nextstate

The restriction on the first 'my' being a list is that we only ever
convert pushmarks into padranges, to keep things manageable (both for
compiling and for Deparse). This simply means that

    my $x; my ($a,$b); my @c; my %d; my ($e,@f)

becomes

    padsv[$x]
    nextstate
    padrange[$a;$b;@c;%d;$e;@f]
    nextstate

ext/B/t/optree_misc.t
op.c

index 648539b..f9886cd 100644 (file)
@@ -10,7 +10,7 @@ BEGIN {
 }
 use OptreeCheck;
 use Config;
-plan tests => 14;
+plan tests => 16;
 
 SKIP: {
 skip "no perlio in this build", 4 unless $Config::Config{useperlio};
@@ -367,4 +367,68 @@ EOT_EOT
 EONT_EONT
 
 
+checkOptree ( name      => 'consolidate padranges and singletons',
+             code      => sub { my ($a,$b); my $c; my ($d,$e);
+                                my @f; my $g; my ($h,$i); my %j; 1 },
+             strip_open_hints => 1,
+             skip      => ($] < 5.017006),
+             expect    => <<'EOT_EOT', expect_nt => <<'EONT_EONT');
+# 5  <1> leavesub[1 ref] K/REFC,1 ->(end)
+# -     <@> lineseq KP ->5
+# 1        <;> nextstate(main 903 optree_misc.t:371) v ->2
+# -        <@> list vKP ->-
+# 2           <0> padrange[$a:903,910; $b:903,910; $c:904,910; $d:905,910; $e:905,910; @f:906,910; $g:907,910; $h:908,910; $i:908,910; %j:909,910] vM/LVINTRO,10 ->3
+# -           <0> padsv[$a:903,910] vM/LVINTRO ->-
+# -           <0> padsv[$b:903,910] vM/LVINTRO ->-
+# -        <;> nextstate(main 904 optree_misc.t:371) v ->-
+# -        <0> padsv[$c:904,910] vM/LVINTRO ->-
+# -        <;> nextstate(main 905 optree_misc.t:371) v:{ ->-
+# -        <@> list vKP ->-
+# -           <0> pushmark vM/LVINTRO ->-
+# -           <0> padsv[$d:905,910] vM/LVINTRO ->-
+# -           <0> padsv[$e:905,910] vM/LVINTRO ->-
+# -        <;> nextstate(main 906 optree_misc.t:372) v:{ ->-
+# -        <0> padav[@f:906,910] vM/LVINTRO ->-
+# -        <;> nextstate(main 907 optree_misc.t:372) v:{ ->-
+# -        <0> padsv[$g:907,910] vM/LVINTRO ->-
+# -        <;> nextstate(main 908 optree_misc.t:372) v:{ ->-
+# -        <@> list vKP ->-
+# -           <0> pushmark vM/LVINTRO ->-
+# -           <0> padsv[$h:908,910] vM/LVINTRO ->-
+# -           <0> padsv[$i:908,910] vM/LVINTRO ->-
+# -        <;> nextstate(main 909 optree_misc.t:372) v:{ ->-
+# -        <0> padhv[%j:909,910] vM/LVINTRO ->3
+# 3        <;> nextstate(main 910 optree_misc.t:372) v:{ ->4
+# 4        <$> const[IV 1] s ->5
+EOT_EOT
+# 5  <1> leavesub[1 ref] K/REFC,1 ->(end)
+# -     <@> lineseq KP ->5
+# 1        <;> nextstate(main 903 optree_misc.t:371) v ->2
+# -        <@> list vKP ->-
+# 2           <0> padrange[$a:903,910; $b:903,910; $c:904,910; $d:905,910; $e:905,910; @f:906,910; $g:907,910; $h:908,910; $i:908,910; %j:909,910] vM/LVINTRO,10 ->3
+# -           <0> padsv[$a:903,910] vM/LVINTRO ->-
+# -           <0> padsv[$b:903,910] vM/LVINTRO ->-
+# -        <;> nextstate(main 904 optree_misc.t:371) v ->-
+# -        <0> padsv[$c:904,910] vM/LVINTRO ->-
+# -        <;> nextstate(main 905 optree_misc.t:371) v:{ ->-
+# -        <@> list vKP ->-
+# -           <0> pushmark vM/LVINTRO ->-
+# -           <0> padsv[$d:905,910] vM/LVINTRO ->-
+# -           <0> padsv[$e:905,910] vM/LVINTRO ->-
+# -        <;> nextstate(main 906 optree_misc.t:372) v:{ ->-
+# -        <0> padav[@f:906,910] vM/LVINTRO ->-
+# -        <;> nextstate(main 907 optree_misc.t:372) v:{ ->-
+# -        <0> padsv[$g:907,910] vM/LVINTRO ->-
+# -        <;> nextstate(main 908 optree_misc.t:372) v:{ ->-
+# -        <@> list vKP ->-
+# -           <0> pushmark vM/LVINTRO ->-
+# -           <0> padsv[$h:908,910] vM/LVINTRO ->-
+# -           <0> padsv[$i:908,910] vM/LVINTRO ->-
+# -        <;> nextstate(main 909 optree_misc.t:372) v:{ ->-
+# -        <0> padhv[%j:909,910] vM/LVINTRO ->3
+# 3        <;> nextstate(main 910 optree_misc.t:372) v:{ ->4
+# 4        <$> const(IV 1) s ->5
+EONT_EONT
+
+
 unlink $tmpfile;
diff --git a/op.c b/op.c
index fdb5094..b4a845e 100644 (file)
--- a/op.c
+++ b/op.c
@@ -10945,6 +10945,7 @@ Perl_rpeep(pTHX_ register OP *o)
             PADOFFSET base = 0; /* init only to stop compiler whining */
             U8 gimme       = 0; /* init only to stop compiler whining */
             bool defav = 0;  /* seen (...) = @_ */
+            bool reuse = 0;  /* reuse an existing padrange op */
 
             /* look for a pushmark -> gv[_] -> rv2av */
 
@@ -11077,10 +11078,12 @@ Perl_rpeep(pTHX_ register OP *o)
                     followop = followop->op_next; /* skip OP_LIST */
 
                     /* consolidate two successive my(...);'s */
+
                     if (   oldoldop
                         && oldoldop->op_type == OP_PADRANGE
                         && (oldoldop->op_flags & OPf_WANT) == OPf_WANT_VOID
                         && (oldoldop->op_private & OPpLVAL_INTRO) == intro
+                        && !(oldoldop->op_flags & OPf_SPECIAL)
                     ) {
                         U8 old_count;
                         assert(oldoldop->op_next == oldop);
@@ -11093,26 +11096,62 @@ Perl_rpeep(pTHX_ register OP *o)
                         assert(oldoldop->op_targ + old_count == base);
 
                         if (old_count < OPpPADRANGE_COUNTMASK - count) {
-                            oldoldop->op_private = (intro | (old_count+count));
-                            oldoldop->op_next = followop;
-                            break;
+                            base = oldoldop->op_targ;
+                            count += old_count;
+                            reuse = 1;
                         }
                     }
+
+                    /* if there's any immediately following singleton
+                     * my var's; then swallow them and the associated
+                     * nextstates; i.e.
+                     *    my ($a,$b); my $c; my $d;
+                     * is treated as
+                     *    my ($a,$b,$c,$d);
+                     */
+
+                    while (    ((p = followop->op_next))
+                            && (  p->op_type == OP_PADSV
+                               || p->op_type == OP_PADAV
+                               || p->op_type == OP_PADHV)
+                            && (p->op_flags & OPf_WANT) == OPf_WANT_VOID
+                            && (p->op_private & OPpLVAL_INTRO) == intro
+                            && p->op_next
+                            && (   p->op_next->op_type == OP_NEXTSTATE
+                                || p->op_next->op_type == OP_DBSTATE)
+                            && count < OPpPADRANGE_COUNTMASK
+                    ) {
+                        assert(base + count == p->op_targ);
+                        count++;
+                        followop = p->op_next;
+                    }
                 }
                 else
                     break;
             }
 
-            /* Convert the pushmark into a padrange */
-            o->op_next = followop;
-            o->op_type = OP_PADRANGE;
-            o->op_ppaddr = PL_ppaddr[OP_PADRANGE];
-            o->op_targ = base;
-            /* bit 7: INTRO; bit 6..0: count */
-            o->op_private = (intro | count);
-            o->op_flags = ((o->op_flags & ~(OPf_WANT|OPf_SPECIAL))
-                                | gimme | (defav ? OPf_SPECIAL : 0));
-
+            if (reuse) {
+                assert(oldoldop->op_type == OP_PADRANGE);
+                oldoldop->op_next = followop;
+                oldoldop->op_private = (intro | count);
+                o = oldoldop;
+                oldop = NULL;
+                oldoldop = NULL;
+            }
+            else {
+                /* Convert the pushmark into a padrange.
+                 * To make Deparse easier, we guarantee that a padrange was
+                 * *always* formerly a pushmark */
+                assert(o->op_type == OP_PUSHMARK);
+                o->op_next = followop;
+                o->op_type = OP_PADRANGE;
+                o->op_ppaddr = PL_ppaddr[OP_PADRANGE];
+                o->op_targ = base;
+                /* bit 7: INTRO; bit 6..0: count */
+                o->op_private = (intro | count);
+                o->op_flags = ((o->op_flags & ~(OPf_WANT|OPf_SPECIAL))
+                                    | gimme | (defav ? OPf_SPECIAL : 0));
+            }
             break;
         }