This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Refactor newATTRSUB()'s logic for grafting a sub definition to an existing stub
[perl5.git] / op.c
diff --git a/op.c b/op.c
index 6add236..73f5f4a 100644 (file)
--- a/op.c
+++ b/op.c
@@ -103,8 +103,9 @@ recursive, but it's recursive on basic blocks, not on tree nodes.
 #include "perl.h"
 #include "keywords.h"
 
-#define CALL_PEEP(o) CALL_FPTR(PL_peepp)(aTHX_ o)
-#define CALL_OPFREEHOOK(o) if (PL_opfreehook) CALL_FPTR(PL_opfreehook)(aTHX_ o)
+#define CALL_PEEP(o) PL_peepp(aTHX_ o)
+#define CALL_RPEEP(o) PL_rpeepp(aTHX_ o)
+#define CALL_OPFREEHOOK(o) if (PL_opfreehook) PL_opfreehook(aTHX_ o)
 
 #if defined(PL_OP_SLAB_ALLOC)
 
@@ -305,10 +306,16 @@ Perl_Slab_Free(pTHX_ void *op)
      ? ( op_free((OP*)o),                                      \
         Perl_croak(aTHX_ "'%s' trapped by operation mask", PL_op_desc[type]),  \
         (OP*)0 )                                               \
-     : CALL_FPTR(PL_check[type])(aTHX_ (OP*)o))
+     : PL_check[type](aTHX_ (OP*)o))
 
 #define RETURN_UNLIMITED_NUMBER (PERL_INT_MAX / 2)
 
+#define CHANGE_TYPE(o,type) \
+    STMT_START {                               \
+       o->op_type = (OPCODE)type;              \
+       o->op_ppaddr = PL_ppaddr[type];         \
+    } STMT_END
+
 STATIC const char*
 S_gv_ename(pTHX_ GV *gv)
 {
@@ -372,7 +379,7 @@ S_no_bareword_allowed(pTHX_ const OP *o)
 /* "register" allocation */
 
 PADOFFSET
-Perl_allocmy(pTHX_ const char *const name)
+Perl_allocmy(pTHX_ const char *const name, const STRLEN len, const U32 flags)
 {
     dVAR;
     PADOFFSET off;
@@ -380,38 +387,43 @@ Perl_allocmy(pTHX_ const char *const name)
 
     PERL_ARGS_ASSERT_ALLOCMY;
 
+    if (flags)
+       Perl_croak(aTHX_ "panic: allocmy illegal flag bits 0x%" UVxf,
+                  (UV)flags);
+
+    /* Until we're using the length for real, cross check that we're being
+       told the truth.  */
+    assert(strlen(name) == len);
+
     /* complain about "my $<special_var>" etc etc */
-    if (*name &&
+    if (len &&
        !(is_our ||
          isALPHA(name[1]) ||
          (USE_UTF8_IN_NAMES && UTF8_IS_START(name[1])) ||
-         (name[1] == '_' && (*name == '$' || name[2]))))
+         (name[1] == '_' && (*name == '$' || len > 2))))
     {
        /* name[2] is true if strlen(name) > 2  */
        if (!isPRINT(name[1]) || strchr("\t\n\r\f", name[1])) {
-           yyerror(Perl_form(aTHX_ "Can't use global %c^%c%s in \"%s\"",
-                             name[0], toCTRL(name[1]), name + 2,
+           yyerror(Perl_form(aTHX_ "Can't use global %c^%c%.*s in \"%s\"",
+                             name[0], toCTRL(name[1]), (int)(len - 2), name + 2,
                              PL_parser->in_my == KEY_state ? "state" : "my"));
        } else {
-           yyerror(Perl_form(aTHX_ "Can't use global %s in \"%s\"",name,
+           yyerror(Perl_form(aTHX_ "Can't use global %.*s in \"%s\"", (int) len, name,
                              PL_parser->in_my == KEY_state ? "state" : "my"));
        }
     }
 
-    /* check for duplicate declaration */
-    pad_check_dup(name, is_our, (PL_curstash ? PL_curstash : PL_defstash));
-
     /* allocate a spare slot and store the name in that slot */
 
-    off = pad_add_name(name,
+    off = pad_add_name(name, len,
+                      is_our ? padadd_OUR :
+                      PL_parser->in_my == KEY_state ? padadd_STATE : 0,
                    PL_parser->in_my_stash,
                    (is_our
                        /* $_ is always in main::, even with our */
                        ? (PL_curstash && !strEQ(name,"$_") ? PL_curstash : PL_defstash)
                        : NULL
-                   ),
-                   0, /*  not fake */
-                   PL_parser->in_my == KEY_state
+                   )
     );
     /* anon sub prototypes contains state vars should always be cloned,
      * otherwise the state var would be shared between anon subs */
@@ -557,12 +569,13 @@ Perl_op_clear(pTHX_ OP *o)
            o->op_targ = 0;
            goto retry;
        }
+    case OP_ENTERTRY:
     case OP_ENTEREVAL: /* Was holding hints. */
        o->op_targ = 0;
        break;
     default:
        if (!(o->op_flags & OPf_REF)
-           || (PL_check[o->op_type] != MEMBER_TO_FPTR(Perl_ck_ftst)))
+           || (PL_check[o->op_type] != Perl_ck_ftst))
            break;
        /* FALL THROUGH */
     case OP_GVSV:
@@ -638,6 +651,7 @@ Perl_op_clear(pTHX_ OP *o)
            break;
        /* FALL THROUGH */
     case OP_TRANS:
+    case OP_TRANSR:
        if (o->op_private & (OPpTRANS_FROM_UTF|OPpTRANS_TO_UTF)) {
 #ifdef USE_ITHREADS
            if (cPADOPo->op_padix > 0) {
@@ -710,7 +724,7 @@ S_cop_free(pTHX_ COP* cop)
     CopSTASH_free(cop);
     if (! specialWARN(cop->cop_warnings))
        PerlMemShared_free(cop->cop_warnings);
-    Perl_refcounted_he_free(aTHX_ cop->cop_hints_hash);
+    cophh_free(CopHINTHASH_get(cop));
 }
 
 STATIC void
@@ -811,14 +825,47 @@ Perl_op_refcnt_unlock(pTHX)
 
 /* Contextualizers */
 
-#define LINKLIST(o) ((o)->op_next ? (o)->op_next : linklist((OP*)o))
+/*
+=for apidoc Am|OP *|op_contextualize|OP *o|I32 context
 
-static OP *
-S_linklist(pTHX_ OP *o)
+Applies a syntactic context to an op tree representing an expression.
+I<o> is the op tree, and I<context> must be C<G_SCALAR>, C<G_ARRAY>,
+or C<G_VOID> to specify the context to apply.  The modified op tree
+is returned.
+
+=cut
+*/
+
+OP *
+Perl_op_contextualize(pTHX_ OP *o, I32 context)
+{
+    PERL_ARGS_ASSERT_OP_CONTEXTUALIZE;
+    switch (context) {
+       case G_SCALAR: return scalar(o);
+       case G_ARRAY:  return list(o);
+       case G_VOID:   return scalarvoid(o);
+       default:
+           Perl_croak(aTHX_ "panic: op_contextualize bad context");
+           return o;
+    }
+}
+
+/*
+=head1 Optree Manipulation Functions
+
+=for apidoc Am|OP*|op_linklist|OP *o
+This function is the implementation of the L</LINKLIST> macro. It should
+not be called directly.
+
+=cut
+*/
+
+OP *
+Perl_op_linklist(pTHX_ OP *o)
 {
     OP *first;
 
-    PERL_ARGS_ASSERT_LINKLIST;
+    PERL_ARGS_ASSERT_OP_LINKLIST;
 
     if (o->op_next)
        return o->op_next;
@@ -918,25 +965,28 @@ Perl_scalar(pTHX_ OP *o)
     case OP_LEAVETRY:
        kid = cLISTOPo->op_first;
        scalar(kid);
-       while ((kid = kid->op_sibling)) {
-           if (kid->op_sibling)
-               scalarvoid(kid);
-           else
+       kid = kid->op_sibling;
+    do_kids:
+       while (kid) {
+           OP *sib = kid->op_sibling;
+           if (sib && kid->op_type != OP_LEAVEWHEN) {
+               if (sib->op_type == OP_BREAK && sib->op_flags & OPf_SPECIAL) {
+                   scalar(kid);
+                   scalarvoid(sib);
+                   break;
+               } else
+                   scalarvoid(kid);
+           } else
                scalar(kid);
+           kid = sib;
        }
        PL_curcop = &PL_compiling;
        break;
     case OP_SCOPE:
     case OP_LINESEQ:
     case OP_LIST:
-       for (kid = cLISTOPo->op_first; kid; kid = kid->op_sibling) {
-           if (kid->op_sibling)
-               scalarvoid(kid);
-           else
-               scalar(kid);
-       }
-       PL_curcop = &PL_compiling;
-       break;
+       kid = cLISTOPo->op_first;
+       goto do_kids;
     case OP_SORT:
        Perl_ck_warner(aTHX_ packWARN(WARN_VOID), "Useless use of sort in scalar context");
        break;
@@ -980,7 +1030,7 @@ Perl_scalarvoid(pTHX_ OP *o)
     want = o->op_flags & OPf_WANT;
     if ((want && want != OPf_WANT_SCALAR)
         || (PL_parser && PL_parser->error_count)
-        || o->op_type == OP_RETURN)
+        || o->op_type == OP_RETURN || o->op_type == OP_REQUIRE || o->op_type == OP_LEAVEWHEN)
     {
        return o;
     }
@@ -1081,15 +1131,35 @@ Perl_scalarvoid(pTHX_ OP *o)
            useless = OP_DESC(o);
        break;
 
+    case OP_SPLIT:
+       kid = cLISTOPo->op_first;
+       if (kid && kid->op_type == OP_PUSHRE
+#ifdef USE_ITHREADS
+               && !((PMOP*)kid)->op_pmreplrootu.op_pmtargetoff)
+#else
+               && !((PMOP*)kid)->op_pmreplrootu.op_pmtargetgv)
+#endif
+           useless = OP_DESC(o);
+       break;
+
     case OP_NOT:
        kid = cUNOPo->op_first;
        if (kid->op_type != OP_MATCH && kid->op_type != OP_SUBST &&
-           kid->op_type != OP_TRANS) {
+           kid->op_type != OP_TRANS && kid->op_type != OP_TRANSR) {
                goto func_ops;
        }
        useless = "negative pattern binding (!~)";
        break;
 
+    case OP_SUBST:
+       if (cPMOPo->op_pmflags & PMf_NONDESTRUCT)
+           useless = "non-destructive substitution (s///r)";
+       break;
+
+    case OP_TRANSR:
+       useless = "non-destructive transliteration (tr///r)";
+       break;
+
     case OP_RV2GV:
     case OP_RV2SV:
     case OP_RV2AV:
@@ -1210,10 +1280,6 @@ Perl_scalarvoid(pTHX_ OP *o)
     case OP_ENTEREVAL:
        scalarkids(o);
        break;
-    case OP_REQUIRE:
-       /* all requires must return a boolean value */
-       o->op_flags &= ~OPf_WANT;
-       /* FALL THROUGH */
     case OP_SCALAR:
        return scalar(o);
     }
@@ -1284,28 +1350,27 @@ Perl_list(pTHX_ OP *o)
     case OP_LEAVETRY:
        kid = cLISTOPo->op_first;
        list(kid);
-       while ((kid = kid->op_sibling)) {
-           if (kid->op_sibling)
-               scalarvoid(kid);
-           else
+       kid = kid->op_sibling;
+    do_kids:
+       while (kid) {
+           OP *sib = kid->op_sibling;
+           if (sib && kid->op_type != OP_LEAVEWHEN) {
+               if (sib->op_type == OP_BREAK && sib->op_flags & OPf_SPECIAL) {
+                   list(kid);
+                   scalarvoid(sib);
+                   break;
+               } else
+                   scalarvoid(kid);
+           } else
                list(kid);
+           kid = sib;
        }
        PL_curcop = &PL_compiling;
        break;
     case OP_SCOPE:
     case OP_LINESEQ:
-       for (kid = cLISTOPo->op_first; kid; kid = kid->op_sibling) {
-           if (kid->op_sibling)
-               scalarvoid(kid);
-           else
-               list(kid);
-       }
-       PL_curcop = &PL_compiling;
-       break;
-    case OP_REQUIRE:
-       /* all requires must return a boolean value */
-       o->op_flags &= ~OPf_WANT;
-       return scalar(o);
+       kid = cLISTOPo->op_first;
+       goto do_kids;
     }
     return o;
 }
@@ -1343,24 +1408,28 @@ S_modkids(pTHX_ OP *o, I32 type)
     if (o && o->op_flags & OPf_KIDS) {
         OP *kid;
        for (kid = cLISTOPo->op_first; kid; kid = kid->op_sibling)
-           mod(kid, type);
+           op_lvalue(kid, type);
     }
     return o;
 }
 
-/* Propagate lvalue ("modifiable") context to an op and its children.
- * 'type' represents the context type, roughly based on the type of op that
- * would do the modifying, although local() is represented by OP_NULL.
- * It's responsible for detecting things that can't be modified,  flag
- * things that need to behave specially in an lvalue context (e.g., "$$x = 5"
- * might have to vivify a reference in $x), and so on.
- *
- * For example, "$a+1 = 2" would cause mod() to be called with o being
- * OP_ADD and type being OP_SASSIGN, and would output an error.
- */
+/*
+=for apidoc Amx|OP *|op_lvalue|OP *o|I32 type
+
+Propagate lvalue ("modifiable") context to an op and its children.
+I<type> represents the context type, roughly based on the type of op that
+would do the modifying, although C<local()> is represented by OP_NULL,
+because it has no op type of its own (it is signalled by a flag on
+the lvalue op).  This function detects things that can't be modified,
+such as C<$x+1>, and generates errors for them.  It also flags things
+that need to behave specially in an lvalue context, such as C<$$x>
+which might have to vivify a reference in C<$x>.
+
+=cut
+*/
 
 OP *
-Perl_mod(pTHX_ OP *o, I32 type)
+Perl_op_lvalue(pTHX_ OP *o, I32 type)
 {
     dVAR;
     OP *kid;
@@ -1544,7 +1613,7 @@ Perl_mod(pTHX_ OP *o, I32 type)
     case OP_COND_EXPR:
        localize = 1;
        for (kid = cUNOPo->op_first->op_sibling; kid; kid = kid->op_sibling)
-           mod(kid, type);
+           op_lvalue(kid, type);
        break;
 
     case OP_RV2AV:
@@ -1632,7 +1701,7 @@ Perl_mod(pTHX_ OP *o, I32 type)
        o->op_targ = pad_alloc(o->op_type, SVs_PADMY);
        assert(SvTYPE(PAD_SV(o->op_targ)) == SVt_NULL);
        if (o->op_flags & OPf_KIDS)
-           mod(cBINOPo->op_first->op_sibling, type);
+           op_lvalue(cBINOPo->op_first->op_sibling, type);
        break;
 
     case OP_AELEM:
@@ -1653,7 +1722,7 @@ Perl_mod(pTHX_ OP *o, I32 type)
     case OP_LINESEQ:
        localize = 0;
        if (o->op_flags & OPf_KIDS)
-           mod(cLISTOPo->op_last, type);
+           op_lvalue(cLISTOPo->op_last, type);
        break;
 
     case OP_NULL:
@@ -1663,27 +1732,27 @@ Perl_mod(pTHX_ OP *o, I32 type)
        else if (!(o->op_flags & OPf_KIDS))
            break;
        if (o->op_targ != OP_LIST) {
-           mod(cBINOPo->op_first, type);
+           op_lvalue(cBINOPo->op_first, type);
            break;
        }
        /* FALL THROUGH */
     case OP_LIST:
        localize = 0;
        for (kid = cLISTOPo->op_first; kid; kid = kid->op_sibling)
-           mod(kid, type);
+           op_lvalue(kid, type);
        break;
 
     case OP_RETURN:
        if (type != OP_LEAVESUBLV)
            goto nomod;
-       break; /* mod()ing was handled by ck_return() */
+       break; /* op_lvalue()ing was handled by ck_return() */
     }
 
     /* [20011101.069] File test operators interpret OPf_REF to mean that
        their argument is a filehandle; thus \stat(".") should not set
        it. AMS 20011102 */
     if (type == OP_REFGEN &&
-        PL_check[o->op_type] == MEMBER_TO_FPTR(Perl_ck_ftst))
+        PL_check[o->op_type] == Perl_ck_ftst)
         return o;
 
     if (type != OP_LEAVESUBLV)
@@ -1749,6 +1818,7 @@ S_scalar_mod_type(const OP *o, I32 type)
     case OP_CONCAT:
     case OP_SUBST:
     case OP_TRANS:
+    case OP_TRANSR:
     case OP_READ:
     case OP_SYSREAD:
     case OP_RECV:
