This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Revert "op.c: Don’t keep looping when we see potential common vars"
[perl5.git] / op.c
diff --git a/op.c b/op.c
index 3d005aa..7b8c176 100644 (file)
--- a/op.c
+++ b/op.c
@@ -1695,6 +1695,8 @@ Perl_scalarvoid(pTHX_ OP *o)
     case OP_SPLIT:
        kid = cLISTOPo->op_first;
        if (kid && kid->op_type == OP_PUSHRE
+               && !kid->op_targ
+               && !(o->op_flags & OPf_STACKED)
 #ifdef USE_ITHREADS
                && !((PMOP*)kid)->op_pmreplrootu.op_pmtargetoff)
 #else
@@ -2397,17 +2399,19 @@ S_lvref(pTHX_ OP *o, I32 type)
       checkgv:
        if (cUNOPo->op_first->op_type != OP_GV) goto badref;
        o->op_flags |= OPf_STACKED;
+       break;
+    case OP_PADHV:
+       if (o->op_flags & OPf_PARENS) goto parenhash;
+       o->op_private |= OPpLVREF_HV;
        /* FALLTHROUGH */
     case OP_PADSV:
+       PAD_COMPNAME_GEN_set(o->op_targ, PERL_INT_MAX);
        break;
     case OP_PADAV:
+       PAD_COMPNAME_GEN_set(o->op_targ, PERL_INT_MAX);
        if (o->op_flags & OPf_PARENS) goto slurpy;
        o->op_private |= OPpLVREF_AV;
        break;
-    case OP_PADHV:
-       if (o->op_flags & OPf_PARENS) goto parenhash;
-       o->op_private |= OPpLVREF_HV;
-       break;
     case OP_AELEM:
     case OP_HELEM:
        o->op_private |= OPpLVREF_ELEM;
@@ -3868,6 +3872,7 @@ S_fold_constants(pTHX_ OP *o)
     OP * VOL curop;
     OP *newop;
     VOL I32 type = o->op_type;
+    bool folded;
     SV * VOL sv = NULL;
     int ret = 0;
     I32 oldscope;
@@ -4014,6 +4019,7 @@ S_fold_constants(pTHX_ OP *o)
     if (ret)
        goto nope;
 
+    folded = o->op_folded;
     op_free(o);
     assert(sv);
     if (type == OP_STRINGIFY) SvPADTMP_off(sv);
@@ -4026,7 +4032,11 @@ S_fold_constants(pTHX_ OP *o)
     else
     {
        newop = newSVOP(OP_CONST, 0, MUTABLE_SV(sv));
-       if (type != OP_STRINGIFY) newop->op_folded = 1;
+       /* OP_STRINGIFY and constant folding are used to implement qq.
+          Here the constant folding is an implementation detail that we
+          want to hide.  If the stringify op is itself already marked
+          folded, however, then it is actually a folded join.  */
+       if (type != OP_STRINGIFY || folded) newop->op_folded = 1;
     }
     return newop;
 
@@ -5879,8 +5889,27 @@ S_assignment_type(pTHX_ const OP *o)
 
 /*
   Helper function for newASSIGNOP to detection commonality between the
-  lhs and the rhs.  Marks all variables with PL_generation.  If it
+  lhs and the rhs.  (It is actually called very indirectly.  newASSIGNOP
+  flags the op and the peephole optimizer calls this helper function
+  if the flag is set.)  Marks all variables with PL_generation.  If it
   returns TRUE the assignment must be able to handle common variables.
+
+  PL_generation sorcery:
+  An assignment like ($a,$b) = ($c,$d) is easier than
+  ($a,$b) = ($c,$a), since there is no need for temporary vars.
+  To detect whether there are common vars, the global var
+  PL_generation is incremented for each assign op we compile.
+  Then, while compiling the assign op, we run through all the
+  variables on both sides of the assignment, setting a spare slot
+  in each of them to PL_generation.  If any of them already have
+  that value, we know we've got commonality.  Also, if the
+  generation number is already set to PERL_INT_MAX, then
+  the variable is involved in aliasing, so we also have
+  potential commonality in that case.  We could use a
+  single bit marker, but then we'd have to make 2 passes, first
+  to clear the flag, then to test and set it.  And that
+  wouldn't help with aliasing, either.  To find somewhere
+  to store these values, evil chicanery is done with SvUVX().
 */
 PERL_STATIC_INLINE bool
 S_aassign_common_vars(pTHX_ OP* o)
