This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Allow void padrange even without nextstate
authorFather Chrysostomos <sprout@cpan.org>
Fri, 17 Oct 2014 20:56:52 +0000 (13:56 -0700)
committerFather Chrysostomos <sprout@cpan.org>
Fri, 17 Oct 2014 20:57:24 +0000 (13:57 -0700)
This allows the padrange optimisation to happen with code like this:

    my ($a, $b), our $c;

The padrange optimisation looks for:

    list
      pushmark
      padsv
      padsv
      ...
    nextstate

if it is in void context.

padsv pushes an sv on to the stack, and then nextstate resets the
stack.  padrange doesn’t bother pushing in void context, which is why
this optimisation was written this way.

However, there are various ops that don’t bother touching the stack in
void context, for efficiency; conversely, there are many that do for
simplicity’s sake.  In all cases, however, things pushed in void con-
text are subsequently reset, usually by list/nextstate/leavesub,
before they can accidentally become part of a list.  So whether pad-
range pushes SVs or not in void context is irrelevant, and it does not
matter whether there is a nextstate op.

Further, this optimisation also allowed:

  ex-list
    pushmark
    ...

pushmark and list ops cancel each other out in list and void context.
padrange does not push a mark in void context, so the list is removed
from the execution chain.  That was also happening for a nulled list,
which is incorrect, as that means a lone pushmark is being removed.  I
don’t believe ex-list+pushmark ever occurs in void context, but if it
does then this code was wrong.

op.c

diff --git a/op.c b/op.c
index a1c2cab..7c59648 100644 (file)
--- a/op.c
+++ b/op.c
@@ -12083,17 +12083,16 @@ Perl_rpeep(pTHX_ OP *o)
              * padrange.
              * In particular in void context, we can only optimise to
              * a padrange if see see the complete sequence
-             *     pushmark, pad*v, ...., list, nextstate
-             * which has the net effect of of leaving the stack empty
-             * (for now we leave the nextstate in the execution chain, for
-             * its other side-effects).
+             *     pushmark, pad*v, ...., list
+             * which has the net effect of of leaving the markstack as it
+             * was.  Not pushing on to the stack (whereas padsv does touch
+             * the stack) makes no difference in void context.
              */
             assert(followop);
             if (gimme == OPf_WANT_VOID) {
-                if (OP_TYPE_IS_OR_WAS(followop, OP_LIST)
+                if (followop->op_type == OP_LIST
                         && gimme == (followop->op_flags & OPf_WANT)
-                        && (   followop->op_next->op_type == OP_NEXTSTATE
-                            || followop->op_next->op_type == OP_DBSTATE))
+                   )
                 {
                     followop = followop->op_next; /* skip OP_LIST */