@@ -1913,7 +1983,7 @@ S_dup_attrlist(pTHX_ OP *o)
        rop = NULL;
        for (o = cLISTOPo->op_first; o; o=o->op_sibling) {
            if (o->op_type == OP_CONST)
-               rop = append_elem(OP_LIST, rop,
+               rop = op_append_elem(OP_LIST, rop,
                                  newSVOP(OP_CONST, o->op_flags,
                                          SvREFCNT_inc_NN(cSVOPo->op_sv)));
        }
@@ -1949,9 +2019,9 @@ S_apply_attrs(pTHX_ HV *stash, SV *target, OP *attrs, bool for_my)
        Perl_load_module(aTHX_ PERL_LOADMOD_IMPORT_OPS,
                         newSVpvs(ATTRSMODULE),
                         NULL,
-                        prepend_elem(OP_LIST,
+                        op_prepend_elem(OP_LIST,
                                      newSVOP(OP_CONST, 0, stashsv),
-                                     prepend_elem(OP_LIST,
+                                     op_prepend_elem(OP_LIST,
                                                   newSVOP(OP_CONST, 0,
                                                           newRV(target)),
                                                   dup_attrlist(attrs))));
@@ -1986,23 +2056,23 @@ S_apply_attrs_my(pTHX_ HV *stash, OP *target, OP *attrs, OP **imopsp)
 
     arg = newOP(OP_PADSV, 0);
     arg->op_targ = target->op_targ;
-    arg = prepend_elem(OP_LIST,
+    arg = op_prepend_elem(OP_LIST,
                       newSVOP(OP_CONST, 0, stashsv),
-                      prepend_elem(OP_LIST,
+                      op_prepend_elem(OP_LIST,
                                    newUNOP(OP_REFGEN, 0,
-                                           mod(arg, OP_REFGEN)),
+                                           op_lvalue(arg, OP_REFGEN)),
                                    dup_attrlist(attrs)));
 
     /* Fake up a method call to import */
     meth = newSVpvs_share("import");
     imop = convert(OP_ENTERSUB, OPf_STACKED|OPf_SPECIAL|OPf_WANT_VOID,
-                  append_elem(OP_LIST,
-                              prepend_elem(OP_LIST, pack, list(arg)),
+                  op_append_elem(OP_LIST,
+                              op_prepend_elem(OP_LIST, pack, list(arg)),
                               newSVOP(OP_METHOD_NAMED, 0, meth)));
     imop->op_private |= OPpENTERSUB_NOMOD;
 
     /* Combine the ops. */
-    *imopsp = append_elem(OP_LIST, *imopsp, imop);
+    *imopsp = op_append_elem(OP_LIST, *imopsp, imop);
 }
 
 /*
@@ -2039,7 +2109,7 @@ Perl_apply_attrs_string(pTHX_ const char *stashpv, CV *cv,
         if (len) {
             const char * const sstr = attrstr;
             for (; !isSPACE(*attrstr) && len; --len, ++attrstr) ;
-            attrs = append_elem(OP_LIST, attrs,
+            attrs = op_append_elem(OP_LIST, attrs,
                                 newSVOP(OP_CONST, 0,
                                         newSVpvn(sstr, attrstr-sstr)));
         }
@@ -2047,9 +2117,9 @@ Perl_apply_attrs_string(pTHX_ const char *stashpv, CV *cv,
 
     Perl_load_module(aTHX_ PERL_LOADMOD_IMPORT_OPS,
                     newSVpvs(ATTRSMODULE),
-                     NULL, prepend_elem(OP_LIST,
+                     NULL, op_prepend_elem(OP_LIST,
                                  newSVOP(OP_CONST, 0, newSVpv(stashpv,0)),
-                                 prepend_elem(OP_LIST,
+                                 op_prepend_elem(OP_LIST,
                                               newSVOP(OP_CONST, 0,
                                                       newRV(MUTABLE_SV(cv))),
                                                attrs)));
@@ -2160,11 +2230,11 @@ Perl_my_attrs(pTHX_ OP *o, OP *attrs)
     o = my_kid(o, attrs, &rops);
     if (rops) {
        if (maybe_scalar && o->op_type == OP_PADSV) {
-           o = scalar(append_list(OP_LIST, (LISTOP*)rops, (LISTOP*)o));
+           o = scalar(op_append_list(OP_LIST, rops, o));
            o->op_private |= OPpLVAL_INTRO;
        }
        else
-           o = append_list(OP_LIST, (LISTOP*)o, (LISTOP*)rops);
+           o = op_append_list(OP_LIST, o, rops);
     }
     PL_parser->in_my = FALSE;
     PL_parser->in_my_stash = NULL;
@@ -2194,7 +2264,10 @@ Perl_bind_match(pTHX_ I32 type, OP *left, OP *right)
          || ltype == OP_PADHV) && ckWARN(WARN_MISC))
     {
       const char * const desc
-         = PL_op_desc[(rtype == OP_SUBST || rtype == OP_TRANS)
+         = PL_op_desc[(
+                         rtype == OP_SUBST || rtype == OP_TRANS
+                      || rtype == OP_TRANSR
+                      )
                       ? (int)rtype : OP_MATCH];
       const char * const sample = ((ltype == OP_RV2AV || ltype == OP_PADAV)
             ? "@array" : "%hash");
@@ -2210,9 +2283,17 @@ Perl_bind_match(pTHX_ I32 type, OP *left, OP *right)
        no_bareword_allowed(right);
     }
 
-    ismatchop = rtype == OP_MATCH ||
-               rtype == OP_SUBST ||
-               rtype == OP_TRANS;
+    /* !~ doesn't make sense with /r, so error on it for now */
+    if (rtype == OP_SUBST && (cPMOPx(right)->op_pmflags & PMf_NONDESTRUCT) &&
+       type == OP_NOT)
+       yyerror("Using !~ with s///r doesn't make sense");
+    if (rtype == OP_TRANSR && type == OP_NOT)
+       yyerror("Using !~ with tr///r doesn't make sense");
+
+    ismatchop = (rtype == OP_MATCH ||
+                rtype == OP_SUBST ||
+                rtype == OP_TRANS || rtype == OP_TRANSR)
+            && !(right->op_flags & OPf_SPECIAL);
     if (ismatchop && right->op_private & OPpTARGET_MY) {
        right->op_targ = 0;
        right->op_private &= ~OPpTARGET_MY;
@@ -2221,16 +2302,18 @@ Perl_bind_match(pTHX_ I32 type, OP *left, OP *right)
        OP *newleft;
 
        right->op_flags |= OPf_STACKED;
-       if (rtype != OP_MATCH &&
+       if (rtype != OP_MATCH && rtype != OP_TRANSR &&
             ! (rtype == OP_TRANS &&
-               right->op_private & OPpTRANS_IDENTICAL))
-           newleft = mod(left, rtype);
+               right->op_private & OPpTRANS_IDENTICAL) &&
+           ! (rtype == OP_SUBST &&
+              (cPMOPx(right)->op_pmflags & PMf_NONDESTRUCT)))
+           newleft = op_lvalue(left, rtype);
        else
            newleft = left;
-       if (right->op_type == OP_TRANS)
+       if (right->op_type == OP_TRANS || right->op_type == OP_TRANSR)
            o = newBINOP(OP_NULL, OPf_STACKED, scalar(newleft), right);
        else
-           o = prepend_elem(rtype, scalar(newleft), right);
+           o = op_prepend_elem(rtype, scalar(newleft), right);
        if (type == OP_NOT)
            return newUNOP(OP_NOT, 0, scalar(o));
        return o;
@@ -2248,13 +2331,27 @@ Perl_invert(pTHX_ OP *o)
     return newUNOP(OP_NOT, OPf_SPECIAL, scalar(o));
 }
 
+/*
+=for apidoc Amx|OP *|op_scope|OP *o
+
+Wraps up an op tree with some additional ops so that at runtime a dynamic
+scope will be created.  The original ops run in the new dynamic scope,
+and then, provided that they exit normally, the scope will be unwound.
+The additional ops used to create and unwind the dynamic scope will
+normally be an C<enter>/C<leave> pair, but a C<scope> op may be used
+instead if the ops are simple enough to not need the full dynamic scope
+structure.
+
+=cut
+*/
+
 OP *
-Perl_scope(pTHX_ OP *o)
+Perl_op_scope(pTHX_ OP *o)
 {
     dVAR;
     if (o) {
        if (o->op_flags & OPf_PARENS || PERLDB_NOOPT || PL_tainting) {
-           o = prepend_elem(OP_LINESEQ, newOP(OP_ENTER, 0), o);
+           o = op_prepend_elem(OP_LINESEQ, newOP(OP_ENTER, 0), o);
            o->op_type = OP_LEAVE;
            o->op_ppaddr = PL_ppaddr[OP_LEAVE];
        }
@@ -2278,17 +2375,21 @@ Perl_scope(pTHX_ OP *o)
     }
     return o;
 }
-       
+
 int
 Perl_block_start(pTHX_ int full)
 {
     dVAR;
     const int retval = PL_savestack_ix;
+
     pad_block_start(full);
     SAVEHINTS();
     PL_hints &= ~HINT_BLOCK_SCOPE;
     SAVECOMPILEWARNINGS();
     PL_compiling.cop_warnings = DUP_WARNINGS(PL_compiling.cop_warnings);
+
+    CALL_BLOCK_HOOKS(bhk_start, full);
+
     return retval;
 }
 
@@ -2297,15 +2398,40 @@ Perl_block_end(pTHX_ I32 floor, OP *seq)
 {
     dVAR;
     const int needblockscope = PL_hints & HINT_BLOCK_SCOPE;
-    OP* const retval = scalarseq(seq);
+    OP* retval = scalarseq(seq);
+
+    CALL_BLOCK_HOOKS(bhk_pre_end, &retval);
+
     LEAVE_SCOPE(floor);
     CopHINTS_set(&PL_compiling, PL_hints);
     if (needblockscope)
        PL_hints |= HINT_BLOCK_SCOPE; /* propagate out */
     pad_leavemy();
+
+    CALL_BLOCK_HOOKS(bhk_post_end, &retval);
+
     return retval;
 }
 
+/*
+=head1 Compile-time scope hooks
+
+=for apidoc Ao||blockhook_register
+
+Register a set of hooks to be called when the Perl lexical scope changes
+at compile time. See L<perlguts/"Compile-time scope hooks">.
+
+=cut
+*/
+
+void
+Perl_blockhook_register(pTHX_ BHK *hk)
+{
+    PERL_ARGS_ASSERT_BLOCKHOOK_REGISTER;
+
+    Perl_av_create_and_push(aTHX_ &PL_blockhooks, newSViv(PTR2IV(hk)));
+}
+
 STATIC OP *
 S_newDEFSVOP(pTHX)
 {
@@ -2334,7 +2460,10 @@ Perl_newPROG(pTHX_ OP *o)
        PL_eval_root = newUNOP(OP_LEAVEEVAL,
                               ((PL_in_eval & EVAL_KEEPERR)
                                ? OPf_SPECIAL : 0), o);
-       PL_eval_start = linklist(PL_eval_root);
+       /* don't use LINKLIST, since PL_eval_root might indirect through
+        * a rather expensive function call and LINKLIST evaluates its
+        * argument more than once */
+       PL_eval_start = op_linklist(PL_eval_root);
        PL_eval_root->op_private |= OPpREFCOUNTED;
        OpREFCNT_set(PL_eval_root, 1);
        PL_eval_root->op_next = 0;
@@ -2347,7 +2476,7 @@ Perl_newPROG(pTHX_ OP *o)
            S_op_destroy(aTHX_ o);
            return;
        }
-       PL_main_root = scope(sawparens(scalarvoid(o)));
+       PL_main_root = op_scope(sawparens(scalarvoid(o)));
        PL_curcop = &PL_compiling;
        PL_main_start = LINKLIST(PL_main_root);
        PL_main_root->op_private |= OPpREFCOUNTED;
@@ -2426,7 +2555,7 @@ Perl_localize(pTHX_ OP *o, I32 lex)
     if (lex)
        o = my(o);
     else
-       o = mod(o, OP_NULL);            /* a bit kludgey */
+       o = op_lvalue(o, OP_NULL);              /* a bit kludgey */
     PL_parser->in_my = FALSE;
     PL_parser->in_my_stash = NULL;
     return o;
@@ -2440,7 +2569,7 @@ Perl_jmaybe(pTHX_ OP *o)
     if (o->op_type == OP_LIST) {
        OP * const o2
            = newSVREF(newGVOP(OP_GV, 0, gv_fetchpvs(";", GV_ADD|GV_NOTQUAL, SVt_PV)));
-       o = convert(OP_JOIN, 0, prepend_elem(OP_LIST, o2, o));
+       o = convert(OP_JOIN, 0, op_prepend_elem(OP_LIST, o2, o));
     }
     return o;
 }
@@ -2494,6 +2623,7 @@ S_fold_constants(pTHX_ register OP *o)
     case OP_SLE:
     case OP_SGE:
     case OP_SCMP:
+    case OP_SPRINTF:
        /* XXX what about the numeric ops? */
        if (PL_hints & HINT_LOCALE)
            goto nope;
@@ -2612,7 +2742,7 @@ S_gen_constant_list(pTHX_ register OP *o)
     o->op_ppaddr = PL_ppaddr[OP_RV2AV];
     o->op_flags &= ~OPf_REF;   /* treat \(1..2) like an ordinary list */
     o->op_flags |= OPf_PARENS; /* and flatten \(1..2,3) */
-    o->op_opt = 0;             /* needs to be revisited in peep() */
+    o->op_opt = 0;             /* needs to be revisited in rpeep() */
     curop = ((UNOP*)o)->op_first;
     ((UNOP*)o)->op_first = newSVOP(OP_CONST, 0, SvREFCNT_inc_NN(*PL_stack_sp--));
 #ifdef PERL_MAD
@@ -2620,7 +2750,7 @@ S_gen_constant_list(pTHX_ register OP *o)
 #else
     op_free(curop);
 #endif
-    linklist(o);
+    LINKLIST(o);
     return list(o);
 }
 
@@ -2647,10 +2777,27 @@ Perl_convert(pTHX_ I32 type, I32 flags, OP *o)
     return fold_constants(o);
 }
 
+/*
+=head1 Optree Manipulation Functions
+*/
+
 /* List constructors */
 
+/*
+=for apidoc Am|OP *|op_append_elem|I32 optype|OP *first|OP *last
+
+Append an item to the list of ops contained directly within a list-type
+op, returning the lengthened list.  I<first> is the list-type op,
+and I<last> is the op to append to the list.  I<optype> specifies the
+intended opcode for the list.  If I<first> is not already a list of the
+right type, it will be upgraded into one.  If either I<first> or I<last>
+is null, the other is returned unchanged.
+
+=cut
+*/
+
 OP *
-Perl_append_elem(pTHX_ I32 type, OP *first, OP *last)
+Perl_op_append_elem(pTHX_ I32 type, OP *first, OP *last)
 {
     if (!first)
        return last;
@@ -2674,48 +2821,74 @@ Perl_append_elem(pTHX_ I32 type, OP *first, OP *last)
     return first;
 }
 
+/*
+=for apidoc Am|OP *|op_append_list|I32 optype|OP *first|OP *last
+
+Concatenate the lists of ops contained directly within two list-type ops,
+returning the combined list.  I<first> and I<last> are the list-type ops
+to concatenate.  I<optype> specifies the intended opcode for the list.
+If either I<first> or I<last> is not already a list of the right type,
+it will be upgraded into one.  If either I<first> or I<last> is null,
+the other is returned unchanged.
+
+=cut
+*/
+
 OP *