@@ -5888,7 +5917,7 @@ S_aassign_common_vars(pTHX_ OP* o)
     OP *curop;
     for (curop = cUNOPo->op_first; curop; curop = OP_SIBLING(curop)) {
        if (PL_opargs[curop->op_type] & OA_DANGEROUS) {
-           if (curop->op_type == OP_GV) {
+           if (curop->op_type == OP_GV || curop->op_type == OP_GVSV) {
                GV *gv = cGVOPx_gv(curop);
                if (gv == PL_defgv
                    || (int)GvASSIGN_GENERATION(gv) == PL_generation)
@@ -5900,8 +5929,10 @@ S_aassign_common_vars(pTHX_ OP* o)
                curop->op_type == OP_PADHV ||
                curop->op_type == OP_PADANY)
                {
+                 padcheck:
                    if (PAD_COMPNAME_GEN(curop->op_targ)
-                       == (STRLEN)PL_generation)
+                       == (STRLEN)PL_generation
+                    || PAD_COMPNAME_GEN(curop->op_targ) == PERL_INT_MAX)
                        return TRUE;
                    PAD_COMPNAME_GEN_set(curop->op_targ, PL_generation);
 
@@ -5930,7 +5961,12 @@ S_aassign_common_vars(pTHX_ OP* o)
                        return TRUE;
                    GvASSIGN_GENERATION_set(gv, PL_generation);
                }
+               else if (curop->op_targ)
+                   goto padcheck;
            }
+           else if (curop->op_type == OP_PADRANGE)
+               /* Ignore padrange; checking its siblings is sufficient. */
+               continue;
            else
                return TRUE;
        }
@@ -5943,6 +5979,33 @@ S_aassign_common_vars(pTHX_ OP* o)
     return FALSE;
 }
 
+/* This variant only handles lexical aliases.  It is called when
+   newASSIGNOP decides that we don’t have any common vars, as lexical ali-
+   ases trump that decision.  */
+PERL_STATIC_INLINE bool
+S_aassign_common_vars_aliases_only(pTHX_ OP *o)
+{
+    OP *curop;
+    for (curop = cUNOPo->op_first; curop; curop = OP_SIBLING(curop)) {
+       if ((curop->op_type == OP_PADSV ||
+            curop->op_type == OP_PADAV ||
+            curop->op_type == OP_PADHV ||
+            curop->op_type == OP_PADANY)
+          && PAD_COMPNAME_GEN(curop->op_targ) == PERL_INT_MAX)
+           return TRUE;
+
+       if (curop->op_type == OP_PUSHRE && curop->op_targ
+        && PAD_COMPNAME_GEN(curop->op_targ) == PERL_INT_MAX)
+           return TRUE;
+
+       if (curop->op_flags & OPf_KIDS) {
+           if (S_aassign_common_vars_aliases_only(aTHX_ curop))
+               return TRUE;
+       }
+    }
+    return FALSE;
+}
+
 /*
 =for apidoc Am|OP *|newASSIGNOP|I32 flags|OP *left|I32 optype|OP *right
 
@@ -6057,69 +6120,78 @@ Perl_newASSIGNOP(pTHX_ I32 flags, OP *left, I32 optype, OP *right)
            }
        }
 
-       /* PL_generation sorcery:
-        * an assignment like ($a,$b) = ($c,$d) is easier than
-        * ($a,$b) = ($c,$a), since there is no need for temporary vars.
-        * To detect whether there are common vars, the global var
-        * PL_generation is incremented for each assign op we compile.
-        * Then, while compiling the assign op, we run through all the
-        * variables on both sides of the assignment, setting a spare slot
-        * in each of them to PL_generation. If any of them already have
-        * that value, we know we've got commonality.  We could use a
-        * single bit marker, but then we'd have to make 2 passes, first
-        * to clear the flag, then to test and set it.  To find somewhere
-        * to store these values, evil chicanery is done with SvUVX().
-        */
-
        if (maybe_common_vars) {
-           PL_generation++;
-           if (aassign_common_vars(o))
+               /* The peephole optimizer will do the full check and pos-
+                  sibly turn this off.  */
                o->op_private |= OPpASSIGN_COMMON;
-           LINKLIST(o);
        }
 
