*
* During the conversion process, EXPR ops are stripped from the tree
* and unshifted onto o. Finally, any of o's remaining original
- * childen are discarded and o is converted into an OP_MULTICONCAT.
+ * children are discarded and o is converted into an OP_MULTICONCAT.
*
* In this middle of this, o may contain both: unshifted args on the
* left, and some remaining original args on the right. lastkidop
/* for N levels of aggregate lookup, we normally expect
* that the first N-1 [ah]elem ops will be flagged as
- * /DEREF (so they autovivifiy if necessary), and the last
+ * /DEREF (so they autovivify if necessary), and the last
* lookup op not to be.
* For other things (like @{$h{k1}{k2}}) extra scope or
* leave ops can appear, so abandon the effort in that
/* Two NEXTSTATEs in a row serve no purpose. Except if they happen
to carry two labels. For now, take the easier option, and skip
- this optimisation if the first NEXTSTATE has a label. */
+ this optimisation if the first NEXTSTATE has a label.
+ Yves asked what about if they have different hints or features?
+ Tony thinks that as we remove the first of the pair it should
+ be fine.
+ */
if (!CopLABEL((COP*)o) && !PERLDB_NOOPT) {
OP *nextop = o->op_next;
while (nextop) {
}
}
+ /* If the pushmark is associated with an empty anonhash
+ * or anonlist, null out the pushmark and swap in a
+ * specialised op for the parent.
+ * 4 <@> anonhash sK* ->5
+ * 3 <0> pushmark s ->4
+ * becomes:
+ * 3 <@> emptyavhv sK* ->4
+ * - <0> pushmark s ->3
+ */
+ if (!OpHAS_SIBLING(o) && (o->op_next == o->op_sibparent) && (
+ (o->op_next->op_type == OP_ANONHASH) ||
+ (o->op_next->op_type == OP_ANONLIST) ) &&
+ (o->op_next->op_flags & OPf_SPECIAL) ) {
+
+ OP* anon = o->op_next;
+ /* These next two are _potentially_ a padsv and an sassign */
+ OP* padsv = anon->op_next;
+ OP* sassign = (padsv) ? padsv->op_next: NULL;
+
+ anon->op_private = (anon->op_type == OP_ANONLIST) ?
+ 0 : OPpEMPTYAVHV_IS_HV;
+ OpTYPE_set(anon, OP_EMPTYAVHV);
+ op_null(o);
+ o = anon;
+ if (oldop) /* A previous optimization may have NULLED it */
+ oldop->op_next = anon;
+
+ /* Further optimise scalar assignment of an empty anonhash
+ * or anonlist by subsuming the padsv & sassign OPs. */
+ if ((padsv->op_type == OP_PADSV) &&
+ !(padsv->op_private & OPpDEREF) &&
+ sassign && (sassign->op_type == OP_SASSIGN) ){
+
+ /* Take some public flags from the sassign */
+ anon->op_flags = OPf_KIDS | OPf_SPECIAL |
+ (anon->op_flags & OPf_PARENS) |
+ (sassign->op_flags & (OPf_WANT|OPf_PARENS));
+
+ /* Take some private flags from the padsv */
+ anon->op_private |= OPpTARGET_MY |
+ (padsv->op_private & (OPpLVAL_INTRO|OPpPAD_STATE));
+
+ /* Take the targ slot from the padsv*/
+ anon->op_targ = padsv->op_targ;
+ padsv->op_targ = 0;
+
+ /* Clean up */
+ anon->op_next = sassign->op_next;
+ op_null(padsv);
+ op_null(sassign);
+ }
+ break;
+
+ }
+
+
/* Convert a series of PAD ops for my vars plus support into a
* single padrange op. Basically
*
* any other pad ops, and possibly some trailing ops.
* Note that we don't null() the skipped ops, to make it
* easier for Deparse to undo this optimisation (and none of
- * the skipped ops are holding any resourses). It also makes
+ * the skipped ops are holding any resources). It also makes
* it easier for find_uninit_var(), as it can just ignore
* padrange, and examine the original pad ops.
*/
break;
}
+ case OP_UNDEF:
+ if ((o->op_flags & OPf_KIDS) &&
+ (cUNOPx(o)->op_first->op_type == OP_PADSV)) {
+
+ /* Convert:
+ * undef
+ * padsv[$x]
+ * to:
+ * undef[$x]
+ */
+
+ OP * padsv = cUNOPx(o)->op_first;
+ o->op_private = OPpTARGET_MY |
+ (padsv->op_private & (OPpLVAL_INTRO|OPpPAD_STATE));
+ o->op_targ = padsv->op_targ; padsv->op_targ = 0;
+ op_null(padsv);
+ /* Optimizer does NOT seem to fix up the padsv op_next ptr */
+ if (oldoldop)
+ oldoldop->op_next = o;
+ oldop = oldoldop;
+ oldoldop = NULL;
+
+ } else if (o->op_next->op_type == OP_PADSV) {
+ OP * padsv = o->op_next;
+ OP * sassign = (padsv->op_next &&
+ padsv->op_next->op_type == OP_SASSIGN) ?
+ padsv->op_next : NULL;
+ if (sassign && cBINOPx(sassign)->op_first == o) {
+ /* Convert:
+ * sassign
+ * undef
+ * padsv[$x]
+ * to:
+ * undef[$x]
+ * NOTE: undef does not have the "T" flag set in
+ * regen/opcodes, as this would cause
+ * S_maybe_targlex to do the optimization.
+ * Seems easier to keep it all here, rather
+ * than have an undef-specific branch in
+ * S_maybe_targlex just to add the
+ * OPpUNDEF_KEEP_PV flag.
+ */
+ o->op_private = OPpTARGET_MY | OPpUNDEF_KEEP_PV |
+ (padsv->op_private & (OPpLVAL_INTRO|OPpPAD_STATE));
+ o->op_targ = padsv->op_targ; padsv->op_targ = 0;
+ op_null(padsv);
+ op_null(sassign);
+ /* Optimizer DOES seems to fix up the op_next ptrs */
+ }
+ }
+ break;
+
case OP_QR:
case OP_MATCH:
if (!(cPMOP->op_pmflags & PMf_ONCE)) {
}
break;
- case OP_SASSIGN:
+ case OP_SASSIGN: {
if (OP_GIMME(o,0) == G_VOID
|| ( o->op_next->op_type == OP_LINESEQ
&& ( o->op_next->op_next->op_type == OP_LEAVESUB
if (!(o->op_private & (OPpASSIGN_BACKWARDS|OPpASSIGN_CV_TO_GV))
&& lval && (lval->op_type == OP_PADSV) &&
!(lval->op_private & OPpDEREF)
+ /* skip if padrange has already gazumped the padsv */
+ && (lval == oldop)
+ /* Memoize::Once produces a non-standard SASSIGN that
+ * doesn't actually point to pp_sassign, has only one
+ * child (PADSV), and gets to it via op_other rather
+ * than op_next. Don't try to optimize this. */
+ && (lval != rhs)
) {
-
/* SASSIGN's bitfield flags, such as op_moresib and
* op_slabbed, will be carried over unchanged. */
OpTYPE_set(o, OP_PADSV_STORE);
o->op_targ = lval->op_targ; lval->op_targ = 0;
/* Fixup op_next ptrs */
+ assert(oldop->op_type == OP_PADSV);
/* oldoldop can be arbitrarily deep in the RHS OP tree */
oldoldop->op_next = o;
/* NULL the previous op ptrs, so rpeep can continue */
oldoldop = NULL; oldop = NULL;
}
+
+ /* Combine a simple SASSIGN OP with an AELEMFAST_LEX lvalue
+ * into a single OP. This optimization covers arbitrarily
+ * complicated RHS OP trees. */
+
+ if (!(o->op_private & (OPpASSIGN_BACKWARDS|OPpASSIGN_CV_TO_GV))
+ && (lval->op_type == OP_NULL) && (lval->op_private == 2) &&
+ (cBINOPx(lval)->op_first->op_type == OP_AELEMFAST_LEX)
+ ) {
+ OP * lex = cBINOPx(lval)->op_first;
+ /* SASSIGN's bitfield flags, such as op_moresib and
+ * op_slabbed, will be carried over unchanged. */
+ OpTYPE_set(o, OP_AELEMFASTLEX_STORE);
+
+ /* Explicitly craft the new OP's op_flags, carrying
+ * some bits over from the SASSIGN */
+ o->op_flags = (
+ OPf_KIDS | OPf_STACKED |
+ (o->op_flags & (OPf_WANT|OPf_PARENS))
+ );
+
+ /* Copy the AELEMFAST_LEX op->private, which contains
+ * the key index. */
+ o->op_private = lex->op_private;
+
+ /* Take the targ from the AELEMFAST_LEX */
+ o->op_targ = lex->op_targ; lex->op_targ = 0;
+
+ assert(oldop->op_type == OP_AELEMFAST_LEX);
+ /* oldoldop can be arbitrarily deep in the RHS OP tree */
+ oldoldop->op_next = o;
+
+ /* Even when (rhs != oldoldop), rhs might still have a
+ * relevant op_next ptr to lex. (Updating it here can
+ * also cause other ops in the RHS to get the desired
+ * op_next pointer, presumably thanks to the finalizer.)
+ * This is definitely truewhen rhs is OP_NULL with a
+ * LOGOP kid (e.g. orassign). There may be other cases. */
+ if (rhs->op_next == lex)
+ rhs->op_next = o;
+
+ /* Now null-out the AELEMFAST_LEX */
+ op_null(lex);
+
+ /* NULL the previous op ptrs, so rpeep can continue */
+ oldop = oldoldop; oldoldop = NULL;
+ }
+
break;
+ }
case OP_AASSIGN: {
int l, r, lr, lscalars, rscalars;