-Perl_append_list(pTHX_ I32 type, LISTOP *first, LISTOP *last)
+Perl_op_append_list(pTHX_ I32 type, OP *first, OP *last)
 {
     if (!first)
-       return (OP*)last;
+       return last;
 
     if (!last)
-       return (OP*)first;
+       return first;
 
     if (first->op_type != (unsigned)type)
-       return prepend_elem(type, (OP*)first, (OP*)last);
+       return op_prepend_elem(type, first, last);
 
     if (last->op_type != (unsigned)type)
-       return append_elem(type, (OP*)first, (OP*)last);
+       return op_append_elem(type, first, last);
 
-    first->op_last->op_sibling = last->op_first;
-    first->op_last = last->op_last;
+    ((LISTOP*)first)->op_last->op_sibling = ((LISTOP*)last)->op_first;
+    ((LISTOP*)first)->op_last = ((LISTOP*)last)->op_last;
     first->op_flags |= (last->op_flags & OPf_KIDS);
 
 #ifdef PERL_MAD
-    if (last->op_first && first->op_madprop) {
-       MADPROP *mp = last->op_first->op_madprop;
+    if (((LISTOP*)last)->op_first && first->op_madprop) {
+       MADPROP *mp = ((LISTOP*)last)->op_first->op_madprop;
        if (mp) {
            while (mp->mad_next)
                mp = mp->mad_next;
            mp->mad_next = first->op_madprop;
        }
        else {
-           last->op_first->op_madprop = first->op_madprop;
+           ((LISTOP*)last)->op_first->op_madprop = first->op_madprop;
        }
     }
     first->op_madprop = last->op_madprop;
     last->op_madprop = 0;
 #endif
 
-    S_op_destroy(aTHX_ (OP*)last);
+    S_op_destroy(aTHX_ last);
 
-    return (OP*)first;
+    return first;
 }
 
+/*
+=for apidoc Am|OP *|op_prepend_elem|I32 optype|OP *first|OP *last
+
+Prepend an item to the list of ops contained directly within a list-type
+op, returning the lengthened list.  I<first> is the op to prepend to the
+list, and I<last> is the list-type op.  I<optype> specifies the intended
+opcode for the list.  If I<last> is not already a list of the right type,
+it will be upgraded into one.  If either I<first> or I<last> is null,
+the other is returned unchanged.
+
+=cut
+*/
+
 OP *
-Perl_prepend_elem(pTHX_ I32 type, OP *first, OP *last)
+Perl_op_prepend_elem(pTHX_ I32 type, OP *first, OP *last)
 {
     if (!first)
        return last;
@@ -2994,6 +3167,17 @@ Perl_mad_free(pTHX_ MADPROP* mp)
 
 #endif
 
+/*
+=head1 Optree construction
+
+=for apidoc Am|OP *|newNULLLIST
+
+Constructs, checks, and returns a new C<stub> op, which represents an
+empty list expression.
+
+=cut
+*/
+
 OP *
 Perl_newNULLLIST(pTHX)
 {
@@ -3009,12 +3193,26 @@ S_force_list(pTHX_ OP *o)
     return o;
 }
 
+/*
+=for apidoc Am|OP *|newLISTOP|I32 type|I32 flags|OP *first|OP *last
+
+Constructs, checks, and returns an op of any list type.  I<type> is
+the opcode.  I<flags> gives the eight bits of C<op_flags>, except that
+C<OPf_KIDS> will be set automatically if required.  I<first> and I<last>
+supply up to two ops to be direct children of the list op; they are
+consumed by this function and become part of the constructed op tree.
+
+=cut
+*/
+
 OP *
 Perl_newLISTOP(pTHX_ I32 type, I32 flags, OP *first, OP *last)
 {
     dVAR;
     LISTOP *listop;
 
+    assert((PL_opargs[type] & OA_CLASS_MASK) == OA_LISTOP);
+
     NewOp(1101, listop, 1, LISTOP);
 
     listop->op_type = (OPCODE)type;
@@ -3043,11 +3241,28 @@ Perl_newLISTOP(pTHX_ I32 type, I32 flags, OP *first, OP *last)
     return CHECKOP(type, listop);
 }
 
+/*
+=for apidoc Am|OP *|newOP|I32 type|I32 flags
+
+Constructs, checks, and returns an op of any base type (any type that
+has no extra fields).  I<type> is the opcode.  I<flags> gives the
+eight bits of C<op_flags>, and, shifted up eight bits, the eight bits
+of C<op_private>.
+
+=cut
+*/
+
 OP *
 Perl_newOP(pTHX_ I32 type, I32 flags)
 {
     dVAR;
     OP *o;
+
+    assert((PL_opargs[type] & OA_CLASS_MASK) == OA_BASEOP
+       || (PL_opargs[type] & OA_CLASS_MASK) == OA_BASEOP_OR_UNOP
+       || (PL_opargs[type] & OA_CLASS_MASK) == OA_FILESTATOP
+       || (PL_opargs[type] & OA_CLASS_MASK) == OA_LOOPEXOP);
+
     NewOp(1101, o, 1, OP);
     o->op_type = (OPCODE)type;
     o->op_ppaddr = PL_ppaddr[type];
@@ -3065,12 +3280,34 @@ Perl_newOP(pTHX_ I32 type, I32 flags)
     return CHECKOP(type, o);
 }
 
+/*
+=for apidoc Am|OP *|newUNOP|I32 type|I32 flags|OP *first
+
+Constructs, checks, and returns an op of any unary type.  I<type> is
+the opcode.  I<flags> gives the eight bits of C<op_flags>, except that
+C<OPf_KIDS> will be set automatically if required, and, shifted up eight
+bits, the eight bits of C<op_private>, except that the bit with value 1
+is automatically set.  I<first> supplies an optional op to be the direct
+child of the unary op; it is consumed by this function and become part
+of the constructed op tree.
+
+=cut
+*/
+
 OP *
 Perl_newUNOP(pTHX_ I32 type, I32 flags, OP *first)
 {
     dVAR;
     UNOP *unop;
 
+    assert((PL_opargs[type] & OA_CLASS_MASK) == OA_UNOP
+       || (PL_opargs[type] & OA_CLASS_MASK) == OA_BASEOP_OR_UNOP
+       || (PL_opargs[type] & OA_CLASS_MASK) == OA_FILESTATOP
+       || (PL_opargs[type] & OA_CLASS_MASK) == OA_LOOPEXOP
+       || type == OP_SASSIGN
+       || type == OP_ENTERTRY
+       || type == OP_NULL );
+
     if (!first)
        first = newOP(OP_STUB, 0);
     if (PL_opargs[type] & OA_MARK)
@@ -3089,11 +3326,29 @@ Perl_newUNOP(pTHX_ I32 type, I32 flags, OP *first)
     return fold_constants((OP *) unop);
 }
 
+/*
+=for apidoc Am|OP *|newBINOP|I32 type|I32 flags|OP *first|OP *last
+
+Constructs, checks, and returns an op of any binary type.  I<type>
+is the opcode.  I<flags> gives the eight bits of C<op_flags>, except
+that C<OPf_KIDS> will be set automatically, and, shifted up eight bits,
+the eight bits of C<op_private>, except that the bit with value 1 or
+2 is automatically set as required.  I<first> and I<last> supply up to
+two ops to be the direct children of the binary op; they are consumed
+by this function and become part of the constructed op tree.
+
+=cut
+*/
+
 OP *
 Perl_newBINOP(pTHX_ I32 type, I32 flags, OP *first, OP *last)
 {
     dVAR;
     BINOP *binop;
+
+    assert((PL_opargs[type] & OA_CLASS_MASK) == OA_BINOP
+       || type == OP_SASSIGN || type == OP_NULL );
+
     NewOp(1101, binop, 1, BINOP);
 
     if (!first)
@@ -3483,12 +3738,24 @@ S_pmtrans(pTHX_ OP *o, OP *expr, OP *repl)
     return o;
 }
 
+/*
+=for apidoc Am|OP *|newPMOP|I32 type|I32 flags
+
+Constructs, checks, and returns an op of any pattern matching type.
+I<type> is the opcode.  I<flags> gives the eight bits of C<op_flags>
+and, shifted up eight bits, the eight bits of C<op_private>.
+
+=cut
+*/
+
 OP *
 Perl_newPMOP(pTHX_ I32 type, I32 flags)
 {
     dVAR;
     PMOP *pmop;
 
+    assert((PL_opargs[type] & OA_CLASS_MASK) == OA_PMOP);
+
     NewOp(1101, pmop, 1, PMOP);
     pmop->op_type = (OPCODE)type;
     pmop->op_ppaddr = PL_ppaddr[type];
@@ -3497,8 +3764,25 @@ Perl_newPMOP(pTHX_ I32 type, I32 flags)
 
     if (PL_hints & HINT_RE_TAINT)
        pmop->op_pmflags |= PMf_RETAINT;
-    if (PL_hints & HINT_LOCALE)
+    if (PL_hints & HINT_LOCALE) {
        pmop->op_pmflags |= PMf_LOCALE;
+    }
+    else if ((! (PL_hints & HINT_BYTES)) && (PL_hints & HINT_UNI_8_BIT)) {
+        pmop->op_pmflags |= RXf_PMf_UNICODE;
+    }
+    if (PL_hints & HINT_RE_FLAGS) {
+        SV *reflags = Perl_refcounted_he_fetch_pvn(aTHX_
+         PL_compiling.cop_hints_hash, STR_WITH_LEN("reflags"), 0, 0
+        );
+        if (reflags && SvOK(reflags)) pmop->op_pmflags |= SvIV(reflags);
+        reflags = Perl_refcounted_he_fetch_pvn(aTHX_
+         PL_compiling.cop_hints_hash, STR_WITH_LEN("reflags_dul"), 0, 0
+        );
+        if (reflags && SvOK(reflags)) {
+            pmop->op_pmflags &= ~(RXf_PMf_LOCALE|RXf_PMf_UNICODE);
+            pmop->op_pmflags |= SvIV(reflags);
+        }
+    }
 
 
 #ifdef USE_ITHREADS
@@ -3551,7 +3835,10 @@ Perl_pmruntime(pTHX_ OP *o, OP *expr, bool isreg)
 
     PERL_ARGS_ASSERT_PMRUNTIME;
 
-    if (o->op_type == OP_SUBST || o->op_type == OP_TRANS) {
+    if (
+        o->op_type == OP_SUBST
+     || o->op_type == OP_TRANS || o->op_type == OP_TRANSR
+    ) {
        /* last element in list is the replacement; pop it */
        OP* kid;
        repl = cLISTOPx(expr)->op_last;
@@ -3573,7 +3860,7 @@ Perl_pmruntime(pTHX_ OP *o, OP *expr, bool isreg)
        op_free(oe);
     }
 
-    if (o->op_type == OP_TRANS) {
+    if (o->op_type == OP_TRANS || o->op_type == OP_TRANSR) {
        return pmtrans(o, expr, repl);
     }
 
@@ -3643,7 +3930,7 @@ Perl_pmruntime(pTHX_ OP *o, OP *expr, bool isreg)
            expr->op_next = (OP*)rcop;
        }
 
-       prepend_elem(o->op_type, scalar((OP*)rcop), o);
+       op_prepend_elem(o->op_type, scalar((OP*)rcop), o);
     }
 
     if (repl) {
@@ -3697,7 +3984,7 @@ Perl_pmruntime(pTHX_ OP *o, OP *expr, bool isreg)
                     || RX_EXTFLAGS(PM_GETRE(pm)) & RXf_EVAL_SEEN)))
        {
            pm->op_pmflags |= PMf_CONST;        /* const for long enough */
-           prepend_elem(o->op_type, scalar(repl), o);
+           op_prepend_elem(o->op_type, scalar(repl), o);
        }
        else {
            if (curop == repl && !PM_GETRE(pm)) { /* Has variables. */
@@ -3725,6 +4012,17 @@ Perl_pmruntime(pTHX_ OP *o, OP *expr, bool isreg)
     return (OP*)pm;
 }
 
+/*
+=for apidoc Am|OP *|newSVOP|I32 type|I32 flags|SV *sv
+
+Constructs, checks, and returns an op of any type that involves an
+embedded SV.  I<type> is the opcode.  I<flags> gives the eight bits
+of C<op_flags>.  I<sv> gives the SV to embed in the op; this function
+takes ownership of one reference to it.
+
+=cut
+*/
+
 OP *
 Perl_newSVOP(pTHX_ I32 type, I32 flags, SV *sv)
 {
@@ -3733,6 +4031,10 @@ Perl_newSVOP(pTHX_ I32 type, I32 flags, SV *sv)
 
     PERL_ARGS_ASSERT_NEWSVOP;
 
+    assert((PL_opargs[type] & OA_CLASS_MASK) == OA_SVOP
+       || (PL_opargs[type] & OA_CLASS_MASK) == OA_PVOP_OR_SVOP
+       || (PL_opargs[type] & OA_CLASS_MASK) == OA_FILESTATOP);
+
     NewOp(1101, svop, 1, SVOP);
     svop->op_type = (OPCODE)type;
     svop->op_ppaddr = PL_ppaddr[type];
@@ -3747,6 +4049,21 @@ Perl_newSVOP(pTHX_ I32 type, I32 flags, SV *sv)
 }
 
 #ifdef USE_ITHREADS
+
+/*
+=for apidoc Am|OP *|newPADOP|I32 type|I32 flags|SV *sv
+
+Constructs, checks, and returns an op of any type that involves a
+reference to a pad element.  I<type> is the opcode.  I<flags> gives the
+eight bits of C<op_flags>.  A pad slot is automatically allocated, and
+is populated with I<sv>; this function takes ownership of one reference
+to it.
+
+This function only exists if Perl has been compiled to use ithreads.
+
+=cut
+*/
+
 OP *
 Perl_newPADOP(pTHX_ I32 type, I32 flags, SV *sv)
 {
@@ -3755,6 +4072,10 @@ Perl_newPADOP(pTHX_ I32 type, I32 flags, SV *sv)
 
     PERL_ARGS_ASSERT_NEWPADOP;
 
+    assert((PL_opargs[type] & OA_CLASS_MASK) == OA_SVOP
+       || (PL_opargs[type] & OA_CLASS_MASK) == OA_PVOP_OR_SVOP
+       || (PL_opargs[type] & OA_CLASS_MASK) == OA_FILESTATOP);
+
     NewOp(1101, padop, 1, PADOP);
     padop->op_type = (OPCODE)type;
     padop->op_ppaddr = PL_ppaddr[type];
@@ -3771,7 +4092,20 @@ Perl_newPADOP(pTHX_ I32 type, I32 flags, SV *sv)
        padop->op_targ = pad_alloc(type, SVs_PADTMP);
     return CHECKOP(type, padop);
 }
-#endif
+
+#endif /* !USE_ITHREADS */
+
+/*
+=for apidoc Am|OP *|newGVOP|I32 type|I32 flags|GV *gv
+
+Constructs, checks, and returns an op of any type that involves an
+embedded reference to a GV.  I<type> is the opcode.  I<flags> gives the
+eight bits of C<op_flags>.  I<gv> identifies the GV that the op should
+reference; calling this function does not transfer ownership of any
+reference to it.
+
+=cut
+*/
 
 OP *
 Perl_newGVOP(pTHX_ I32 type, I32 flags, GV *gv)
@@ -3788,11 +4122,27 @@ Perl_newGVOP(pTHX_ I32 type, I32 flags, GV *gv)
 #endif
 }
 
+/*
+=for apidoc Am|OP *|newPVOP|I32 type|I32 flags|char *pv
+
+Constructs, checks, and returns an op of any type that involves an
+embedded C-level pointer (PV).  I<type> is the opcode.  I<flags> gives
+the eight bits of C<op_flags>.  I<pv> supplies the C-level pointer, which
+must have been allocated using L</PerlMemShared_malloc>; the memory will
+be freed when the op is destroyed.
+
+=cut
+*/
+
 OP *
 Perl_newPVOP(pTHX_ I32 type, I32 flags, char *pv)
 {
     dVAR;
     PVOP *pvop;
+
+    assert((PL_opargs[type] & OA_CLASS_MASK) == OA_PVOP_OR_SVOP
+       || (PL_opargs[type] & OA_CLASS_MASK) == OA_LOOPEXOP);
+
     NewOp(1101, pvop, 1, PVOP);
     pvop->op_type = (OPCODE)type;
     pvop->op_ppaddr = PL_ppaddr[type];
@@ -3904,8 +4254,8 @@ Perl_utilize(pTHX_ int aver, I32 floor, OP *version, OP *idop, OP *arg)
            /* Fake up a method call to VERSION */
            meth = newSVpvs_share("VERSION");
            veop = convert(OP_ENTERSUB, OPf_STACKED|OPf_SPECIAL,
-                           append_elem(OP_LIST,
-                                       prepend_elem(OP_LIST, pack, list(version)),
+                           op_append_elem(OP_LIST,
+                                       op_prepend_elem(OP_LIST, pack, list(version)),
                                        newSVOP(OP_METHOD_NAMED, 0, meth)));
        }
     }
@@ -3934,8 +4284,8 @@ Perl_utilize(pTHX_ int aver, I32 floor, OP *version, OP *idop, OP *arg)
        meth = aver
            ? newSVpvs_share("import") : newSVpvs_share("unimport");
        imop = convert(OP_ENTERSUB, OPf_STACKED|OPf_SPECIAL,
-                      append_elem(OP_LIST,
-                                  prepend_elem(OP_LIST, pack, list(arg)),
+                      op_append_elem(OP_LIST,
+                                  op_prepend_elem(OP_LIST, pack, list(arg)),
                                   newSVOP(OP_METHOD_NAMED, 0, meth)));
     }
 
@@ -3944,8 +4294,8 @@ Perl_utilize(pTHX_ int aver, I32 floor, OP *version, OP *idop, OP *arg)
        newSVOP(OP_CONST, 0, newSVpvs_share("BEGIN")),
        NULL,
        NULL,
-       append_elem(OP_LINESEQ,
-           append_elem(OP_LINESEQ,
+       op_append_elem(OP_LINESEQ,
+           op_append_elem(OP_LINESEQ,
                newSTATEOP(0, NULL, newUNOP(OP_REQUIRE, 0, idop)),
                newSTATEOP(0, NULL, veop)),
            newSTATEOP(0, NULL, imop) ));
@@ -4053,7 +4403,7 @@ Perl_vload_module(pTHX_ U32 flags, SV *name, SV *ver, va_list *args)
        imop = NULL;
        sv = va_arg(*args, SV*);
        while (sv) {
-           imop = append_elem(OP_LIST, imop, newSVOP(OP_CONST, 0, sv));
+           imop = op_append_elem(OP_LIST, imop, newSVOP(OP_CONST, 0, sv));
            sv = va_arg(*args, SV*);
        }
     }
@@ -4066,7 +4416,7 @@ Perl_vload_module(pTHX_ U32 flags, SV *name, SV *ver, va_list *args)
 
     ENTER;
     SAVEVPTR(PL_curcop);
-    lex_start(NULL, NULL, FALSE);
+    lex_start(NULL, NULL, 0);
     utilize(!(flags & PERL_LOADMOD_DENY), start_subparse(FALSE, 0),
            veop, modname, imop);
     LEAVE;
@@ -4091,7 +4441,7 @@ Perl_dofile(pTHX_ OP *term, I32 force_builtin)
 
     if (gv && GvCVu(gv) && GvIMPORTED_CV(gv)) {
        doop = ck_subr(newUNOP(OP_ENTERSUB, OPf_STACKED,
-                              append_elem(OP_LIST, term,
+                              op_append_elem(OP_LIST, term,
                                           scalar(newUNOP(OP_RV2CV, 0,
                                                          newGVOP(OP_GV, 0, gv))))));
     }
@@ -4101,6 +4451,22 @@ Perl_dofile(pTHX_ OP *term, I32 force_builtin)
     return doop;
 }
 
+/*
+=head1 Optree construction
+
+=for apidoc Am|OP *|newSLICEOP|I32 flags|OP *subscript|OP *listval
+
+Constructs, checks, and returns an C<lslice> (list slice) op.  I<flags>
+gives the eight bits of C<op_flags>, except that C<OPf_KIDS> will
+be set automatically, and, shifted up eight bits, the eight bits of
+C<op_private>, except that the bit with value 1 or 2 is automatically
+set as required.  I<listval> and I<subscript> supply the parameters of
+the slice; they are consumed by this function and become part of the
+constructed op tree.
+
+=cut
+*/
+
 OP *
 Perl_newSLICEOP(pTHX_ I32 flags, OP *subscript, OP *listval)
 {
@@ -4153,6 +4519,29 @@ S_is_list_assignment(pTHX_ register const OP *o)
     return FALSE;
 }
 
+/*
+=for apidoc Am|OP *|newASSIGNOP|I32 flags|OP *left|I32 optype|OP *right
+
+Constructs, checks, and returns an assignment op.  I<left> and I<right>
+supply the parameters of the assignment; they are consumed by this
+function and become part of the constructed op tree.
+
+If I<optype> is C<OP_ANDASSIGN>, C<OP_ORASSIGN>, or C<OP_DORASSIGN>, then
+a suitable conditional optree is constructed.  If I<optype> is the opcode
+of a binary operator, such as C<OP_BIT_OR>, then an op is constructed that
+performs the binary operation and assigns the result to the left argument.
+Either way, if I<optype> is non-zero then I<flags> has no effect.
+
+If I<optype> is zero, then a plain scalar or list assignment is
+constructed.  Which type of assignment it is is automatically determined.
+I<flags> gives the eight bits of C<op_flags>, except that C<OPf_KIDS>
+will be set automatically, and, shifted up eight bits, the eight bits
+of C<op_private>, except that the bit with value 1 or 2 is automatically
+set as required.
+
+=cut
+*/
+
 OP *
 Perl_newASSIGNOP(pTHX_ I32 flags, OP *left, I32 optype, OP *right)
 {
@@ -4162,12 +4551,12 @@ Perl_newASSIGNOP(pTHX_ I32 flags, OP *left, I32 optype, OP *right)
     if (optype) {
        if (optype == OP_ANDASSIGN || optype == OP_ORASSIGN || optype == OP_DORASSIGN) {
            return newLOGOP(optype, 0,
-               mod(scalar(left), optype),
+               op_lvalue(scalar(left), optype),
                newUNOP(OP_SASSIGN, 0, scalar(right)));
        }
        else {
            return newBINOP(optype, OPf_STACKED,
-               mod(scalar(left), optype), scalar(right));
+               op_lvalue(scalar(left), optype), scalar(right));
        }
     }
 
@@ -4181,10 +4570,11 @@ Perl_newASSIGNOP(pTHX_ I32 flags, OP *left, I32 optype, OP *right)
        /* Grandfathering $[ assignment here.  Bletch.*/
        /* Only simple assignments like C<< ($[) = 1 >> are allowed */
        PL_eval_start = (left->op_type == OP_CONST) ? right : NULL;
-       left = mod(left, OP_AASSIGN);
+       left = op_lvalue(left, OP_AASSIGN);
        if (PL_eval_start)
            PL_eval_start = 0;
        else if (left->op_type == OP_CONST) {
+           deprecate("assignment to $[");
            /* FIXME for MAD */
            /* Result of assignment is always 1 (or we'd be dead already) */
            return newSVOP(OP_CONST, 0, newSViv(1));
@@ -4236,7 +4626,7 @@ Perl_newASSIGNOP(pTHX_ I32 flags, OP *left, I32 optype, OP *right)
                    || left->op_type == OP_PADHV
                    || left->op_type == OP_PADANY))
        {
-           maybe_common_vars = FALSE;
+           if (left->op_type == OP_PADSV) maybe_common_vars = FALSE;
            if (left->op_private & OPpPAD_STATE) {
                /* All single variable list context state assignments, hence
                   state ($a) = ...
@@ -4380,12 +4770,13 @@ Perl_newASSIGNOP(pTHX_ I32 flags, OP *left, I32 optype, OP *right)
        right = newOP(OP_UNDEF, 0);
     if (right->op_type == OP_READLINE) {
        right->op_flags |= OPf_STACKED;
-       return newBINOP(OP_NULL, flags, mod(scalar(left), OP_SASSIGN), scalar(right));
+       return newBINOP(OP_NULL, flags, op_lvalue(scalar(left), OP_SASSIGN),
+               scalar(right));
     }
     else {
        PL_eval_start = right;  /* Grandfathering $[ assignment here.  Bletch.*/
        o = newBINOP(OP_SASSIGN, flags,
-           scalar(right), mod(scalar(left), OP_SASSIGN) );
+           scalar(right), op_lvalue(scalar(left), OP_SASSIGN) );
        if (PL_eval_start)
            PL_eval_start = 0;
        else {
@@ -4400,6 +4791,24 @@ Perl_newASSIGNOP(pTHX_ I32 flags, OP *left, I32 optype, OP *right)
     return o;
 }
 
+/*
+=for apidoc Am|OP *|newSTATEOP|I32 flags|char *label|OP *o
+
+Constructs a state op (COP).  The state op is normally a C<nextstate> op,
+but will be a C<dbstate> op if debugging is enabled for currently-compiled
+code.  The state op is populated from L</PL_curcop> (or L</PL_compiling>).
+If I<label> is non-null, it supplies the name of a label to attach to
+the state op; this function takes ownership of the memory pointed at by
+I<label>, and will free it.  I<flags> gives the eight bits of C<op_flags>
+for the state op.
+
+If I<o> is null, the state op is returned.  Otherwise the state op is
+combined with I<o> into a C<lineseq> list op, which is returned.  I<o>
+is consumed by this function and becomes part of the returned op tree.
+
+=cut
+*/
+
 OP *
 Perl_newSTATEOP(pTHX_ I32 flags, char *label, OP *o)
 {
@@ -4429,15 +4838,9 @@ Perl_newSTATEOP(pTHX_ I32 flags, char *label, OP *o)
        CopHINTS and a possible value in cop_hints_hash, so no need to copy it.
     */
     cop->cop_warnings = DUP_WARNINGS(PL_curcop->cop_warnings);
-    cop->cop_hints_hash = PL_curcop->cop_hints_hash;
-    if (cop->cop_hints_hash) {
-       HINTS_REFCNT_LOCK;
-       cop->cop_hints_hash->refcounted_he_refcnt++;
-       HINTS_REFCNT_UNLOCK;
-    }
+    CopHINTHASH_set(cop, cophh_copy(CopHINTHASH_get(PL_curcop)));
     if (label) {
-       cop->cop_hints_hash
-           = Perl_store_cop_label(aTHX_ cop->cop_hints_hash, label);
+       Perl_store_cop_label(aTHX_ cop, label, strlen(label), 0);
                                                     
        PL_hints |= HINT_BLOCK_SCOPE;
        /* It seems that we need to defer freeing this pointer, as other parts
@@ -4474,9 +4877,22 @@ Perl_newSTATEOP(pTHX_ I32 flags, char *label, OP *o)
 
     if (flags & OPf_SPECIAL)
        op_null((OP*)cop);
-    return prepend_elem(OP_LINESEQ, (OP*)cop, o);
+    return op_prepend_elem(OP_LINESEQ, (OP*)cop, o);
 }
 
+/*
+=for apidoc Am|OP *|newLOGOP|I32 type|I32 flags|OP *first|OP *other
+
+Constructs, checks, and returns a logical (flow control) op.  I<type>
+is the opcode.  I<flags> gives the eight bits of C<op_flags>, except
+that C<OPf_KIDS> will be set automatically, and, shifted up eight bits,
+the eight bits of C<op_private>, except that the bit with value 1 is
+automatically set.  I<first> supplies the expression controlling the
+flow, and I<other> supplies the side (alternate) chain of ops; they are
+consumed by this function and become part of the constructed op tree.
+
+=cut
+*/
 
 OP *
 Perl_newLOGOP(pTHX_ I32 type, I32 flags, OP *first, OP *other)
@@ -4550,6 +4966,8 @@ S_new_logop(pTHX_ I32 type, I32 flags, OP** firstp, OP** otherp)
     if (type == OP_XOR)                /* Not short circuit, but here by precedence. */
        return newBINOP(type, flags, scalar(first), scalar(other));
 
+    assert((PL_opargs[type] & OA_CLASS_MASK) == OA_LOGOP);
+
     scalarboolean(first);
     /* optimize AND and OR ops that have NOTs as children */
     if (first->op_type == OP_NOT
@@ -4590,6 +5008,12 @@ S_new_logop(pTHX_ I32 type, I32 flags, OP** firstp, OP** otherp)
            op_free(first);
            if (other->op_type == OP_LEAVE)
                other = newUNOP(OP_NULL, OPf_SPECIAL, other);
+           else if (other->op_type == OP_MATCH
+                 || other->op_type == OP_SUBST
+                 || other->op_type == OP_TRANSR
+                 || other->op_type == OP_TRANS)
+               /* Mark the op as being unbindable with =~ */
+               other->op_flags |= OPf_SPECIAL;
            return other;
        }
        else {
@@ -4691,6 +5115,20 @@ S_new_logop(pTHX_ I32 type, I32 flags, OP** firstp, OP** otherp)
     return o;
 }
 
+/*
+=for apidoc Am|OP *|newCONDOP|I32 flags|OP *first|OP *trueop|OP *falseop
+
+Constructs, checks, and returns a conditional-expression (C<cond_expr>)
+op.  I<flags> gives the eight bits of C<op_flags>, except that C<OPf_KIDS>
+will be set automatically, and, shifted up eight bits, the eight bits of
+C<op_private>, except that the bit with value 1 is automatically set.
+I<first> supplies the expression selecting between the two branches,
+and I<trueop> and I<falseop> supply the branches; they are consumed by
+this function and become part of the constructed op tree.
+
+=cut
+*/
+
 OP *
 Perl_newCONDOP(pTHX_ I32 flags, OP *first, OP *trueop, OP *falseop)
 {
@@ -4728,6 +5166,10 @@ Perl_newCONDOP(pTHX_ I32 flags, OP *first, OP *trueop, OP *falseop)
        }
        if (live->op_type == OP_LEAVE)
            live = newUNOP(OP_NULL, OPf_SPECIAL, live);
+       else if (live->op_type == OP_MATCH || live->op_type == OP_SUBST
+             || live->op_type == OP_TRANS || live->op_type == OP_TRANSR)
+           /* Mark the op as being unbindable with =~ */
+           live->op_flags |= OPf_SPECIAL;
        return live;
     }
     NewOp(1101, logop, 1, LOGOP);
@@ -4756,6 +5198,20 @@ Perl_newCONDOP(pTHX_ I32 flags, OP *first, OP *trueop, OP *falseop)
     return o;
 }
 
+/*
+=for apidoc Am|OP *|newRANGE|I32 flags|OP *left|OP *right
+
+Constructs and returns a C<range> op, with subordinate C<flip> and
+C<flop> ops.  I<flags> gives the eight bits of C<op_flags> for the
+C<flip> op and, shifted up eight bits, the eight bits of C<op_private>
+for both the C<flip> and C<range> ops, except that the bit with value
+1 is automatically set.  I<left> and I<right> supply the expressions
+controlling the endpoints of the range; they are consumed by this function
+and become part of the constructed op tree.
+
+=cut
+*/
+
 OP *
 Perl_newRANGE(pTHX_ I32 flags, OP *left, OP *right)
 {
@@ -4784,7 +5240,7 @@ Perl_newRANGE(pTHX_ I32 flags, OP *left, OP *right)
     flip = newUNOP(OP_FLIP, flags, (OP*)range);
     flop = newUNOP(OP_FLOP, 0, flip);
     o = newUNOP(OP_NULL, 0, flop);
-    linklist(flop);
+    LINKLIST(flop);
     range->op_next = leftstart;
 
     left->op_next = flip;
@@ -4800,12 +5256,28 @@ Perl_newRANGE(pTHX_ I32 flags, OP *left, OP *right)
 
     flip->op_next = o;
     if (!flip->op_private || !flop->op_private)
-       linklist(o);            /* blow off optimizer unless constant */
+       LINKLIST(o);            /* blow off optimizer unless constant */
 
     return o;
 }
 
-OP *
+/*
+=for apidoc Am|OP *|newLOOPOP|I32 flags|I32 debuggable|OP *expr|OP *block
+
+Constructs, checks, and returns an op tree expressing a loop.  This is
+only a loop in the control flow through the op tree; it does not have
+the heavyweight loop structure that allows exiting the loop by C<last>
+and suchlike.  I<flags> gives the eight bits of C<op_flags> for the
+top-level op, except that some bits will be set automatically as required.
+I<expr> supplies the expression controlling loop iteration, and I<block>
+supplies the body of the loop; they are consumed by this function and
+become part of the constructed op tree.  I<debuggable> is currently
+unused and should always be 1.
+
+=cut
+*/
+
+OP *
 Perl_newLOOPOP(pTHX_ I32 flags, I32 debuggable, OP *expr, OP *block)
 {
     dVAR;
@@ -4847,11 +5319,11 @@ Perl_newLOOPOP(pTHX_ I32 flags, I32 debuggable, OP *expr, OP *block)
        }
     }
 