-       if (right && right->op_type == OP_SPLIT) {
+       if (right && right->op_type == OP_SPLIT
+        && !(right->op_flags & OPf_STACKED)) {
            OP* tmpop = ((LISTOP*)right)->op_first;
            if (tmpop && (tmpop->op_type == OP_PUSHRE)) {
                PMOP * const pm = (PMOP*)tmpop;
-               if (left->op_type == OP_RV2AV &&
-                   !(left->op_private & OPpLVAL_INTRO) &&
-                   !(o->op_private & OPpASSIGN_COMMON) )
-               {
-                   tmpop = ((UNOP*)left)->op_first;
-                   if (tmpop->op_type == OP_GV
+               if (
 #ifdef USE_ITHREADS
-                       && !pm->op_pmreplrootu.op_pmtargetoff
+                   !pm->op_pmreplrootu.op_pmtargetoff
 #else
-                       && !pm->op_pmreplrootu.op_pmtargetgv
+                   !pm->op_pmreplrootu.op_pmtargetgv
 #endif
+                && !pm->op_targ
+               ) {
+                   if (!(left->op_private & OPpLVAL_INTRO) &&
+                       ( (left->op_type == OP_RV2AV &&
+                         (tmpop=((UNOP*)left)->op_first)->op_type==OP_GV)
+                       || left->op_type == OP_PADAV )
                        ) {
+                       if (tmpop != (OP *)pm) {
 #ifdef USE_ITHREADS
-                       pm->op_pmreplrootu.op_pmtargetoff
+                         pm->op_pmreplrootu.op_pmtargetoff
                            = cPADOPx(tmpop)->op_padix;
-                       cPADOPx(tmpop)->op_padix = 0;   /* steal it */
+                         cPADOPx(tmpop)->op_padix = 0; /* steal it */
 #else
-                       pm->op_pmreplrootu.op_pmtargetgv
+                         pm->op_pmreplrootu.op_pmtargetgv
                            = MUTABLE_GV(cSVOPx(tmpop)->op_sv);
-                       cSVOPx(tmpop)->op_sv = NULL;    /* steal it */
+                         cSVOPx(tmpop)->op_sv = NULL;  /* steal it */
 #endif
+                         right->op_private |=
+                           left->op_private & OPpOUR_INTRO;
+                       }
+                       else {
+                           pm->op_targ = left->op_targ;
+                           left->op_targ = 0; /* filch it */
+                       }
+                     detach_split:
                        tmpop = cUNOPo->op_first;       /* to list (nulled) */
                        tmpop = ((UNOP*)tmpop)->op_first; /* to pushmark */
                         /* detach rest of siblings from o subtree,
                          * and free subtree */
                         op_sibling_splice(cUNOPo->op_first, tmpop, -1, NULL);
-                       right->op_next = tmpop->op_next;  /* fix starting loc */
-                       right->op_private |=
-                           left->op_private & OPpOUR_INTRO;
                        op_free(o);                     /* blow off assign */
                        right->op_flags &= ~OPf_WANT;
                                /* "I don't know and I don't care." */
                        return right;
                    }
-               }
-               else {
-                   if (PL_modcount < RETURN_UNLIMITED_NUMBER &&
-                     ((LISTOP*)right)->op_last->op_type == OP_CONST)
+                   else if (left->op_type == OP_RV2AV
+                         || left->op_type == OP_PADAV)
+                   {
+                       /* Detach the array.  */
+#ifdef DEBUGGING
+                       OP * const ary =
+#endif
+                       op_sibling_splice(cBINOPo->op_last,
+                                         cUNOPx(cBINOPo->op_last)
+                                               ->op_first, 1, NULL);
+                       assert(ary == left);
+                       /* Attach it to the split.  */
+                       op_sibling_splice(right, cLISTOPx(right)->op_last,
+                                         0, left);
+                       right->op_flags |= OPf_STACKED;
+                       /* Detach split and expunge aassign as above.  */
+                       goto detach_split;
+                   }
+                   else if (PL_modcount < RETURN_UNLIMITED_NUMBER &&
+                           ((LISTOP*)right)->op_last->op_type == OP_CONST)
                    {
                        SV ** const svp =
                            &((SVOP*)((LISTOP*)right)->op_last)->op_sv;
@@ -6938,6 +7010,7 @@ Perl_newFOROP(pTHX_ I32 flags, OP *sv, OP *expr, OP *block, OP *cont)
             sv->op_targ = 0;
             op_free(sv);
            sv = NULL;
+           PAD_COMPNAME_GEN_set(padoff, PERL_INT_MAX);
        }
        else if (sv->op_type == OP_NULL && sv->op_targ == OP_SREFGEN)
            NOOP;
@@ -9939,14 +10012,18 @@ Perl_ck_sassign(pTHX_ OP *o)
            OP *const first = newOP(OP_NULL, 0);
            OP *const nullop = newCONDOP(0, first, o, other);
            OP *const condop = first->op_next;
-           /* hijacking PADSTALE for uninitialized state variables */
-           SvPADSTALE_on(PAD_SVl(target));
 
            condop->op_type = OP_ONCE;
            condop->op_ppaddr = PL_ppaddr[OP_ONCE];
-           condop->op_targ = target;
            other->op_targ = target;
 
+           /* Store the initializedness of state vars in a separate
+              pad entry.  */
+           condop->op_targ =
+             pad_add_name_pvn("$",1,padadd_NO_DUP_CHECK|padadd_STATE,0,0);
+           /* hijacking PADSTALE for uninitialized state variables */
+           SvPADSTALE_on(PAD_SVl(condop->op_targ));
+
            return nullop;
        }
     }
@@ -10056,6 +10133,7 @@ Perl_ck_refassign(pTHX_ OP *o)
       settarg:
        o->op_targ = varop->op_targ;
        varop->op_targ = 0;
+       PAD_COMPNAME_GEN_set(o->op_targ, PERL_INT_MAX);
        break;
     case OP_RV2AV:
        o->op_private = OPpLVREF_AV;
@@ -10528,9 +10606,23 @@ Perl_ck_split(pTHX_ OP *o)
 }
 
 OP *
+Perl_ck_stringify(pTHX_ OP *o)
+{
+    OP * const kid = OP_SIBLING(cUNOPo->op_first);
+    PERL_ARGS_ASSERT_CK_STRINGIFY;
+    if (kid->op_type == OP_JOIN) {
+       assert(!OP_HAS_SIBLING(kid));
+       op_sibling_splice(o, cUNOPo->op_first, -1, NULL);
+       op_free(o);
+       return kid;
+    }
+    return ck_fun(o);
+}
+       
+OP *
 Perl_ck_join(pTHX_ OP *o)
 {
-    const OP * const kid = OP_SIBLING(cLISTOPo->op_first);
+    OP * const kid = OP_SIBLING(cLISTOPo->op_first);
 
     PERL_ARGS_ASSERT_CK_JOIN;
 
@@ -10546,6 +10638,23 @@ Perl_ck_join(pTHX_ OP *o)
                        SVfARG(msg), SVfARG(msg));
        }
     }
+    if (kid->op_type == OP_CONST  /* an innocent, unsuspicious separator */
+     || (kid->op_type == OP_PADSV && !(kid->op_private & OPpLVAL_INTRO))
+     || (kid->op_type==OP_RV2SV && kUNOP->op_first->op_type == OP_GV
+       && !(kid->op_private & (OPpLVAL_INTRO|OPpOUR_INTRO))))
+    {
+       const OP * const bairn = OP_SIBLING(kid); /* the list */
+       if (bairn && !OP_HAS_SIBLING(bairn) /* single-item list */
+        && PL_opargs[bairn->op_type] & OA_RETSCALAR)
+       {
+           OP * const ret = convert(OP_STRINGIFY, 0,
+                                    op_sibling_splice(o, kid, 1, NULL));
+           op_free(o);
+           ret->op_folded = 1;
+           return ret;
+       }
+    }
+
     return ck_fun(o);
 }
 
@@ -12497,6 +12606,21 @@ Perl_rpeep(pTHX_ OP *o)
            }
            break;
 
+       case OP_AASSIGN:
+           /* We do the common-vars check here, rather than in newASSIGNOP
+              (as formerly), so that all lexical vars that get aliased are
+              marked as such before we do the check.  */
+           if (o->op_private & OPpASSIGN_COMMON) {
+                /* See the comment before S_aassign_common_vars concerning
+                   PL_generation sorcery.  */
+               PL_generation++;
+               if (!aassign_common_vars(o))
+                   o->op_private &=~ OPpASSIGN_COMMON;
+           }
+           else if (S_aassign_common_vars_aliases_only(aTHX_ o))
+               o->op_private |= OPpASSIGN_COMMON;
+           break;
+
        case OP_CUSTOM: {
            Perl_cpeep_t cpeep = 
                XopENTRYCUSTOM(o, xop_peep);