-    /* if block is null, the next append_elem() would put UNSTACK, a scalar
+    /* if block is null, the next op_append_elem() would put UNSTACK, a scalar
      * op, in listop. This is wrong. [perl #27024] */
     if (!block)
        block = newOP(OP_NULL, 0);
-    listop = append_elem(OP_LINESEQ, block, newOP(OP_UNSTACK, 0));
+    listop = op_append_elem(OP_LINESEQ, block, newOP(OP_UNSTACK, 0));
     o = new_logop(OP_AND, 0, &expr, &listop);
 
     if (listop)
@@ -4864,14 +5336,38 @@ Perl_newLOOPOP(pTHX_ I32 flags, I32 debuggable, OP *expr, OP *block)
        o = newUNOP(OP_NULL, 0, o);     /* or do {} while 1 loses outer block */
 
     o->op_flags |= flags;
-    o = scope(o);
+    o = op_scope(o);
     o->op_flags |= OPf_SPECIAL;        /* suppress POPBLOCK curpm restoration*/
     return o;
 }
 
+/*
+=for apidoc Am|OP *|newWHILEOP|I32 flags|I32 debuggable|LOOP *loop|OP *expr|OP *block|OP *cont|I32 has_my
+
+Constructs, checks, and returns an op tree expressing a C<while> loop.
+This is a heavyweight loop, with structure that allows exiting the loop
+by C<last> and suchlike.
+
+I<loop> is an optional preconstructed C<enterloop> op to use in the
+loop; if it is null then a suitable op will be constructed automatically.
+I<expr> supplies the loop's controlling expression.  I<block> supplies the
+main body of the loop, and I<cont> optionally supplies a C<continue> block
+that operates as a second half of the body.  All of these optree inputs
+are consumed by this function and become part of the constructed op tree.
+
+I<flags> gives the eight bits of C<op_flags> for the C<leaveloop>
+op and, shifted up eight bits, the eight bits of C<op_private> for
+the C<leaveloop> op, except that (in both cases) some bits will be set
+automatically.  I<debuggable> is currently unused and should always be 1.
+I<has_my> can be supplied as true to force the
+loop body to be enclosed in its own scope.
+
+=cut
+*/
+
 OP *
-Perl_newWHILEOP(pTHX_ I32 flags, I32 debuggable, LOOP *loop, I32
-whileline, OP *expr, OP *block, OP *cont, I32 has_my)
+Perl_newWHILEOP(pTHX_ I32 flags, I32 debuggable, LOOP *loop,
+       OP *expr, OP *block, OP *cont, I32 has_my)
 {
     dVAR;
     OP *redo;
@@ -4914,7 +5410,7 @@ whileline, OP *expr, OP *block, OP *cont, I32 has_my)
     if (!block)
        block = newOP(OP_NULL, 0);
     else if (cont || has_my) {
-       block = scope(block);
+       block = op_scope(block);
     }
 
     if (cont) {
@@ -4924,16 +5420,15 @@ whileline, OP *expr, OP *block, OP *cont, I32 has_my)
        OP * const unstack = newOP(OP_UNSTACK, 0);
        if (!next)
            next = unstack;
-       cont = append_elem(OP_LINESEQ, cont, unstack);
+       cont = op_append_elem(OP_LINESEQ, cont, unstack);
     }
 
     assert(block);
-    listop = append_list(OP_LINESEQ, (LISTOP*)block, (LISTOP*)cont);
+    listop = op_append_list(OP_LINESEQ, block, cont);
     assert(listop);
     redo = LINKLIST(listop);
 
     if (expr) {
-       PL_parser->copline = (line_t)whileline;
        scalar(listop);
        o = new_logop(OP_AND, 0, &expr, &listop);
        if (o == expr && o->op_type == OP_CONST && !SvTRUE(cSVOPo->op_sv)) {
@@ -4972,8 +5467,31 @@ whileline, OP *expr, OP *block, OP *cont, I32 has_my)
     return o;
 }
 
+/*
+=for apidoc Am|OP *|newFOROP|I32 flags|OP *sv|OP *expr|OP *block|OP *cont
+
+Constructs, checks, and returns an op tree expressing a C<foreach>
+loop (iteration through a list of values).  This is a heavyweight loop,
+with structure that allows exiting the loop by C<last> and suchlike.
+
+I<sv> optionally supplies the variable that will be aliased to each
+item in turn; if null, it defaults to C<$_> (either lexical or global).
+I<expr> supplies the list of values to iterate over.  I<block> supplies
+the main body of the loop, and I<cont> optionally supplies a C<continue>
+block that operates as a second half of the body.  All of these optree
+inputs are consumed by this function and become part of the constructed
+op tree.
+
+I<flags> gives the eight bits of C<op_flags> for the C<leaveloop>
+op and, shifted up eight bits, the eight bits of C<op_private> for
+the C<leaveloop> op, except that (in both cases) some bits will be set
+automatically.
+
+=cut
+*/
+
 OP *
-Perl_newFOROP(pTHX_ I32 flags, char *label, line_t forline, OP *sv, OP *expr, OP *block, OP *cont)
+Perl_newFOROP(pTHX_ I32 flags, OP *sv, OP *expr, OP *block, OP *cont)
 {
     dVAR;
     LOOP *loop;
@@ -5034,7 +5552,7 @@ Perl_newFOROP(pTHX_ I32 flags, char *label, line_t forline, OP *sv, OP *expr, OP
        iterpflags |= OPpITER_DEF;
     }
     if (expr->op_type == OP_RV2AV || expr->op_type == OP_PADAV) {
-       expr = mod(force_list(scalar(ref(expr, OP_ITER))), OP_GREPSTART);
+       expr = op_lvalue(force_list(scalar(ref(expr, OP_ITER))), OP_GREPSTART);
        iterflags |= OPf_STACKED;
     }
     else if (expr->op_type == OP_NULL &&
@@ -5070,11 +5588,11 @@ Perl_newFOROP(pTHX_ I32 flags, char *label, line_t forline, OP *sv, OP *expr, OP
        iterflags |= OPf_STACKED;
     }
     else {
-        expr = mod(force_list(expr), OP_GREPSTART);
+        expr = op_lvalue(force_list(expr), OP_GREPSTART);
     }
 
     loop = (LOOP*)list(convert(OP_ENTERITER, iterflags,
-                              append_elem(OP_LIST, expr, scalar(sv))));
+                              op_append_elem(OP_LIST, expr, scalar(sv))));
     assert(!loop->op_next);
     /* for my  $x () sets OPpLVAL_INTRO;
      * for our $x () sets OPpOUR_INTRO */
@@ -5091,13 +5609,23 @@ Perl_newFOROP(pTHX_ I32 flags, char *label, line_t forline, OP *sv, OP *expr, OP
     loop = (LOOP*)PerlMemShared_realloc(loop, sizeof(LOOP));
 #endif
     loop->op_targ = padoff;
-    wop = newWHILEOP(flags, 1, loop, forline, newOP(OP_ITER, 0), block, cont, 0);
+    wop = newWHILEOP(flags, 1, loop, newOP(OP_ITER, 0), block, cont, 0);
     if (madsv)
        op_getmad(madsv, (OP*)loop, 'v');
-    PL_parser->copline = forline;
-    return newSTATEOP(0, label, wop);
+    return wop;
 }
 
+/*
+=for apidoc Am|OP *|newLOOPEX|I32 type|OP *label
+
+Constructs, checks, and returns a loop-exiting op (such as C<goto>
+or C<last>).  I<type> is the opcode.  I<label> supplies the parameter
+determining the target of the op; it is consumed by this function and
+become part of the constructed op tree.
+
+=cut
+*/
+
 OP*
 Perl_newLOOPEX(pTHX_ I32 type, OP *label)
 {
@@ -5106,6 +5634,8 @@ Perl_newLOOPEX(pTHX_ I32 type, OP *label)
 
     PERL_ARGS_ASSERT_NEWLOOPEX;
 
+    assert((PL_opargs[type] & OA_CLASS_MASK) == OA_LOOPEXOP);
+
     if (type != OP_GOTO || label->op_type == OP_CONST) {
        /* "last()" means "last" */
        if (label->op_type == OP_STUB && (label->op_flags & OPf_PARENS))
@@ -5125,7 +5655,7 @@ Perl_newLOOPEX(pTHX_ I32 type, OP *label)
        /* Check whether it's going to be a goto &function */
        if (label->op_type == OP_ENTERSUB
                && !(label->op_flags & OPf_STACKED))
-           label = newUNOP(OP_REFGEN, 0, mod(label, OP_REFGEN));
+           label = newUNOP(OP_REFGEN, 0, op_lvalue(label, OP_REFGEN));
        o = newUNOP(type, OPf_STACKED, label);
     }
     PL_hints |= HINT_BLOCK_SCOPE;
@@ -5144,8 +5674,19 @@ S_ref_array_or_hash(pTHX_ OP *cond)
     ||  cond->op_type == OP_RV2HV
     ||  cond->op_type == OP_PADHV))
 
-       return newUNOP(OP_REFGEN,
-           0, mod(cond, OP_REFGEN));
+       return newUNOP(OP_REFGEN, 0, op_lvalue(cond, OP_REFGEN));
+
+    else if(cond
+    && (cond->op_type == OP_ASLICE
+    ||  cond->op_type == OP_HSLICE)) {
+
+       /* anonlist now needs a list from this op, was previously used in
+        * scalar context */
+       cond->op_flags |= ~(OPf_WANT_SCALAR | OPf_REF);
+       cond->op_flags |= OPf_WANT_LIST;
+
+       return newANONLIST(op_lvalue(cond, OP_ANONLIST));
+    }
 
     else
        return cond;
@@ -5235,14 +5776,11 @@ S_looks_like_bool(pTHX_ const OP *o)
             && looks_like_bool(cLOGOPo->op_first->op_sibling));
 
        case OP_NULL:
+       case OP_SCALAR:
            return (
                o->op_flags & OPf_KIDS
            && looks_like_bool(cUNOPo->op_first));
 
-        case OP_SCALAR:
-            return looks_like_bool(cUNOPo->op_first);
-
-
        case OP_ENTERSUB:
 
        case OP_NOT:    case OP_XOR:
@@ -5289,6 +5827,19 @@ S_looks_like_bool(pTHX_ const OP *o)
     }
 }
 
+/*
+=for apidoc Am|OP *|newGIVENOP|OP *cond|OP *block|PADOFFSET defsv_off
+
+Constructs, checks, and returns an op tree expressing a C<given> block.
+I<cond> supplies the expression that will be locally assigned to a lexical
+variable, and I<block> supplies the body of the C<given> construct; they
+are consumed by this function and become part of the constructed op tree.
+I<defsv_off> is the pad offset of the scalar lexical variable that will
+be affected.
+
+=cut
+*/
+
 OP *
 Perl_newGIVENOP(pTHX_ OP *cond, OP *block, PADOFFSET defsv_off)
 {
@@ -5301,7 +5852,19 @@ Perl_newGIVENOP(pTHX_ OP *cond, OP *block, PADOFFSET defsv_off)
        defsv_off);
 }
 
-/* If cond is null, this is a default {} block */
+/*
+=for apidoc Am|OP *|newWHENOP|OP *cond|OP *block
+
+Constructs, checks, and returns an op tree expressing a C<when> block.
+I<cond> supplies the test expression, and I<block> supplies the block
+that will be executed if the test evaluates to true; they are consumed
+by this function and become part of the constructed op tree.  I<cond>
+will be interpreted DWIMically, often as a comparison against C<$_>,
+and may be null to generate a C<default> block.
+
+=cut
+*/
+
 OP *
 Perl_newWHENOP(pTHX_ OP *cond, OP *block)
 {
@@ -5320,75 +5883,10 @@ Perl_newWHENOP(pTHX_ OP *cond, OP *block)
     
     return newGIVWHENOP(
        cond_op,
-       append_elem(block->op_type, block, newOP(OP_BREAK, OPf_SPECIAL)),
+       op_append_elem(block->op_type, block, newOP(OP_BREAK, OPf_SPECIAL)),
        OP_ENTERWHEN, OP_LEAVEWHEN, 0);
 }
 
-/*
-=for apidoc cv_undef
-
-Clear out all the active components of a CV. This can happen either
-by an explicit C<undef &foo>, or by the reference count going to zero.
-In the former case, we keep the CvOUTSIDE pointer, so that any anonymous
-children can still follow the full lexical scope chain.
-
-=cut
-*/
-
-void
-Perl_cv_undef(pTHX_ CV *cv)
-{
-    dVAR;
-
-    PERL_ARGS_ASSERT_CV_UNDEF;
-
-    DEBUG_X(PerlIO_printf(Perl_debug_log,
-         "CV undef: cv=0x%"UVxf" comppad=0x%"UVxf"\n",
-           PTR2UV(cv), PTR2UV(PL_comppad))
-    );
-
-#ifdef USE_ITHREADS
-    if (CvFILE(cv) && !CvISXSUB(cv)) {
-       /* for XSUBs CvFILE point directly to static memory; __FILE__ */
-       Safefree(CvFILE(cv));
-    }
-    CvFILE(cv) = NULL;
-#endif
-
-    if (!CvISXSUB(cv) && CvROOT(cv)) {
-       if (SvTYPE(cv) == SVt_PVCV && CvDEPTH(cv))
-           Perl_croak(aTHX_ "Can't undef active subroutine");
-       ENTER;
-
-       PAD_SAVE_SETNULLPAD();
-
-       op_free(CvROOT(cv));
-       CvROOT(cv) = NULL;
-       CvSTART(cv) = NULL;
-       LEAVE;
-    }
-    SvPOK_off(MUTABLE_SV(cv));         /* forget prototype */
-    CvGV(cv) = NULL;
-
-    pad_undef(cv);
-
-    /* remove CvOUTSIDE unless this is an undef rather than a free */
-    if (!SvREFCNT(cv) && CvOUTSIDE(cv)) {
-       if (!CvWEAKOUTSIDE(cv))
-           SvREFCNT_dec(CvOUTSIDE(cv));
-       CvOUTSIDE(cv) = NULL;
-    }
-    if (CvCONST(cv)) {
-       SvREFCNT_dec(MUTABLE_SV(CvXSUBANY(cv).any_ptr));
-       CvCONST_off(cv);
-    }
-    if (CvISXSUB(cv) && CvXSUB(cv)) {
-       CvXSUB(cv) = NULL;
-    }
-    /* delete all flags except WEAKOUTSIDE */
-    CvFLAGS(cv) &= CVf_WEAKOUTSIDE;
-}
-
 void
 Perl_cv_ckproto_len(pTHX_ const CV *cv, const GV *gv, const char *p,
                    const STRLEN len)
@@ -5558,18 +6056,12 @@ Perl_newMYSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
 }
 
 CV *
-Perl_newSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *block)
-{
-    return Perl_newATTRSUB(aTHX_ floor, o, proto, NULL, block);
-}
-
-CV *
 Perl_newATTRSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
 {
     dVAR;
     GV *gv;
     const char *ps;
-    STRLEN ps_len;
+    STRLEN ps_len = 0; /* init it to avoid false uninit warning from icc */
     register CV *cv = NULL;
     SV *const_sv;
     /* If the subroutine has no body, no attributes, and no builtin attributes
@@ -5668,7 +6160,9 @@ Perl_newATTRSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
                 )&& !attrs) {
                if (CvFLAGS(PL_compcv)) {
                    /* might have had built-in attrs applied */
-                   CvFLAGS(cv) |= (CvFLAGS(PL_compcv) & CVf_BUILTIN_ATTRS);
+                   if (CvLVALUE(PL_compcv) && ! CvLVALUE(cv) && ckWARN(WARN_MISC))
+                       Perl_warner(aTHX_ packWARN(WARN_MISC), "lvalue attribute ignored after the subroutine has been defined");
+                   CvFLAGS(cv) |= (CvFLAGS(PL_compcv) & CVf_BUILTIN_ATTRS & ~CVf_LVALUE);
                }
                /* just a "sub foo;" when &foo is already defined */
                SAVEFREESV(PL_compcv);
@@ -5737,15 +6231,31 @@ Perl_newATTRSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
                   && block->op_type != OP_NULL
 #endif
        ) {
-           cv_undef(cv);
-           CvFLAGS(cv) = CvFLAGS(PL_compcv);
-           if (!CvWEAKOUTSIDE(cv))
-               SvREFCNT_dec(CvOUTSIDE(cv));
+           cv_flags_t existing_builtin_attrs = CvFLAGS(cv) & CVf_BUILTIN_ATTRS;
+           AV *const temp_av = CvPADLIST(cv);
+           CV *const temp_cv = CvOUTSIDE(cv);
+
+           assert(!CvWEAKOUTSIDE(cv));
+           assert(!CvCVGV_RC(cv));
+           assert(CvGV(cv) == gv);
+
+           SvPOK_off(cv);
+           CvFLAGS(cv) = CvFLAGS(PL_compcv) | existing_builtin_attrs;
            CvOUTSIDE(cv) = CvOUTSIDE(PL_compcv);
            CvOUTSIDE_SEQ(cv) = CvOUTSIDE_SEQ(PL_compcv);
-           CvOUTSIDE(PL_compcv) = 0;
            CvPADLIST(cv) = CvPADLIST(PL_compcv);
-           CvPADLIST(PL_compcv) = 0;
+           CvOUTSIDE(PL_compcv) = temp_cv;
+           CvPADLIST(PL_compcv) = temp_av;
+
+#ifdef USE_ITHREADS
+           if (CvFILE(cv) && !CvISXSUB(cv)) {
+               /* for XSUBs CvFILE point directly to static memory; __FILE__ */
+               Safefree(CvFILE(cv));
+    }
+#endif
+           CvFILE_set_from_cop(cv, PL_curcop);
+           CvSTASH_set(cv, PL_curstash);
+
            /* inner references to PL_compcv must be fixed up ... */
            pad_fixup_inner_anons(CvPADLIST(cv), PL_compcv, cv);
            if (PERLDB_INTER)/* Advice debugger on the new sub. */
@@ -5766,7 +6276,8 @@ Perl_newATTRSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
            if (PL_madskills) {
                if (strEQ(name, "import")) {
                    PL_formfeed = MUTABLE_SV(cv);
-                   Perl_warner(aTHX_ packWARN(WARN_VOID), "%lx\n", (long)cv);
+                   /* diag_listed_as: SKIPME */
+                   Perl_warner(aTHX_ packWARN(WARN_VOID), "0x%"UVxf"\n", PTR2UV(cv));
                }
            }
            GvCVGEN(gv) = 0;
@@ -5774,9 +6285,9 @@ Perl_newATTRSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
        }
     }
     if (!CvGV(cv)) {
-       CvGV(cv) = gv;
+       CvGV_set(cv, gv);
        CvFILE_set_from_cop(cv, PL_curcop);
-       CvSTASH(cv) = PL_curstash;
+       CvSTASH_set(cv, PL_curstash);
     }
     if (attrs) {
        /* Need to do a C<use attributes $stash_of_cv,\&cv,@attrs>. */
@@ -5818,7 +6329,7 @@ Perl_newATTRSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
     PL_breakable_sub_gen++;
     if (CvLVALUE(cv)) {
        CvROOT(cv) = newUNOP(OP_LEAVESUBLV, 0,
-                            mod(scalarseq(block), OP_LEAVESUBLV));
+                            op_lvalue(scalarseq(block), OP_LEAVESUBLV));
        block->op_attached = 1;
     }
     else {
@@ -5854,20 +6365,19 @@ Perl_newATTRSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block)
 
     if (has_name) {
        if (PERLDB_SUBLINE && PL_curstash != PL_debstash) {
-           SV * const sv = newSV(0);
            SV * const tmpstr = sv_newmortal();
            GV * const db_postponed = gv_fetchpvs("DB::postponed",
                                                  GV_ADDMULTI, SVt_PVHV);
            HV *hv;
-
-           Perl_sv_setpvf(aTHX_ sv, "%s:%ld-%ld",
-                          CopFILE(PL_curcop),
-                          (long)PL_subline, (long)CopLINE(PL_curcop));
+           SV * const sv = Perl_newSVpvf(aTHX_ "%s:%ld-%ld",
+                                         CopFILE(PL_curcop),
+                                         (long)PL_subline,
+                                         (long)CopLINE(PL_curcop));
            gv_efullname3(tmpstr, gv, NULL);
            (void)hv_store(GvHV(PL_DBsub), SvPVX_const(tmpstr),
                    SvCUR(tmpstr), sv, 0);
            hv = GvHVn(db_postponed);
-           if (HvFILL(hv) > 0 && hv_exists(hv, SvPVX_const(tmpstr), SvCUR(tmpstr))) {
+           if (HvTOTALKEYS(hv) > 0 && hv_exists(hv, SvPVX_const(tmpstr), SvCUR(tmpstr))) {
                CV * const pcv = GvCV(db_postponed);
                if (pcv) {
                    dSP;
@@ -6135,7 +6645,9 @@ Perl_newXS(pTHX_ const char *name, XSUBADDR_t subaddr, const char *filename)
             mro_method_changed_in(GvSTASH(gv)); /* newXS */
        }
     }
-    CvGV(cv) = gv;
+    if (!name)
+       CvANON_on(cv);
+    CvGV_set(cv, gv);
     (void)gv_fetchfile(filename);
     CvFILE(cv) = (char *)filename; /* NOTE: not copied, as it is expected to be
                                   an external constant string */
@@ -6144,8 +6656,6 @@ Perl_newXS(pTHX_ const char *name, XSUBADDR_t subaddr, const char *filename)
 
     if (name)
        process_special_blocks(name, gv, cv);
-    else
-       CvANON_on(cv);
 
     return cv;
 }
@@ -6186,7 +6696,7 @@ Perl_newFORM(pTHX_ I32 floor, OP *o, OP *block)
     }
     cv = PL_compcv;
     GvFORM(gv) = cv;
-    CvGV(cv) = gv;
+    CvGV_set(cv, gv);
     CvFILE_set_from_cop(cv, PL_curcop);
 
 
@@ -6554,9 +7064,7 @@ Perl_ck_eval(pTHX_ OP *o)
            /* establish postfix order */
            enter->op_next = (OP*)enter;
 
-           CHECKOP(OP_ENTERTRY, enter);
-
-           o = prepend_elem(OP_LINESEQ, (OP*)enter, (OP*)kid);
+           o = op_prepend_elem(OP_LINESEQ, (OP*)enter, (OP*)kid);
            o->op_type = OP_LEAVETRY;
            o->op_ppaddr = PL_ppaddr[OP_LEAVETRY];
            enter->op_other = o;
@@ -6581,7 +7089,7 @@ Perl_ck_eval(pTHX_ OP *o)
     if ((PL_hints & HINT_LOCALIZE_HH) != 0 && GvHV(PL_hintgv)) {
        /* Store a copy of %^H that pp_entereval can pick up. */
        OP *hhop = newSVOP(OP_HINTSEVAL, 0,
-                          MUTABLE_SV(Perl_hv_copy_hints_hv(aTHX_ GvHV(PL_hintgv))));
+                          MUTABLE_SV(hv_copy_hints_hv(GvHV(PL_hintgv))));
        cUNOPo->op_first->op_sibling = hhop;
        o->op_private |= OPpEVAL_HAS_HH;
     }
@@ -6695,17 +7203,6 @@ Perl_ck_rvconst(pTHX_ register OP *o)
                Perl_croak(aTHX_ "Constant is not %s reference", badtype);
            return o;
        }
-       else if ((o->op_type == OP_RV2HV || o->op_type == OP_RV2SV) &&
-               (PL_hints & HINT_STRICT_REFS) && SvPOK(kidsv)) {
-           /* If this is an access to a stash, disable "strict refs", because
-            * stashes aren't auto-vivified at compile-time (unless we store
-            * symbols in them), and we don't want to produce a run-time
-            * stricture error when auto-vivifying the stash. */
-           const char *s = SvPV_nolen(kidsv);
-           const STRLEN l = SvCUR(kidsv);
-           if (l > 1 && s[l-1] == ':' && s[l-2] == ':')
-               o->op_private &= ~HINT_STRICT_REFS;
-       }
        if ((o->op_private & HINT_STRICT_REFS) && (kid->op_private & OPpCONST_BARE)) {
            const char *badthing;
            switch (o->op_type) {
@@ -6763,6 +7260,8 @@ Perl_ck_rvconst(pTHX_ register OP *o)
 #endif
            kid->op_private = 0;
            kid->op_ppaddr = PL_ppaddr[OP_GV];
+           /* FAKE globs in the symbol table cause weird bugs (#77810) */
+           SvFAKE_off(gv);
        }
     }
     return o;
@@ -6795,7 +7294,7 @@ Perl_ck_ftst(pTHX_ OP *o)
        }
        if ((PL_hints & HINT_FILETEST_ACCESS) && OP_IS_FILETEST_ACCESS(o->op_type))
            o->op_private |= OPpFT_ACCESS;
-       if (PL_check[kidtype] == MEMBER_TO_FPTR(Perl_ck_ftst)
+       if (PL_check[kidtype] == Perl_ck_ftst
                && kidtype != OP_STAT && kidtype != OP_LSTAT)
            o->op_private |= OPpFT_STACKED;
     }
@@ -6898,7 +7397,7 @@ Perl_ck_fun(pTHX_ OP *o)
                }
                else if (kid->op_type != OP_RV2AV && kid->op_type != OP_PADAV)
                    bad_type(numargs, "array", PL_op_desc[type], kid);
-               mod(kid, type);
+               op_lvalue(kid, type);
                break;
            case OA_HVREF:
                if (kid->op_type == OP_CONST &&
@@ -6920,13 +7419,13 @@ Perl_ck_fun(pTHX_ OP *o)
                }
                else if (kid->op_type != OP_RV2HV && kid->op_type != OP_PADHV)
                    bad_type(numargs, "hash", PL_op_desc[type], kid);
-               mod(kid, type);
+               op_lvalue(kid, type);
                break;
            case OA_CVREF:
                {
                    OP * const newop = newUNOP(OP_NULL, 0, kid);
                    kid->op_sibling = 0;
-                   linklist(kid);
+                   LINKLIST(kid);
                    newop->op_next = newop;
                    kid = newop;
                    kid->op_sibling = sibl;
@@ -7027,7 +7526,7 @@ Perl_ck_fun(pTHX_ OP *o)
                                      name = "__ANONIO__";
                                      len = 10;
                                 }
-                                mod(kid, type);
+                                op_lvalue(kid, type);
                            }
                            if (name) {
                                SV *namesv;
@@ -7050,7 +7549,7 @@ Perl_ck_fun(pTHX_ OP *o)
                scalar(kid);
                break;
            case OA_SCALARREF:
-               mod(scalar(kid), type);
+               op_lvalue(scalar(kid), type);
                break;
            }
            oa >>= 4;
@@ -7100,7 +7599,7 @@ Perl_ck_glob(pTHX_ OP *o)
 
     o = ck_fun(o);
     if ((o->op_flags & OPf_KIDS) && !cLISTOPo->op_first->op_sibling)
-       append_elem(OP_GLOB, o, newDEFSVOP());
+       op_append_elem(OP_GLOB, o, newDEFSVOP());
 
     if (!((gv = gv_fetchpvs("glob", GV_NOTQUAL, SVt_PVCV))
          && GvCVu(gv) && GvIMPORTED_CV(gv)))
@@ -7115,17 +7614,18 @@ Perl_ck_glob(pTHX_ OP *o)
        ENTER;
        Perl_load_module(aTHX_ PERL_LOADMOD_NOIMPORT,
                newSVpvs("File::Glob"), NULL, NULL, NULL);
-       gv = gv_fetchpvs("CORE::GLOBAL::glob", 0, SVt_PVCV);
-       glob_gv = gv_fetchpvs("File::Glob::csh_glob", 0, SVt_PVCV);
-       GvCV(gv) = GvCV(glob_gv);
-       SvREFCNT_inc_void(MUTABLE_SV(GvCV(gv)));
-       GvIMPORTED_CV_on(gv);
+       if((glob_gv = gv_fetchpvs("File::Glob::csh_glob", 0, SVt_PVCV))) {
+           gv = gv_fetchpvs("CORE::GLOBAL::glob", 0, SVt_PVCV);
+           GvCV(gv) = GvCV(glob_gv);
+           SvREFCNT_inc_void(MUTABLE_SV(GvCV(gv)));
+           GvIMPORTED_CV_on(gv);
+       }
        LEAVE;
     }
 #endif /* PERL_EXTERNAL_GLOB */
 
     if (gv && GvCVu(gv) && GvIMPORTED_CV(gv)) {
-       append_elem(OP_GLOB, o,
+       op_append_elem(OP_GLOB, o,
                    newSVOP(OP_CONST, 0, newSViv(PL_glob_index++)));
        o->op_type = OP_LIST;
        o->op_ppaddr = PL_ppaddr[OP_LIST];
@@ -7133,7 +7633,7 @@ Perl_ck_glob(pTHX_ OP *o)
        cLISTOPo->op_first->op_ppaddr = PL_ppaddr[OP_PUSHMARK];
        cLISTOPo->op_first->op_targ = 0;
        o = newUNOP(OP_ENTERSUB, OPf_STACKED,
-                   append_elem(OP_LIST, o,
+                   op_append_elem(OP_LIST, o,
                                scalar(newUNOP(OP_RV2CV, 0,
                                               newGVOP(OP_GV, 0, gv)))));
        o = newUNOP(OP_NULL, 0, ck_subr(o));
@@ -7142,7 +7642,7 @@ Perl_ck_glob(pTHX_ OP *o)
     }
     gv = newGVgen("main");
     gv_IOadd(gv);
-    append_elem(OP_GLOB, o, newGVOP(OP_GV, 0, gv));
+    op_append_elem(OP_GLOB, o, newGVOP(OP_GV, 0, gv));
     scalarkids(o);
     return o;
 }
@@ -7164,10 +7664,10 @@ Perl_ck_grep(pTHX_ OP *o)
     if (o->op_flags & OPf_STACKED) {
        OP* k;
        o = ck_sort(o);
-        kid = cLISTOPo->op_first->op_sibling;
-       if (!cUNOPx(kid)->op_next)
-           Perl_croak(aTHX_ "panic: ck_grep");
-       for (k = cUNOPx(kid)->op_first; k; k = k->op_next) {
+        kid = cUNOPx(cLISTOPo->op_first->op_sibling)->op_first;
+       if (kid->op_type != OP_SCOPE && kid->op_type != OP_LEAVE)
+           return no_fh_allowed(o);
+       for (k = kid; k; k = k->op_next) {
            kid = k;
        }
        NewOp(1101, gwop, 1, LOGOP);
@@ -7209,7 +7709,7 @@ Perl_ck_grep(pTHX_ OP *o)
     if (!kid || !kid->op_sibling)
        return too_few_arguments(o,OP_DESC(o));
     for (kid = kid->op_sibling; kid; kid = kid->op_sibling)
-       mod(kid, OP_GREPSTART);
+       op_lvalue(kid, OP_GREPSTART);
 
     return (OP*)gwop;
 }
@@ -7329,7 +7829,7 @@ Perl_ck_listiob(pTHX_ OP *o)
     }
 
     if (!kid)
-       append_elem(o->op_type, o, newDEFSVOP());
+       op_append_elem(o->op_type, o, newDEFSVOP());
 
     return listkids(o);
 }
@@ -7338,6 +7838,7 @@ OP *
 Perl_ck_smartmatch(pTHX_ OP *o)
 {
     dVAR;
+    PERL_ARGS_ASSERT_CK_SMARTMATCH;
     if (0 == (o->op_flags & OPf_SPECIAL)) {
        OP *first  = cBINOPo->op_first;
        OP *second = first->op_sibling;
@@ -7626,7 +8127,7 @@ Perl_ck_require(pTHX_ OP *o)
        op_free(o);
 #endif
        newop = ck_subr(newUNOP(OP_ENTERSUB, OPf_STACKED,
-                               append_elem(OP_LIST, kid,
+                               op_append_elem(OP_LIST, kid,
                                            scalar(newUNOP(OP_RV2CV, 0,
                                                           newGVOP(OP_GV, 0,
                                                                   gv))))));
@@ -7634,7 +8135,7 @@ Perl_ck_require(pTHX_ OP *o)
        return newop;
     }
 
-    return ck_fun(o);
+    return scalar(ck_fun(o));
 }
 
 OP *
@@ -7648,7 +8149,7 @@ Perl_ck_return(pTHX_ OP *o)
     kid = cLISTOPo->op_first->op_sibling;
     if (CvLVALUE(PL_compcv)) {
        for (; kid; kid = kid->op_sibling)
-           mod(kid, OP_LEAVESUBLV);
+           op_lvalue(kid, OP_LEAVESUBLV);
     } else {
        for (; kid; kid = kid->op_sibling)
            if ((kid->op_type == OP_NULL)
@@ -7700,19 +8201,27 @@ Perl_ck_shift(pTHX_ OP *o)
     PERL_ARGS_ASSERT_CK_SHIFT;
 
     if (!(o->op_flags & OPf_KIDS)) {
-       OP *argop = newUNOP(OP_RV2AV, 0,
-           scalar(newGVOP(OP_GV, 0, CvUNIQUE(PL_compcv) ? PL_argvgv : PL_defgv)));
+       OP *argop;
+
+       if (!CvUNIQUE(PL_compcv)) {
+           o->op_flags |= OPf_SPECIAL;
+           return o;
+       }
+
+       argop = newUNOP(OP_RV2AV, 0, scalar(newGVOP(OP_GV, 0, PL_argvgv)));
 #ifdef PERL_MAD
-       OP * const oldo = o;
-       o = newUNOP(type, 0, scalar(argop));
-       op_getmad(oldo,o,'O');
-       return o;
+       {
+           OP * const oldo = o;
+           o = newUNOP(type, 0, scalar(argop));
+           op_getmad(oldo,o,'O');
+           return o;
+       }
 #else
        op_free(o);
        return newUNOP(type, 0, scalar(argop));
 #endif
     }
-    return scalar(modkids(ck_fun(o), type));
+    return scalar(modkids(ck_push(o), type));
 }
 
 OP *
@@ -7745,7 +8254,7 @@ Perl_ck_sort(pTHX_ OP *o)
        OP *kid = cUNOPx(firstkid)->op_first;           /* get past null */
 
        if (kid->op_type == OP_SCOPE || kid->op_type == OP_LEAVE) {
-           linklist(kid);
+           LINKLIST(kid);
            if (kid->op_type == OP_SCOPE) {
                k = kid->op_next;
                kid->op_next = 0;
@@ -7913,13 +8422,13 @@ Perl_ck_split(pTHX_ OP *o)
     }
 
     if (!kid->op_sibling)
-       append_elem(OP_SPLIT, o, newDEFSVOP());
+       op_append_elem(OP_SPLIT, o, newDEFSVOP());
 
     kid = kid->op_sibling;
     scalar(kid);
 
     if (!kid->op_sibling)
-       append_elem(OP_SPLIT, o, newSVOP(OP_CONST, 0, newSViv(0)));
+       op_append_elem(OP_SPLIT, o, newSVOP(OP_CONST, 0, newSViv(0)));
     assert(kid->op_sibling);
 
     kid = kid->op_sibling;
@@ -7951,82 +8460,187 @@ Perl_ck_join(pTHX_ OP *o)
     return ck_fun(o);
 }
 
-OP *
-Perl_ck_subr(pTHX_ OP *o)
-{
-    dVAR;
-    OP *prev = ((cUNOPo->op_first->op_sibling)
-            ? cUNOPo : ((UNOP*)cUNOPo->op_first))->op_first;
-    OP *o2 = prev->op_sibling;
-    OP *cvop;
-    const char *proto = NULL;
-    const char *proto_end = NULL;
-    CV *cv = NULL;
-    GV *namegv = NULL;
-    int optional = 0;
-    I32 arg = 0;
-    I32 contextclass = 0;
-    const char *e = NULL;
-    bool delete_op = 0;
+/*
+=for apidoc Am|CV *|rv2cv_op_cv|OP *cvop|U32 flags
+
+Examines an op, which is expected to identify a subroutine at runtime,
+and attempts to determine at compile time which subroutine it identifies.
+This is normally used during Perl compilation to determine whether
+a prototype can be applied to a function call.  I<cvop> is the op
+being considered, normally an C<rv2cv> op.  A pointer to the identified
+subroutine is returned, if it could be determined statically, and a null
+pointer is returned if it was not possible to determine statically.
+
+Currently, the subroutine can be identified statically if the RV that the
+C<rv2cv> is to operate on is provided by a suitable C<gv> or C<const> op.
+A C<gv> op is suitable if the GV's CV slot is populated.  A C<const> op is
+suitable if the constant value must be an RV pointing to a CV.  Details of
+this process may change in future versions of Perl.  If the C<rv2cv> op
+has the C<OPpENTERSUB_AMPER> flag set then no attempt is made to identify
+the subroutine statically: this flag is used to suppress compile-time
+magic on a subroutine call, forcing it to use default runtime behaviour.
+
+If I<flags> has the bit C<RV2CVOPCV_MARK_EARLY> set, then the handling
+of a GV reference is modified.  If a GV was examined and its CV slot was
+found to be empty, then the C<gv> op has the C<OPpEARLY_CV> flag set.
+If the op is not optimised away, and the CV slot is later populated with
+a subroutine having a prototype, that flag eventually triggers the warning
+"called too early to check prototype".
+
+If I<flags> has the bit C<RV2CVOPCV_RETURN_NAME_GV> set, then instead
+of returning a pointer to the subroutine it returns a pointer to the
+GV giving the most appropriate name for the subroutine in this context.
+Normally this is just the C<CvGV> of the subroutine, but for an anonymous
+(C<CvANON>) subroutine that is referenced through a GV it will be the
+referencing GV.  The resulting C<GV*> is cast to C<CV*> to be returned.
+A null pointer is returned as usual if there is no statically-determinable
+subroutine.
 
-    PERL_ARGS_ASSERT_CK_SUBR;
+=cut
+*/
 
-    o->op_private |= OPpENTERSUB_HASTARG;
-    for (cvop = o2; cvop->op_sibling; cvop = cvop->op_sibling) ;
-    if (cvop->op_type == OP_RV2CV) {
-       o->op_private |= (cvop->op_private & OPpENTERSUB_AMPER);
-       op_null(cvop);          /* disable rv2cv */
-       if (!(o->op_private & OPpENTERSUB_AMPER)) {
-           SVOP *tmpop = (SVOP*)((UNOP*)cvop)->op_first;
-           GV *gv = NULL;
-           switch (tmpop->op_type) {
-               case OP_GV: {
-                   gv = cGVOPx_gv(tmpop);
-                   cv = GvCVu(gv);
-                   if (!cv)
-                       tmpop->op_private |= OPpEARLY_CV;
-               } break;
-               case OP_CONST: {
-                   SV *sv = cSVOPx_sv(tmpop);
-                   if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV)
-                       cv = (CV*)SvRV(sv);
-               } break;
-           }
-           if (cv && SvPOK(cv)) {
-               STRLEN len;
-               namegv = gv && CvANON(cv) ? gv : CvGV(cv);
-               proto = SvPV(MUTABLE_SV(cv), len);
-               proto_end = proto + len;
+CV *
+Perl_rv2cv_op_cv(pTHX_ OP *cvop, U32 flags)
+{
+    OP *rvop;
+    CV *cv;
+    GV *gv;
+    PERL_ARGS_ASSERT_RV2CV_OP_CV;
+    if (flags & ~(RV2CVOPCV_MARK_EARLY|RV2CVOPCV_RETURN_NAME_GV))
+       Perl_croak(aTHX_ "panic: rv2cv_op_cv bad flags %x", (unsigned)flags);
+    if (cvop->op_type != OP_RV2CV)
+       return NULL;
+    if (cvop->op_private & OPpENTERSUB_AMPER)
+       return NULL;
+    if (!(cvop->op_flags & OPf_KIDS))
+       return NULL;
+    rvop = cUNOPx(cvop)->op_first;
+    switch (rvop->op_type) {
+       case OP_GV: {
+           gv = cGVOPx_gv(rvop);
+           cv = GvCVu(gv);
+           if (!cv) {
+               if (flags & RV2CVOPCV_MARK_EARLY)
+                   rvop->op_private |= OPpEARLY_CV;
+               return NULL;
            }
-       }
+       } break;
+       case OP_CONST: {
+           SV *rv = cSVOPx_sv(rvop);
+           if (!SvROK(rv))
+               return NULL;
+           cv = (CV*)SvRV(rv);
+           gv = NULL;
+       } break;
+       default: {
+           return NULL;
+       } break;
     }
-    else if (cvop->op_type == OP_METHOD || cvop->op_type == OP_METHOD_NAMED) {
-       if (o2->op_type == OP_CONST)
-           o2->op_private &= ~OPpCONST_STRICT;
-       else if (o2->op_type == OP_LIST) {
-           OP * const sib = ((UNOP*)o2)->op_first->op_sibling;
-           if (sib && sib->op_type == OP_CONST)
-               sib->op_private &= ~OPpCONST_STRICT;
+    if (SvTYPE((SV*)cv) != SVt_PVCV)
+       return NULL;
+    if (flags & RV2CVOPCV_RETURN_NAME_GV) {
+       if (!CvANON(cv) || !gv)
+           gv = CvGV(cv);
+       return (CV*)gv;
+    } else {
+       return cv;
+    }
+}
+
+/*
+=for apidoc Am|OP *|ck_entersub_args_list|OP *entersubop
+
+Performs the default fixup of the arguments part of an C<entersub>
+op tree.  This consists of applying list context to each of the
+argument ops.  This is the standard treatment used on a call marked
+with C<&>, or a method call, or a call through a subroutine reference,
+or any other call where the callee can't be identified at compile time,
+or a call where the callee has no prototype.
+
+=cut
+*/
+
+OP *
+Perl_ck_entersub_args_list(pTHX_ OP *entersubop)
+{
+    OP *aop;
+    PERL_ARGS_ASSERT_CK_ENTERSUB_ARGS_LIST;
+    aop = cUNOPx(entersubop)->op_first;
+    if (!aop->op_sibling)
+       aop = cUNOPx(aop)->op_first;
+    for (aop = aop->op_sibling; aop->op_sibling; aop = aop->op_sibling) {
+       if (!(PL_madskills && aop->op_type == OP_STUB)) {
+           list(aop);
+           op_lvalue(aop, OP_ENTERSUB);
        }
     }
-    o->op_private |= (PL_hints & HINT_STRICT_REFS);
-    if (PERLDB_SUB && PL_curstash != PL_debstash)
-       o->op_private |= OPpENTERSUB_DB;
-    while (o2 != cvop) {
+    return entersubop;
+}
+
+/*
+=for apidoc Am|OP *|ck_entersub_args_proto|OP *entersubop|GV *namegv|SV *protosv
+
+Performs the fixup of the arguments part of an C<entersub> op tree
+based on a subroutine prototype.  This makes various modifications to
+the argument ops, from applying context up to inserting C<refgen> ops,
+and checking the number and syntactic types of arguments, as directed by
+the prototype.  This is the standard treatment used on a subroutine call,
+not marked with C<&>, where the callee can be identified at compile time
+and has a prototype.
+
+I<protosv> supplies the subroutine prototype to be applied to the call.
+It may be a normal defined scalar, of which the string value will be used.
+Alternatively, for convenience, it may be a subroutine object (a C<CV*>
+that has been cast to C<SV*>) which has a prototype.  The prototype
+supplied, in whichever form, does not need to match the actual callee
+referenced by the op tree.
+
+If the argument ops disagree with the prototype, for example by having
+an unacceptable number of arguments, a valid op tree is returned anyway.
+The error is reflected in the parser state, normally resulting in a single
+exception at the top level of parsing which covers all the compilation
+errors that occurred.  In the error message, the callee is referred to
+by the name defined by the I<namegv> parameter.
+
+=cut
+*/
+
+OP *
+Perl_ck_entersub_args_proto(pTHX_ OP *entersubop, GV *namegv, SV *protosv)
+{
+    STRLEN proto_len;
+    const char *proto, *proto_end;
+    OP *aop, *prev, *cvop;
+    int optional = 0;
+    I32 arg = 0;
+    I32 contextclass = 0;
+    const char *e = NULL;
+    PERL_ARGS_ASSERT_CK_ENTERSUB_ARGS_PROTO;
+    if (SvTYPE(protosv) == SVt_PVCV ? !SvPOK(protosv) : !SvOK(protosv))
+       Perl_croak(aTHX_ "panic: ck_entersub_args_proto CV with no proto");
+    proto = SvPV(protosv, proto_len);
+    proto_end = proto + proto_len;
+    aop = cUNOPx(entersubop)->op_first;
+    if (!aop->op_sibling)
+       aop = cUNOPx(aop)->op_first;
+    prev = aop;
+    aop = aop->op_sibling;
+    for (cvop = aop; cvop->op_sibling; cvop = cvop->op_sibling) ;
+    while (aop != cvop) {
        OP* o3;
-       if (PL_madskills && o2->op_type == OP_STUB) {
-           o2 = o2->op_sibling;
+       if (PL_madskills && aop->op_type == OP_STUB) {
+           aop = aop->op_sibling;
            continue;
        }
-       if (PL_madskills && o2->op_type == OP_NULL)
-           o3 = ((UNOP*)o2)->op_first;
+       if (PL_madskills && aop->op_type == OP_NULL)
+           o3 = ((UNOP*)aop)->op_first;
        else
-           o3 = o2;
-       if (proto) {
-           if (proto >= proto_end)
-               return too_many_arguments(o, gv_ename(namegv));
+           o3 = aop;
 
-           switch (*proto) {
+       if (proto >= proto_end)
+           return too_many_arguments(entersubop, gv_ename(namegv));
+
+       switch (*proto) {
            case ';':
                optional = 1;
                proto++;
@@ -8038,11 +8652,11 @@ Perl_ck_subr(pTHX_ OP *o)
            case '$':
                proto++;
                arg++;
-               scalar(o2);
+               scalar(aop);
                break;
            case '%':
            case '@':
-               list(o2);
+               list(aop);
                arg++;
                break;
            case '&':
@@ -8050,8 +8664,8 @@ Perl_ck_subr(pTHX_ OP *o)
                arg++;
                if (o3->op_type != OP_REFGEN && o3->op_type != OP_UNDEF)
                    bad_type(arg,
-                       arg == 1 ? "block or sub {}" : "sub {}",
-                       gv_ename(namegv), o3);
+                           arg == 1 ? "block or sub {}" : "sub {}",
+                           gv_ename(namegv), o3);
                break;
            case '*':
                /* '*' allows any scalar type, including bareword */
@@ -8070,148 +8684,327 @@ Perl_ck_subr(pTHX_ OP *o)
                            for (; gvop->op_sibling; gvop = gvop->op_sibling)
                                ;
                            if (gvop &&
-                               (gvop->op_private & OPpENTERSUB_NOPAREN) &&
-                               (gvop = ((UNOP*)gvop)->op_first) &&
-                               gvop->op_type == OP_GV)
+                                   (gvop->op_private & OPpENTERSUB_NOPAREN) &&
+                                   (gvop = ((UNOP*)gvop)->op_first) &&
+                                   gvop->op_type == OP_GV)
                            {
                                GV * const gv = cGVOPx_gv(gvop);
-                               OP * const sibling = o2->op_sibling;
+                               OP * const sibling = aop->op_sibling;
                                SV * const n = newSVpvs("");
 #ifdef PERL_MAD
-                               OP * const oldo2 = o2;
+                               OP * const oldaop = aop;
 #else
-                               op_free(o2);
+                               op_free(aop);
 #endif
                                gv_fullname4(n, gv, "", FALSE);
-                               o2 = newSVOP(OP_CONST, 0, n);
-                               op_getmad(oldo2,o2,'O');
-                               prev->op_sibling = o2;
-                               o2->op_sibling = sibling;
+                               aop = newSVOP(OP_CONST, 0, n);
+                               op_getmad(oldaop,aop,'O');
+                               prev->op_sibling = aop;
+                               aop->op_sibling = sibling;
                            }
                        }
                    }
                }
-               scalar(o2);
+               scalar(aop);
+               break;
+           case '+':
+               proto++;
+               arg++;
+               if (o3->op_type == OP_RV2AV ||
+                   o3->op_type == OP_PADAV ||
+                   o3->op_type == OP_RV2HV ||
+                   o3->op_type == OP_PADHV
+               ) {
+                   goto wrapref;
+               }
+               scalar(aop);
                break;
            case '[': case ']':
-                goto oops;
-                break;
+               goto oops;
+               break;
            case '\\':
                proto++;
                arg++;
            again:
                switch (*proto++) {
-               case '[':
-                    if (contextclass++ == 0) {
-                         e = strchr(proto, ']');
-                         if (!e || e == proto)
-                              goto oops;
-                    }
-                    else
-                         goto oops;
-                    goto again;
-                    break;
-               case ']':
-                    if (contextclass) {
-                        const char *p = proto;
-                        const char *const end = proto;
-                        contextclass = 0;
-                        while (*--p != '[') {}
-                        bad_type(arg, Perl_form(aTHX_ "one of %.*s",
-                                                (int)(end - p), p),
-                                 gv_ename(namegv), o3);
-                    } else
-                         goto oops;
-                    break;
-               case '*':
-                    if (o3->op_type == OP_RV2GV)
-                         goto wrapref;
-                    if (!contextclass)
-                         bad_type(arg, "symbol", gv_ename(namegv), o3);
-                    break;
-               case '&':
-                    if (o3->op_type == OP_ENTERSUB)
-                         goto wrapref;
-                    if (!contextclass)
-                         bad_type(arg, "subroutine entry", gv_ename(namegv),
-                                  o3);
-                    break;
-               case '$':
-                   if (o3->op_type == OP_RV2SV ||
-                       o3->op_type == OP_PADSV ||
-                       o3->op_type == OP_HELEM ||
-                       o3->op_type == OP_AELEM)
-                        goto wrapref;
-                   if (!contextclass)
-                       bad_type(arg, "scalar", gv_ename(namegv), o3);
-                    break;
-               case '@':
-                   if (o3->op_type == OP_RV2AV ||
-                       o3->op_type == OP_PADAV)
-                        goto wrapref;
-                   if (!contextclass)
-                       bad_type(arg, "array", gv_ename(namegv), o3);
-                   break;
-               case '%':
-                   if (o3->op_type == OP_RV2HV ||
-                       o3->op_type == OP_PADHV)
-                        goto wrapref;
-                   if (!contextclass)
-                        bad_type(arg, "hash", gv_ename(namegv), o3);
-                   break;
-               wrapref:
-                   {
-                       OP* const kid = o2;
-                       OP* const sib = kid->op_sibling;
-                       kid->op_sibling = 0;
-                       o2 = newUNOP(OP_REFGEN, 0, kid);
-                       o2->op_sibling = sib;
-                       prev->op_sibling = o2;
-                   }
-                   if (contextclass && e) {
-                        proto = e + 1;
-                        contextclass = 0;
-                   }
-                   break;
-               default: goto oops;
+                   case '[':
+                       if (contextclass++ == 0) {
+                           e = strchr(proto, ']');
+                           if (!e || e == proto)
+                               goto oops;
+                       }
+                       else
+                           goto oops;
+                       goto again;
+                       break;
+                   case ']':
+                       if (contextclass) {
+                           const char *p = proto;
+                           const char *const end = proto;
+                           contextclass = 0;
+                           while (*--p != '[') {}
+                           bad_type(arg, Perl_form(aTHX_ "one of %.*s",
+                                       (int)(end - p), p),
+                                   gv_ename(namegv), o3);
+                       } else
+                           goto oops;
+                       break;
+                   case '*':
+                       if (o3->op_type == OP_RV2GV)
+                           goto wrapref;
+                       if (!contextclass)
+                           bad_type(arg, "symbol", gv_ename(namegv), o3);
+                       break;
+                   case '&':
+                       if (o3->op_type == OP_ENTERSUB)
+                           goto wrapref;
+                       if (!contextclass)
+                           bad_type(arg, "subroutine entry", gv_ename(namegv),
+                                   o3);
+                       break;
+                   case '$':
+                       if (o3->op_type == OP_RV2SV ||
+                               o3->op_type == OP_PADSV ||
+                               o3->op_type == OP_HELEM ||
+                               o3->op_type == OP_AELEM)
+                           goto wrapref;
+                       if (!contextclass)
+                           bad_type(arg, "scalar", gv_ename(namegv), o3);
+                       break;
+                   case '@':
+                       if (o3->op_type == OP_RV2AV ||
+                               o3->op_type == OP_PADAV)
+                           goto wrapref;
+                       if (!contextclass)
+                           bad_type(arg, "array", gv_ename(namegv), o3);
+                       break;
+                   case '%':
+                       if (o3->op_type == OP_RV2HV ||
+                               o3->op_type == OP_PADHV)
+                           goto wrapref;
+                       if (!contextclass)
+                           bad_type(arg, "hash", gv_ename(namegv), o3);
+                       break;
+                   wrapref:
+                       {
+                           OP* const kid = aop;
+                           OP* const sib = kid->op_sibling;
+                           kid->op_sibling = 0;
+                           aop = newUNOP(OP_REFGEN, 0, kid);
+                           aop->op_sibling = sib;
+                           prev->op_sibling = aop;
+                       }
+                       if (contextclass && e) {
+                           proto = e + 1;
+                           contextclass = 0;
+                       }
+                       break;
+                   default: goto oops;
                }
                if (contextclass)
-                    goto again;
+                   goto again;
                break;
            case ' ':
                proto++;
                continue;
            default:
-             oops:
+           oops:
                Perl_croak(aTHX_ "Malformed prototype for %s: %"SVf,
-                          gv_ename(namegv), SVfARG(cv));
-           }
+                       gv_ename(namegv), SVfARG(protosv));
        }
-       else
-           list(o2);
-       mod(o2, OP_ENTERSUB);
-       prev = o2;
-       o2 = o2->op_sibling;
-    } /* while */
-    if (o2 == cvop && proto && *proto == '_') {
+
+       op_lvalue(aop, OP_ENTERSUB);
+       prev = aop;
+       aop = aop->op_sibling;
+    }
+    if (aop == cvop && *proto == '_') {
        /* generate an access to $_ */
-       o2 = newDEFSVOP();
-       o2->op_sibling = prev->op_sibling;
-       prev->op_sibling = o2; /* instead of cvop */
+       aop = newDEFSVOP();
+       aop->op_sibling = prev->op_sibling;
+       prev->op_sibling = aop; /* instead of cvop */
     }
-    if (proto && !optional && proto_end > proto &&
+    if (!optional && proto_end > proto &&
        (*proto != '@' && *proto != '%' && *proto != ';' && *proto != '_'))
-       return too_few_arguments(o, gv_ename(namegv));
-    if(delete_op) {
-#ifdef PERL_MAD
-       OP * const oldo = o;
-#else
-       op_free(o);
-#endif
-       o=newSVOP(OP_CONST, 0, newSViv(0));
-       op_getmad(oldo,o,'O');
+       return too_few_arguments(entersubop, gv_ename(namegv));
+    return entersubop;
+}
+
+/*
+=for apidoc Am|OP *|ck_entersub_args_proto_or_list|OP *entersubop|GV *namegv|SV *protosv
+
+Performs the fixup of the arguments part of an C<entersub> op tree either
+based on a subroutine prototype or using default list-context processing.
+This is the standard treatment used on a subroutine call, not marked
+with C<&>, where the callee can be identified at compile time.
+
+I<protosv> supplies the subroutine prototype to be applied to the call,
+or indicates that there is no prototype.  It may be a normal scalar,
+in which case if it is defined then the string value will be used
+as a prototype, and if it is undefined then there is no prototype.
+Alternatively, for convenience, it may be a subroutine object (a C<CV*>
+that has been cast to C<SV*>), of which the prototype will be used if it
+has one.  The prototype (or lack thereof) supplied, in whichever form,
+does not need to match the actual callee referenced by the op tree.
+
+If the argument ops disagree with the prototype, for example by having
+an unacceptable number of arguments, a valid op tree is returned anyway.
+The error is reflected in the parser state, normally resulting in a single
+exception at the top level of parsing which covers all the compilation
+errors that occurred.  In the error message, the callee is referred to
+by the name defined by the I<namegv> parameter.
+
+=cut
+*/
+
+OP *
+Perl_ck_entersub_args_proto_or_list(pTHX_ OP *entersubop,
+       GV *namegv, SV *protosv)
+{
+    PERL_ARGS_ASSERT_CK_ENTERSUB_ARGS_PROTO_OR_LIST;
+    if (SvTYPE(protosv) == SVt_PVCV ? SvPOK(protosv) : SvOK(protosv))
+       return ck_entersub_args_proto(entersubop, namegv, protosv);
+    else
+       return ck_entersub_args_list(entersubop);
+}
+
+/*
+=for apidoc Am|void|cv_get_call_checker|CV *cv|Perl_call_checker *ckfun_p|SV **ckobj_p
+
+Retrieves the function that will be used to fix up a call to I<cv>.
+Specifically, the function is applied to an C<entersub> op tree for a
+subroutine call, not marked with C<&>, where the callee can be identified
+at compile time as I<cv>.
+
+The C-level function pointer is returned in I<*ckfun_p>, and an SV
+argument for it is returned in I<*ckobj_p>.  The function is intended
+to be called in this manner:
+
+    entersubop = (*ckfun_p)(aTHX_ entersubop, namegv, (*ckobj_p));
+
+In this call, I<entersubop> is a pointer to the C<entersub> op,
+which may be replaced by the check function, and I<namegv> is a GV
+supplying the name that should be used by the check function to refer
+to the callee of the C<entersub> op if it needs to emit any diagnostics.
+It is permitted to apply the check function in non-standard situations,
+such as to a call to a different subroutine or to a method call.
+
+By default, the function is
+L<Perl_ck_entersub_args_proto_or_list|/ck_entersub_args_proto_or_list>,
+and the SV parameter is I<cv> itself.  This implements standard
+prototype processing.  It can be changed, for a particular subroutine,
+by L</cv_set_call_checker>.
+
+=cut
+*/
+
+void
+Perl_cv_get_call_checker(pTHX_ CV *cv, Perl_call_checker *ckfun_p, SV **ckobj_p)
+{
+    MAGIC *callmg;
+    PERL_ARGS_ASSERT_CV_GET_CALL_CHECKER;
+    callmg = SvMAGICAL((SV*)cv) ? mg_find((SV*)cv, PERL_MAGIC_checkcall) : NULL;
+    if (callmg) {
+       *ckfun_p = DPTR2FPTR(Perl_call_checker, callmg->mg_ptr);
+       *ckobj_p = callmg->mg_obj;
+    } else {
+       *ckfun_p = Perl_ck_entersub_args_proto_or_list;
+       *ckobj_p = (SV*)cv;
+    }
+}
+
+/*
+=for apidoc Am|void|cv_set_call_checker|CV *cv|Perl_call_checker ckfun|SV *ckobj
+
+Sets the function that will be used to fix up a call to I<cv>.
+Specifically, the function is applied to an C<entersub> op tree for a
+subroutine call, not marked with C<&>, where the callee can be identified
+at compile time as I<cv>.
+
+The C-level function pointer is supplied in I<ckfun>, and an SV argument
+for it is supplied in I<ckobj>.  The function is intended to be called
+in this manner:
+
+    entersubop = ckfun(aTHX_ entersubop, namegv, ckobj);
+
+In this call, I<entersubop> is a pointer to the C<entersub> op,
+which may be replaced by the check function, and I<namegv> is a GV
+supplying the name that should be used by the check function to refer
+to the callee of the C<entersub> op if it needs to emit any diagnostics.
+It is permitted to apply the check function in non-standard situations,
+such as to a call to a different subroutine or to a method call.
+
+The current setting for a particular CV can be retrieved by
+L</cv_get_call_checker>.
+
+=cut
+*/
+
+void
+Perl_cv_set_call_checker(pTHX_ CV *cv, Perl_call_checker ckfun, SV *ckobj)
+{
+    PERL_ARGS_ASSERT_CV_SET_CALL_CHECKER;
+    if (ckfun == Perl_ck_entersub_args_proto_or_list && ckobj == (SV*)cv) {
+       if (SvMAGICAL((SV*)cv))
+           mg_free_type((SV*)cv, PERL_MAGIC_checkcall);
+    } else {
+       MAGIC *callmg;
+       sv_magic((SV*)cv, &PL_sv_undef, PERL_MAGIC_checkcall, NULL, 0);
+       callmg = mg_find((SV*)cv, PERL_MAGIC_checkcall);
+       if (callmg->mg_flags & MGf_REFCOUNTED) {
+           SvREFCNT_dec(callmg->mg_obj);
+           callmg->mg_flags &= ~MGf_REFCOUNTED;
+       }
+       callmg->mg_ptr = FPTR2DPTR(char *, ckfun);
+       callmg->mg_obj = ckobj;
+       if (ckobj != (SV*)cv) {
+           SvREFCNT_inc_simple_void_NN(ckobj);
+           callmg->mg_flags |= MGf_REFCOUNTED;
+       }
+    }
+}
+
+OP *
+Perl_ck_subr(pTHX_ OP *o)
+{
+    OP *aop, *cvop;
+    CV *cv;
+    GV *namegv;
+
+    PERL_ARGS_ASSERT_CK_SUBR;
+
+    aop = cUNOPx(o)->op_first;
+    if (!aop->op_sibling)
+       aop = cUNOPx(aop)->op_first;
+    aop = aop->op_sibling;
+    for (cvop = aop; cvop->op_sibling; cvop = cvop->op_sibling) ;
+    cv = rv2cv_op_cv(cvop, RV2CVOPCV_MARK_EARLY);
+    namegv = cv ? (GV*)rv2cv_op_cv(cvop, RV2CVOPCV_RETURN_NAME_GV) : NULL;
+
+    o->op_private |= OPpENTERSUB_HASTARG;
+    o->op_private |= (PL_hints & HINT_STRICT_REFS);
+    if (PERLDB_SUB && PL_curstash != PL_debstash)
+       o->op_private |= OPpENTERSUB_DB;
+    if (cvop->op_type == OP_RV2CV) {
+       o->op_private |= (cvop->op_private & OPpENTERSUB_AMPER);
+       op_null(cvop);
+    } else if (cvop->op_type == OP_METHOD || cvop->op_type == OP_METHOD_NAMED) {
+       if (aop->op_type == OP_CONST)
+           aop->op_private &= ~OPpCONST_STRICT;
+       else if (aop->op_type == OP_LIST) {
+           OP * const sib = ((UNOP*)aop)->op_first->op_sibling;
+           if (sib && sib->op_type == OP_CONST)
+               sib->op_private &= ~OPpCONST_STRICT;
+       }
+    }
+
+    if (!cv) {
+       return ck_entersub_args_list(o);
+    } else {
+       Perl_call_checker ckfun;
+       SV *ckobj;
+       cv_get_call_checker(cv, &ckfun, &ckobj);
+       return ckfun(aTHX_ o, namegv, ckobj);
     }
-    return o;
 }
 
 OP *
@@ -8226,6 +9019,7 @@ Perl_ck_svconst(pTHX_ OP *o)
 OP *
 Perl_ck_chdir(pTHX_ OP *o)
 {
+    PERL_ARGS_ASSERT_CK_CHDIR;
     if (o->op_flags & OPf_KIDS) {
        SVOP * const kid = (SVOP*)cUNOPo->op_first;
 
@@ -8293,34 +9087,86 @@ Perl_ck_substr(pTHX_ OP *o)
 }
 
 OP *
-Perl_ck_each(pTHX_ OP *o)
+Perl_ck_push(pTHX_ OP *o)
 {
     dVAR;
     OP *kid = o->op_flags & OPf_KIDS ? cLISTOPo->op_first : NULL;
+    OP *cursor = NULL;
+    OP *proxy = NULL;
 
-    PERL_ARGS_ASSERT_CK_EACH;
+    PERL_ARGS_ASSERT_CK_PUSH;
 
+    /* If 1st kid is pushmark (e.g. push, unshift, splice), we need 2nd kid */
     if (kid) {
-       if (kid->op_type == OP_PADAV || kid->op_type == OP_RV2AV) {
-           const unsigned new_type = o->op_type == OP_EACH ? OP_AEACH
-               : o->op_type == OP_KEYS ? OP_AKEYS : OP_AVALUES;
-           o->op_type = new_type;
-           o->op_ppaddr = PL_ppaddr[new_type];
-       }
-       else if (!(kid->op_type == OP_PADHV || kid->op_type == OP_RV2HV
-                   || (kid->op_type == OP_CONST && kid->op_private & OPpCONST_BARE)
-                 )) {
-           bad_type(1, "hash or array", PL_op_desc[o->op_type], kid);
-           return o;
+       cursor = kid->op_type == OP_PUSHMARK ? kid->op_sibling : kid;
+    }
+
+    /* If not array or array deref, wrap it with an array deref.
+     * For OP_CONST, we only wrap arrayrefs */
+    if (cursor) {
+       if ( (    cursor->op_type != OP_PADAV
+              && cursor->op_type != OP_RV2AV
+              && cursor->op_type != OP_CONST
+            )
+            ||
+            (    cursor->op_type == OP_CONST
+              && SvROK(cSVOPx_sv(cursor))
+              && SvTYPE(SvRV(cSVOPx_sv(cursor))) == SVt_PVAV
+            )
+       ) {
+           proxy = newAVREF(cursor);
+           if ( cursor == kid ) {
+               cLISTOPx(o)->op_first = proxy;
+           }
+           else {
+               cLISTOPx(kid)->op_sibling = proxy;
+           }
+           cLISTOPx(proxy)->op_sibling = cLISTOPx(cursor)->op_sibling;
+           cLISTOPx(cursor)->op_sibling = NULL;
        }
     }
     return ck_fun(o);
 }
 
+OP *
+Perl_ck_each(pTHX_ OP *o)
+{
+    dVAR;
+    OP *kid = o->op_flags & OPf_KIDS ? cUNOPo->op_first : NULL;
+    const unsigned orig_type  = o->op_type;
+    const unsigned array_type = orig_type == OP_EACH ? OP_AEACH
+                             : orig_type == OP_KEYS ? OP_AKEYS : OP_AVALUES;
+    const unsigned ref_type   = orig_type == OP_EACH ? OP_REACH
+                             : orig_type == OP_KEYS ? OP_RKEYS : OP_RVALUES;
+
+    PERL_ARGS_ASSERT_CK_EACH;
+
+    if (kid) {
+       switch (kid->op_type) {
+           case OP_PADHV:
+           case OP_RV2HV:
+               break;
+           case OP_PADAV:
+           case OP_RV2AV:
+               CHANGE_TYPE(o, array_type);
+               break;
+           case OP_CONST:
+               if (kid->op_private == OPpCONST_BARE)
+                   /* we let ck_fun treat as hash */
+                   break;
+           default:
+               CHANGE_TYPE(o, ref_type);
+       }
+    }
+    /* if treating as a reference, defer additional checks to runtime */
+    return o->op_type == ref_type ? o : ck_fun(o);
+}
+
 /* caller is supposed to assign the return to the 
    container of the rep_op var */
-OP *
+STATIC OP *
 S_opt_scalarhv(pTHX_ OP *rep_op) {
+    dVAR;
     UNOP *unop;
 
     PERL_ARGS_ASSERT_OPT_SCALARHV;
@@ -8344,12 +9190,84 @@ S_opt_scalarhv(pTHX_ OP *rep_op) {
     return (OP*)unop;
 }                        
 
+/* Checks if o acts as an in-place operator on an array. oright points to the
+ * beginning of the right-hand side. Returns the left-hand side of the
+ * assignment if o acts in-place, or NULL otherwise. */
+
+STATIC OP *
+S_is_inplace_av(pTHX_ OP *o, OP *oright) {
+    OP *o2;
+    OP *oleft = NULL;
+
+    PERL_ARGS_ASSERT_IS_INPLACE_AV;
+
+    if (!oright ||
+       (oright->op_type != OP_RV2AV && oright->op_type != OP_PADAV)
+       || oright->op_next != o
+       || (oright->op_private & OPpLVAL_INTRO)
+    )
+       return NULL;
+
+    /* o2 follows the chain of op_nexts through the LHS of the
+     * assign (if any) to the aassign op itself */
+    o2 = o->op_next;
+    if (!o2 || o2->op_type != OP_NULL)
+       return NULL;
+    o2 = o2->op_next;
+    if (!o2 || o2->op_type != OP_PUSHMARK)
+       return NULL;
+    o2 = o2->op_next;
+    if (o2 && o2->op_type == OP_GV)
+       o2 = o2->op_next;
+    if (!o2
+       || (o2->op_type != OP_PADAV && o2->op_type != OP_RV2AV)
+       || (o2->op_private & OPpLVAL_INTRO)
+    )
+       return NULL;
+    oleft = o2;
+    o2 = o2->op_next;
+    if (!o2 || o2->op_type != OP_NULL)
+       return NULL;
+    o2 = o2->op_next;
+    if (!o2 || o2->op_type != OP_AASSIGN
+           || (o2->op_flags & OPf_WANT) != OPf_WANT_VOID)
+       return NULL;
+
+    /* check that the sort is the first arg on RHS of assign */
+
+    o2 = cUNOPx(o2)->op_first;
+    if (!o2 || o2->op_type != OP_NULL)
+       return NULL;
+    o2 = cUNOPx(o2)->op_first;
+    if (!o2 || o2->op_type != OP_PUSHMARK)
+       return NULL;
+    if (o2->op_sibling != o)
+       return NULL;
+
+    /* check the array is the same on both sides */
+    if (oleft->op_type == OP_RV2AV) {
+       if (oright->op_type != OP_RV2AV
+           || !cUNOPx(oright)->op_first
+           || cUNOPx(oright)->op_first->op_type != OP_GV
+           || cGVOPx_gv(cUNOPx(oleft)->op_first) !=
+              cGVOPx_gv(cUNOPx(oright)->op_first)
+       )
+           return NULL;
+    }
+    else if (oright->op_type != OP_PADAV
+       || oright->op_targ != oleft->op_targ
+    )
+       return NULL;
+
+    return oleft;
+}
+
 /* A peephole optimizer.  We visit the ops in the order they're to execute.
  * See the comments at the top of this file for more details about when
  * peep() is called */
 
 void
-Perl_peep(pTHX_ register OP *o)
+Perl_rpeep(pTHX_ register OP *o)
 {
     dVAR;
     register OP* oldop = NULL;
@@ -8367,10 +9285,62 @@ Perl_peep(pTHX_ register OP *o)
        o->op_opt = 1;
        PL_op = o;
        switch (o->op_type) {
-       case OP_NEXTSTATE:
        case OP_DBSTATE:
            PL_curcop = ((COP*)o);              /* for warnings */
            break;
+       case OP_NEXTSTATE:
+           PL_curcop = ((COP*)o);              /* for warnings */
+
+           /* 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.  */
+           if (!CopLABEL((COP*)o)) {
+               OP *nextop = o->op_next;
+               while (nextop && nextop->op_type == OP_NULL)
+                   nextop = nextop->op_next;
+
+               if (nextop && (nextop->op_type == OP_NEXTSTATE)) {
+                   COP *firstcop = (COP *)o;
+                   COP *secondcop = (COP *)nextop;
+                   /* We want the COP pointed to by o (and anything else) to
+                      become the next COP down the line.  */
+                   cop_free(firstcop);
+
+                   firstcop->op_next = secondcop->op_next;
+
+                   /* Now steal all its pointers, and duplicate the other
+                      data.  */
+                   firstcop->cop_line = secondcop->cop_line;
+#ifdef USE_ITHREADS
+                   firstcop->cop_stashpv = secondcop->cop_stashpv;
+                   firstcop->cop_file = secondcop->cop_file;
+#else
+                   firstcop->cop_stash = secondcop->cop_stash;
+                   firstcop->cop_filegv = secondcop->cop_filegv;
+#endif
+                   firstcop->cop_hints = secondcop->cop_hints;
+                   firstcop->cop_seq = secondcop->cop_seq;
+                   firstcop->cop_warnings = secondcop->cop_warnings;
+                   firstcop->cop_hints_hash = secondcop->cop_hints_hash;
+
+#ifdef USE_ITHREADS
+                   secondcop->cop_stashpv = NULL;
+                   secondcop->cop_file = NULL;
+#else
+                   secondcop->cop_stash = NULL;
+                   secondcop->cop_filegv = NULL;
+#endif
+                   secondcop->cop_warnings = NULL;
+                   secondcop->cop_hints_hash = NULL;
+
+                   /* If we use op_null(), and hence leave an ex-COP, some
+                      warnings are misreported. For example, the compile-time
+                      error in 'use strict; no strict refs;'  */
+                   secondcop->op_type = OP_NULL;
+                   secondcop->op_ppaddr = PL_ppaddr[OP_NULL];
+               }
+           }
+           break;
 
        case OP_CONST:
            if (cSVOPo->op_private & OPpCONST_STRICT)
@@ -8442,7 +9412,7 @@ Perl_peep(pTHX_ register OP *o)
                PL_curcop = ((COP*)o);
            }
            /* XXX: We avoid setting op_seq here to prevent later calls
-              to peep() from mistakenly concluding that optimisation
+              to rpeep() from mistakenly concluding that optimisation
               has already occurred. This doesn't fix the real problem,
               though (See 20010220.007). AMS 20010719 */
            /* op_seq functionality is now replaced by op_opt */
@@ -8548,7 +9518,7 @@ Perl_peep(pTHX_ register OP *o)
             sop = fop->op_sibling;
            while (cLOGOP->op_other->op_type == OP_NULL)
                cLOGOP->op_other = cLOGOP->op_other->op_next;
-           peep(cLOGOP->op_other); /* Recursive calls are not replaced by fptr calls */
+           CALL_RPEEP(cLOGOP->op_other);
           
           stitch_keys:     
            o->op_opt = 1;
@@ -8559,7 +9529,7 @@ Perl_peep(pTHX_ register OP *o)
             ){ 
                 OP * nop = o;
                 OP * lop = o;
-                if (!(nop->op_flags && OPf_WANT_VOID)) {
+                if (!((nop->op_flags & OPf_WANT) == OPf_WANT_VOID)) {
                     while (nop && nop->op_next) {
                         switch (nop->op_next->op_type) {
                             case OP_NOT:
@@ -8577,7 +9547,7 @@ Perl_peep(pTHX_ register OP *o)
                         }
                     }            
                 }
-                if (lop->op_flags && OPf_WANT_VOID) {
+                if ((lop->op_flags & OPf_WANT) == OPf_WANT_VOID) {
                     if (fop->op_type == OP_PADHV || fop->op_type == OP_RV2HV) 
                         cLOGOP->op_first = opt_scalarhv(fop);
                     if (sop && (sop->op_type == OP_PADHV || sop->op_type == OP_RV2HV)) 
@@ -8599,20 +9569,20 @@ Perl_peep(pTHX_ register OP *o)
        case OP_ONCE:
            while (cLOGOP->op_other->op_type == OP_NULL)
                cLOGOP->op_other = cLOGOP->op_other->op_next;
-           peep(cLOGOP->op_other); /* Recursive calls are not replaced by fptr calls */
+           CALL_RPEEP(cLOGOP->op_other);
            break;
 
        case OP_ENTERLOOP:
        case OP_ENTERITER:
            while (cLOOP->op_redoop->op_type == OP_NULL)
                cLOOP->op_redoop = cLOOP->op_redoop->op_next;
-           peep(cLOOP->op_redoop);
+           CALL_RPEEP(cLOOP->op_redoop);
            while (cLOOP->op_nextop->op_type == OP_NULL)
                cLOOP->op_nextop = cLOOP->op_nextop->op_next;
-           peep(cLOOP->op_nextop);
+           CALL_RPEEP(cLOOP->op_nextop);
            while (cLOOP->op_lastop->op_type == OP_NULL)
                cLOOP->op_lastop = cLOOP->op_lastop->op_next;
-           peep(cLOOP->op_lastop);
+           CALL_RPEEP(cLOOP->op_lastop);
            break;
 
        case OP_SUBST:
@@ -8621,7 +9591,7 @@ Perl_peep(pTHX_ register OP *o)
                   cPMOP->op_pmstashstartu.op_pmreplstart->op_type == OP_NULL)
                cPMOP->op_pmstashstartu.op_pmreplstart
                    = cPMOP->op_pmstashstartu.op_pmreplstart->op_next;
-           peep(cPMOP->op_pmstashstartu.op_pmreplstart);
+           CALL_RPEEP(cPMOP->op_pmstashstartu.op_pmreplstart);
            break;
 
        case OP_EXEC:
@@ -8745,6 +9715,20 @@ Perl_peep(pTHX_ register OP *o)
            }
            break;
        }
+       case OP_RV2SV:
+       case OP_RV2AV:
+       case OP_RV2HV:
+           if (oldop
+                && (  oldop->op_type == OP_AELEM
+                   || oldop->op_type == OP_PADSV
+                   || oldop->op_type == OP_RV2SV
+                   || oldop->op_type == OP_RV2GV
+                   || oldop->op_type == OP_HELEM
+                   )
+                && (oldop->op_private & OPpDEREF)
+           ) {
+               o->op_private |= OPpDEREFed;
+           }
 
        case OP_SORT: {
            /* will point to RV2AV or PADAV op on LHS/RHS of assign */
@@ -8784,62 +9768,8 @@ Perl_peep(pTHX_ register OP *o)
                oright = cUNOPx(oright)->op_sibling;
            }
 
-           if (!oright ||
-               (oright->op_type != OP_RV2AV && oright->op_type != OP_PADAV)
-               || oright->op_next != o
-               || (oright->op_private & OPpLVAL_INTRO)
-           )
-               break;
-
-           /* o2 follows the chain of op_nexts through the LHS of the
-            * assign (if any) to the aassign op itself */
-           o2 = o->op_next;
-           if (!o2 || o2->op_type != OP_NULL)
-               break;
-           o2 = o2->op_next;
-           if (!o2 || o2->op_type != OP_PUSHMARK)
-               break;
-           o2 = o2->op_next;
-           if (o2 && o2->op_type == OP_GV)
-               o2 = o2->op_next;
-           if (!o2
-               || (o2->op_type != OP_PADAV && o2->op_type != OP_RV2AV)
-               || (o2->op_private & OPpLVAL_INTRO)
-           )
-               break;
-           oleft = o2;
-           o2 = o2->op_next;
-           if (!o2 || o2->op_type != OP_NULL)
-               break;
-           o2 = o2->op_next;
-           if (!o2 || o2->op_type != OP_AASSIGN
-                   || (o2->op_flags & OPf_WANT) != OPf_WANT_VOID)
-               break;
-
-           /* check that the sort is the first arg on RHS of assign */
-
-           o2 = cUNOPx(o2)->op_first;
-           if (!o2 || o2->op_type != OP_NULL)
-               break;
-           o2 = cUNOPx(o2)->op_first;
-           if (!o2 || o2->op_type != OP_PUSHMARK)
-               break;
-           if (o2->op_sibling != o)
-               break;
-
-           /* check the array is the same on both sides */
-           if (oleft->op_type == OP_RV2AV) {
-               if (oright->op_type != OP_RV2AV
-                   || !cUNOPx(oright)->op_first
-                   || cUNOPx(oright)->op_first->op_type != OP_GV
-                   ||  cGVOPx_gv(cUNOPx(oleft)->op_first) !=
-                       cGVOPx_gv(cUNOPx(oright)->op_first)
-               )
-                   break;
-           }
-           else if (oright->op_type != OP_PADAV
-               || oright->op_targ != oleft->op_targ
-           )
+           oleft = is_inplace_av(o, oright);
+           if (!oleft)
                break;
 
            /* transfer MODishness etc from LHS arg to RHS arg */
@@ -8866,8 +9796,36 @@ Perl_peep(pTHX_ register OP *o)
        case OP_REVERSE: {
            OP *ourmark, *theirmark, *ourlast, *iter, *expushmark, *rv2av;
            OP *gvop = NULL;
+           OP *oleft, *oright;
            LISTOP *enter, *exlist;
 
+           /* @a = reverse @a */
+           if ((oright = cLISTOPo->op_first)
+                   && (oright->op_type == OP_PUSHMARK)
+                   && (oright = oright->op_sibling)
+                   && (oleft = is_inplace_av(o, oright))) {
+               OP *o2;
+
+               /* transfer MODishness etc from LHS arg to RHS arg */
+               oright->op_flags = oleft->op_flags;
+               o->op_private |= OPpREVERSE_INPLACE;
+
+               /* excise push->gv->rv2av->null->aassign */
+               o2 = o->op_next->op_next;
+               op_null(o2); /* PUSHMARK */
+               o2 = o2->op_next;
+               if (o2->op_type == OP_GV) {
+                   op_null(o2); /* GV */
+                   o2 = o2->op_next;
+               }
+               op_null(o2); /* RV2AV or PADAV */
+               o2 = o2->op_next->op_next;
+               op_null(o2); /* AASSIGN */
+
+               o->op_next = o2->op_next;
+               break;
+           }
+
            enter = (LISTOP *) o->op_next;
            if (!enter)
                break;
@@ -9003,54 +9961,109 @@ Perl_peep(pTHX_ register OP *o)
                assert (!cPMOP->op_pmstashstartu.op_pmreplstart);
            }
            break;
+
+       case OP_CUSTOM: {
+           Perl_cpeep_t cpeep = 
+               XopENTRY(Perl_custom_op_xop(aTHX_ o), xop_peep);
+           if (cpeep)
+               cpeep(aTHX_ o, oldop);
+           break;
+       }
+           
        }
        oldop = o;
     }
     LEAVE;
 }
 
-const char*
-Perl_custom_op_name(pTHX_ const OP* o)
+void
+Perl_peep(pTHX_ register OP *o)
 {
-    dVAR;
-    const IV index = PTR2IV(o->op_ppaddr);
-    SV* keysv;
-    HE* he;
+    CALL_RPEEP(o);
+}
 
-    PERL_ARGS_ASSERT_CUSTOM_OP_NAME;
+/*
+=head1 Custom Operators
+
+=for apidoc Ao||custom_op_xop
+Return the XOP structure for a given custom op. This function should be
+considered internal to OP_NAME and the other access macros: use them instead.
 
-    if (!PL_custom_op_names) /* This probably shouldn't happen */
-        return (char *)PL_op_name[OP_CUSTOM];
+=cut
+*/
 
-    keysv = sv_2mortal(newSViv(index));
+const XOP *
+Perl_custom_op_xop(pTHX_ const OP *o)
+{
+    SV *keysv;
+    HE *he = NULL;
+    XOP *xop;
+
+    static const XOP xop_null = { 0, 0, 0, 0, 0 };
+
+    PERL_ARGS_ASSERT_CUSTOM_OP_XOP;
+    assert(o->op_type == OP_CUSTOM);
+
+    /* This is wrong. It assumes a function pointer can be cast to IV,
+     * which isn't guaranteed, but this is what the old custom OP code
+     * did. In principle it should be safer to Copy the bytes of the
+     * pointer into a PV: since the new interface is hidden behind
+     * functions, this can be changed later if necessary.  */
+    /* Change custom_op_xop if this ever happens */
+    keysv = sv_2mortal(newSViv(PTR2IV(o->op_ppaddr)));
+
+    if (PL_custom_ops)
+       he = hv_fetch_ent(PL_custom_ops, keysv, 0, 0);
+
+    /* assume noone will have just registered a desc */
+    if (!he && PL_custom_op_names &&
+       (he = hv_fetch_ent(PL_custom_op_names, keysv, 0, 0))
+    ) {
+       const char *pv;
+       STRLEN l;
+
+       /* XXX does all this need to be shared mem? */
+       Newxz(xop, 1, XOP);
+       pv = SvPV(HeVAL(he), l);
+       XopENTRY_set(xop, xop_name, savepvn(pv, l));
+       if (PL_custom_op_descs &&
+           (he = hv_fetch_ent(PL_custom_op_descs, keysv, 0, 0))
+       ) {
+           pv = SvPV(HeVAL(he), l);
+           XopENTRY_set(xop, xop_desc, savepvn(pv, l));
+       }
+       Perl_custom_op_register(aTHX_ o->op_ppaddr, xop);
+       return xop;
+    }
 
-    he = hv_fetch_ent(PL_custom_op_names, keysv, 0, 0);
-    if (!he)
-        return (char *)PL_op_name[OP_CUSTOM]; /* Don't know who you are */
+    if (!he) return &xop_null;
 
-    return SvPV_nolen(HeVAL(he));
+    xop = INT2PTR(XOP *, SvIV(HeVAL(he)));
+    return xop;
 }
 
-const char*
-Perl_custom_op_desc(pTHX_ const OP* o)
-{
-    dVAR;
-    const IV index = PTR2IV(o->op_ppaddr);
-    SV* keysv;
-    HE* he;
+/*
+=for apidoc Ao||custom_op_register
+Register a custom op. See L<perlguts/"Custom Operators">.
+
+=cut
+*/
 
-    PERL_ARGS_ASSERT_CUSTOM_OP_DESC;
+void
+Perl_custom_op_register(pTHX_ Perl_ppaddr_t ppaddr, const XOP *xop)
+{
+    SV *keysv;
 
-    if (!PL_custom_op_descs)
-        return (char *)PL_op_desc[OP_CUSTOM];
+    PERL_ARGS_ASSERT_CUSTOM_OP_REGISTER;
 
-    keysv = sv_2mortal(newSViv(index));
+    /* see the comment in custom_op_xop */
+    keysv = sv_2mortal(newSViv(PTR2IV(ppaddr)));
 
-    he = hv_fetch_ent(PL_custom_op_descs, keysv, 0, 0);
-    if (!he)
-        return (char *)PL_op_desc[OP_CUSTOM];
+    if (!PL_custom_ops)
+       PL_custom_ops = newHV();
 
-    return SvPV_nolen(HeVAL(he));
+    if (!hv_store_ent(PL_custom_ops, keysv, newSViv(PTR2IV(xop)), 0))
+       Perl_croak(aTHX_ "panic: can't register custom OP %s", xop->xop_name);
 }
 
 #include "XSUB.h"