This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Clone the brief return logic and thereby remove a goto.
[perl5.git] / sv.c
diff --git a/sv.c b/sv.c
index 75c9934..6f0dcad 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -112,8 +112,7 @@ list, and call more_xiv() etc to add a new arena if the list is empty.
 
 At the time of very final cleanup, sv_free_arenas() is called from
 perl_destruct() to physically free all the arenas allocated since the
-start of the interpreter.  Note that this also clears PL_he_arenaroot,
-which is otherwise dealt with in hv.c.
+start of the interpreter.
 
 Manipulation of any of the PL_*root pointers is protected by enclosing
 LOCK_SV_MUTEX; ... UNLOCK_SV_MUTEX calls which should Do the Right Thing
@@ -596,8 +595,6 @@ Perl_sv_free_arenas(pTHX)
        PL_body_roots[i] = 0;
     }
 
-    free_arena(he);
-
     Safefree(PL_nice_chunk);
     PL_nice_chunk = Nullch;
     PL_nice_chunk_size = 0;
@@ -605,873 +602,390 @@ Perl_sv_free_arenas(pTHX)
     PL_sv_root = 0;
 }
 
-/* ---------------------------------------------------------------------
- *
- * support functions for report_uninit()
- */
+/*
+  Here are mid-level routines that manage the allocation of bodies out
+  of the various arenas.  There are 5 kinds of arenas:
 
-/* the maxiumum size of array or hash where we will scan looking
- * for the undefined element that triggered the warning */
+  1. SV-head arenas, which are discussed and handled above
+  2. regular body arenas
+  3. arenas for reduced-size bodies
+  4. Hash-Entry arenas
+  5. pte arenas (thread related)
 
-#define FUV_MAX_SEARCH_SIZE 1000
+  Arena types 2 & 3 are chained by body-type off an array of
+  arena-root pointers, which is indexed by svtype.  Some of the
+  larger/less used body types are malloced singly, since a large
+  unused block of them is wasteful.  Also, several svtypes dont have
+  bodies; the data fits into the sv-head itself.  The arena-root
+  pointer thus has a few unused root-pointers (which may be hijacked
+  later for arena types 4,5)
 
-/* Look for an entry in the hash whose value has the same SV as val;
- * If so, return a mortal copy of the key. */
+  3 differs from 2 as an optimization; some body types have several
+  unused fields in the front of the structure (which are kept in-place
+  for consistency).  These bodies can be allocated in smaller chunks,
+  because the leading fields arent accessed.  Pointers to such bodies
+  are decremented to point at the unused 'ghost' memory, knowing that
+  the pointers are used with offsets to the real memory.
 
-STATIC SV*
-S_find_hash_subscript(pTHX_ HV *hv, SV* val)
+  HE, HEK arenas are managed separately, with separate code, but may
+  be merge-able later..
+
+  PTE arenas are not sv-bodies, but they share these mid-level
+  mechanics, so are considered here.  The new mid-level mechanics rely
+  on the sv_type of the body being allocated, so we just reserve one
+  of the unused body-slots for PTEs, then use it in those (2) PTE
+  contexts below (line ~10k)
+*/
+
+STATIC void *
+S_more_bodies (pTHX_ size_t size, svtype sv_type)
 {
-    dVAR;
-    register HE **array;
-    I32 i;
+    void **arena_root  = &PL_body_arenaroots[sv_type];
+    void **root                = &PL_body_roots[sv_type];
+    char *start;
+    const char *end;
+    const size_t count = PERL_ARENA_SIZE / size;
 
-    if (!hv || SvMAGICAL(hv) || !HvARRAY(hv) ||
-                       (HvTOTALKEYS(hv) > FUV_MAX_SEARCH_SIZE))
-       return Nullsv;
+    Newx(start, count*size, char);
+    *((void **) start) = *arena_root;
+    *arena_root = (void *)start;
 
-    array = HvARRAY(hv);
+    end = start + (count-1) * size;
 
-    for (i=HvMAX(hv); i>0; i--) {
-       register HE *entry;
-       for (entry = array[i]; entry; entry = HeNEXT(entry)) {
-           if (HeVAL(entry) != val)
-               continue;
-           if (    HeVAL(entry) == &PL_sv_undef ||
-                   HeVAL(entry) == &PL_sv_placeholder)
-               continue;
-           if (!HeKEY(entry))
-               return Nullsv;
-           if (HeKLEN(entry) == HEf_SVKEY)
-               return sv_mortalcopy(HeKEY_sv(entry));
-           return sv_2mortal(newSVpvn(HeKEY(entry), HeKLEN(entry)));
-       }
-    }
-    return Nullsv;
-}
+    /* The initial slot is used to link the arenas together, so it isn't to be
+       linked into the list of ready-to-use bodies.  */
 
-/* Look for an entry in the array whose value has the same SV as val;
- * If so, return the index, otherwise return -1. */
+    start += size;
 
-STATIC I32
-S_find_array_subscript(pTHX_ AV *av, SV* val)
-{
-    SV** svp;
-    I32 i;
-    if (!av || SvMAGICAL(av) || !AvARRAY(av) ||
-                       (AvFILLp(av) > FUV_MAX_SEARCH_SIZE))
-       return -1;
+    *root = (void *)start;
 
-    svp = AvARRAY(av);
-    for (i=AvFILLp(av); i>=0; i--) {
-       if (svp[i] == val && svp[i] != &PL_sv_undef)
-           return i;
+    while (start < end) {
+       char * const next = start + size;
+       *(void**) start = (void *)next;
+       start = next;
     }
-    return -1;
+    *(void **)start = 0;
+
+    return *root;
 }
 
-/* S_varname(): return the name of a variable, optionally with a subscript.
- * If gv is non-zero, use the name of that global, along with gvtype (one
- * of "$", "@", "%"); otherwise use the name of the lexical at pad offset
- * targ.  Depending on the value of the subscript_type flag, return:
- */
+/* grab a new thing from the free list, allocating more if necessary */
 
-#define FUV_SUBSCRIPT_NONE     1       /* "@foo"          */
-#define FUV_SUBSCRIPT_ARRAY    2       /* "$foo[aindex]"  */
-#define FUV_SUBSCRIPT_HASH     3       /* "$foo{keyname}" */
-#define FUV_SUBSCRIPT_WITHIN   4       /* "within @foo"   */
+/* 1st, the inline version  */
 
-STATIC SV*
-S_varname(pTHX_ GV *gv, const char gvtype, PADOFFSET targ,
-       SV* keyname, I32 aindex, int subscript_type)
-{
+#define new_body_inline(xpv, size, sv_type) \
+    STMT_START { \
+       void **r3wt = &PL_body_roots[sv_type]; \
+       LOCK_SV_MUTEX; \
+       xpv = *((void **)(r3wt)) \
+         ? *((void **)(r3wt)) : S_more_bodies(aTHX_ size, sv_type); \
+       *(r3wt) = *(void**)(xpv); \
+       UNLOCK_SV_MUTEX; \
+    } STMT_END
 
-    SV * const name = sv_newmortal();
-    if (gv) {
-       char buffer[2];
-       buffer[0] = gvtype;
-       buffer[1] = 0;
+/* now use the inline version in the proper function */
 
-       /* as gv_fullname4(), but add literal '^' for $^FOO names  */
+#ifndef PURIFY
 
-       gv_fullname4(name, gv, buffer, 0);
+/* This isn't being used with -DPURIFY, so don't declare it. Otherwise
+   compilers issue warnings.  */
 
-       if ((unsigned int)SvPVX(name)[1] <= 26) {
-           buffer[0] = '^';
-           buffer[1] = SvPVX(name)[1] + 'A' - 1;
+STATIC void *
+S_new_body(pTHX_ size_t size, svtype sv_type)
+{
+    void *xpv;
+    new_body_inline(xpv, size, sv_type);
+    return xpv;
+}
 
-           /* Swap the 1 unprintable control character for the 2 byte pretty
-              version - ie substr($name, 1, 1) = $buffer; */
-           sv_insert(name, 1, 1, buffer, 2);
-       }
-    }
-    else {
-       U32 unused;
-       CV * const cv = find_runcv(&unused);
-       SV *sv;
-       AV *av;
+#endif
 
-       if (!cv || !CvPADLIST(cv))
-           return Nullsv;
-       av = (AV*)(*av_fetch(CvPADLIST(cv), 0, FALSE));
-       sv = *av_fetch(av, targ, FALSE);
-       /* SvLEN in a pad name is not to be trusted */
-       sv_setpv(name, SvPV_nolen_const(sv));
-    }
+/* return a thing to the free list */
 
-    if (subscript_type == FUV_SUBSCRIPT_HASH) {
-       SV * const sv = NEWSV(0,0);
-       *SvPVX(name) = '$';
-       Perl_sv_catpvf(aTHX_ name, "{%s}",
-           pv_display(sv,SvPVX_const(keyname), SvCUR(keyname), 0, 32));
-       SvREFCNT_dec(sv);
-    }
-    else if (subscript_type == FUV_SUBSCRIPT_ARRAY) {
-       *SvPVX(name) = '$';
-       Perl_sv_catpvf(aTHX_ name, "[%"IVdf"]", (IV)aindex);
-    }
-    else if (subscript_type == FUV_SUBSCRIPT_WITHIN)
-       sv_insert(name, 0, 0,  "within ", 7);
+#define del_body(thing, root)                  \
+    STMT_START {                               \
+       void **thing_copy = (void **)thing;     \
+       LOCK_SV_MUTEX;                          \
+       *thing_copy = *root;                    \
+       *root = (void*)thing_copy;              \
+       UNLOCK_SV_MUTEX;                        \
+    } STMT_END
 
-    return name;
-}
+/* 
+   Revisiting type 3 arenas, there are 4 body-types which have some
+   members that are never accessed.  They are XPV, XPVIV, XPVAV,
+   XPVHV, which have corresponding types: xpv_allocated,
+   xpviv_allocated, xpvav_allocated, xpvhv_allocated,
 
+   For these types, the arenas are carved up into *_allocated size
+   chunks, we thus avoid wasted memory for those unaccessed members.
+   When bodies are allocated, we adjust the pointer back in memory by
+   the size of the bit not allocated, so it's as if we allocated the
+   full structure.  (But things will all go boom if you write to the
+   part that is "not there", because you'll be overwriting the last
+   members of the preceding structure in memory.)
 
-/*
-=for apidoc find_uninit_var
+   We calculate the correction using the STRUCT_OFFSET macro. For example, if
+   xpv_allocated is the same structure as XPV then the two OFFSETs sum to zero,
+   and the pointer is unchanged. If the allocated structure is smaller (no
+   initial NV actually allocated) then the net effect is to subtract the size
+   of the NV from the pointer, to return a new pointer as if an initial NV were
+   actually allocated.
 
-Find the name of the undefined variable (if any) that caused the operator o
-to issue a "Use of uninitialized value" warning.
-If match is true, only return a name if it's value matches uninit_sv.
-So roughly speaking, if a unary operator (such as OP_COS) generates a
-warning, then following the direct child of the op may yield an
-OP_PADSV or OP_GV that gives the name of the undefined variable. On the
-other hand, with OP_ADD there are two branches to follow, so we only print
-the variable name if we get an exact match.
+   This is the same trick as was used for NV and IV bodies. Ironically it
+   doesn't need to be used for NV bodies any more, because NV is now at the
+   start of the structure. IV bodies don't need it either, because they are
+   no longer allocated.  */
 
-The name is returned as a mortal SV.
+/* The following 2 arrays hide the above details in a pair of
+   lookup-tables, allowing us to be body-type agnostic.
 
-Assumes that PL_op is the op that originally triggered the error, and that
-PL_comppad/PL_curpad points to the currently executing pad.
+   size maps svtype to its body's allocated size.
+   offset maps svtype to the body-pointer adjustment needed
 
-=cut
+   NB: elements in latter are 0 or <0, and are added during
+   allocation, and subtracted during deallocation.  It may be clearer
+   to invert the values, and call it shrinkage_by_svtype.
 */
 
-STATIC SV *
-S_find_uninit_var(pTHX_ OP* obase, SV* uninit_sv, bool match)
-{
-    dVAR;
-    SV *sv;
-    AV *av;
-    GV *gv;
-    OP *o, *o2, *kid;
-
-    if (!obase || (match && (!uninit_sv || uninit_sv == &PL_sv_undef ||
-                           uninit_sv == &PL_sv_placeholder)))
-       return Nullsv;
+struct body_details {
+    size_t size;       /* Size to allocate  */
+    size_t copy;       /* Size of structure to copy (may be shorter)  */
+    size_t offset;
+    bool cant_upgrade; /* Can upgrade this type */
+    bool zero_nv;      /* zero the NV when upgrading from this */
+    bool arena;                /* Allocated from an arena */
+};
 
-    switch (obase->op_type) {
+#define HADNV FALSE
+#define NONV TRUE
 
-    case OP_RV2AV:
-    case OP_RV2HV:
-    case OP_PADAV:
-    case OP_PADHV:
-      {
-       const bool pad  = (obase->op_type == OP_PADAV || obase->op_type == OP_PADHV);
-       const bool hash = (obase->op_type == OP_PADHV || obase->op_type == OP_RV2HV);
-       I32 index = 0;
-       SV *keysv = Nullsv;
-       int subscript_type = FUV_SUBSCRIPT_WITHIN;
-
-       if (pad) { /* @lex, %lex */
-           sv = PAD_SVl(obase->op_targ);
-           gv = Nullgv;
-       }
-       else {
-           if (cUNOPx(obase)->op_first->op_type == OP_GV) {
-           /* @global, %global */
-               gv = cGVOPx_gv(cUNOPx(obase)->op_first);
-               if (!gv)
-                   break;
-               sv = hash ? (SV*)GvHV(gv): (SV*)GvAV(gv);
-           }
-           else /* @{expr}, %{expr} */
-               return find_uninit_var(cUNOPx(obase)->op_first,
-                                                   uninit_sv, match);
-       }
+#ifdef PURIFY
+/* With -DPURFIY we allocate everything directly, and don't use arenas.
+   This seems a rather elegant way to simplify some of the code below.  */
+#define HASARENA FALSE
+#else
+#define HASARENA TRUE
+#endif
+#define NOARENA FALSE
 
-       /* attempt to find a match within the aggregate */
-       if (hash) {
-           keysv = S_find_hash_subscript(aTHX_ (HV*)sv, uninit_sv);
-           if (keysv)
-               subscript_type = FUV_SUBSCRIPT_HASH;
-       }
-       else {
-           index = S_find_array_subscript(aTHX_ (AV*)sv, uninit_sv);
-           if (index >= 0)
-               subscript_type = FUV_SUBSCRIPT_ARRAY;
-       }
+/* A macro to work out the offset needed to subtract from a pointer to (say)
 
-       if (match && subscript_type == FUV_SUBSCRIPT_WITHIN)
-           break;
+typedef struct {
+    STRLEN     xpv_cur;
+    STRLEN     xpv_len;
+} xpv_allocated;
 
-       return varname(gv, hash ? '%' : '@', obase->op_targ,
-                                   keysv, index, subscript_type);
-      }
+to make its members accessible via a pointer to (say)
 
-    case OP_PADSV:
-       if (match && PAD_SVl(obase->op_targ) != uninit_sv)
-           break;
-       return varname(Nullgv, '$', obase->op_targ,
-                                   Nullsv, 0, FUV_SUBSCRIPT_NONE);
+struct xpv {
+    NV         xnv_nv;
+    STRLEN     xpv_cur;
+    STRLEN     xpv_len;
+};
 
-    case OP_GVSV:
-       gv = cGVOPx_gv(obase);
-       if (!gv || (match && GvSV(gv) != uninit_sv))
-           break;
-       return varname(gv, '$', 0, Nullsv, 0, FUV_SUBSCRIPT_NONE);
+*/
 
-    case OP_AELEMFAST:
-       if (obase->op_flags & OPf_SPECIAL) { /* lexical array */
-           if (match) {
-               SV **svp;
-               av = (AV*)PAD_SV(obase->op_targ);
-               if (!av || SvRMAGICAL(av))
-                   break;
-               svp = av_fetch(av, (I32)obase->op_private, FALSE);
-               if (!svp || *svp != uninit_sv)
-                   break;
-           }
-           return varname(Nullgv, '$', obase->op_targ,
-                   Nullsv, (I32)obase->op_private, FUV_SUBSCRIPT_ARRAY);
-       }
-       else {
-           gv = cGVOPx_gv(obase);
-           if (!gv)
-               break;
-           if (match) {
-               SV **svp;
-               av = GvAV(gv);
-               if (!av || SvRMAGICAL(av))
-                   break;
-               svp = av_fetch(av, (I32)obase->op_private, FALSE);
-               if (!svp || *svp != uninit_sv)
-                   break;
-           }
-           return varname(gv, '$', 0,
-                   Nullsv, (I32)obase->op_private, FUV_SUBSCRIPT_ARRAY);
-       }
-       break;
+#define relative_STRUCT_OFFSET(longer, shorter, member) \
+    (STRUCT_OFFSET(shorter, member) - STRUCT_OFFSET(longer, member))
 
-    case OP_EXISTS:
-       o = cUNOPx(obase)->op_first;
-       if (!o || o->op_type != OP_NULL ||
-               ! (o->op_targ == OP_AELEM || o->op_targ == OP_HELEM))
-           break;
-       return find_uninit_var(cBINOPo->op_last, uninit_sv, match);
+/* Calculate the length to copy. Specifically work out the length less any
+   final padding the compiler needed to add.  See the comment in sv_upgrade
+   for why copying the padding proved to be a bug.  */
 
-    case OP_AELEM:
-    case OP_HELEM:
-       if (PL_op == obase)
-           /* $a[uninit_expr] or $h{uninit_expr} */
-           return find_uninit_var(cBINOPx(obase)->op_last, uninit_sv, match);
+#define copy_length(type, last_member) \
+       STRUCT_OFFSET(type, last_member) \
+       + sizeof (((type*)SvANY((SV*)0))->last_member)
 
-       gv = Nullgv;
-       o = cBINOPx(obase)->op_first;
-       kid = cBINOPx(obase)->op_last;
+static const struct body_details bodies_by_type[] = {
+    {0, 0, 0, FALSE, NONV, NOARENA},
+    /* IVs are in the head, so the allocation size is 0  */
+    {0, sizeof(IV), STRUCT_OFFSET(XPVIV, xiv_iv), FALSE, NONV, NOARENA},
+    /* 8 bytes on most ILP32 with IEEE doubles */
+    {sizeof(NV), sizeof(NV), 0, FALSE, HADNV, HASARENA},
+    /* RVs are in the head now */
+    /* However, this slot is overloaded and used by the pte  */
+    {0, 0, 0, FALSE, NONV, NOARENA},
+    /* 8 bytes on most ILP32 with IEEE doubles */
+    {sizeof(xpv_allocated),
+     copy_length(XPV, xpv_len)
+     + relative_STRUCT_OFFSET(XPV, xpv_allocated, xpv_cur),
+     - relative_STRUCT_OFFSET(XPV, xpv_allocated, xpv_cur),
+     FALSE, NONV, HASARENA},
+    /* 12 */
+    {sizeof(xpviv_allocated),
+     copy_length(XPVIV, xiv_u)
+     + relative_STRUCT_OFFSET(XPVIV, xpviv_allocated, xpv_cur),
+     - relative_STRUCT_OFFSET(XPVIV, xpviv_allocated, xpv_cur),
+     FALSE, NONV, HASARENA},
+    /* 20 */
+    {sizeof(XPVNV), copy_length(XPVNV, xiv_u), 0, FALSE, HADNV, HASARENA},
+    /* 28 */
+    {sizeof(XPVMG), copy_length(XPVMG, xmg_stash), 0, FALSE, HADNV, HASARENA},
+    /* 36 */
+    {sizeof(XPVBM), sizeof(XPVBM), 0, TRUE, HADNV, HASARENA},
+    /* 48 */
+    {sizeof(XPVGV), sizeof(XPVGV), 0, TRUE, HADNV, HASARENA},
+    /* 64 */
+    {sizeof(XPVLV), sizeof(XPVLV), 0, TRUE, HADNV, HASARENA},
+    /* 20 */
+    {sizeof(xpvav_allocated),
+     copy_length(XPVAV, xmg_stash)
+     + relative_STRUCT_OFFSET(XPVAV, xpvav_allocated, xav_fill),
+     - relative_STRUCT_OFFSET(XPVAV, xpvav_allocated, xav_fill),
+     TRUE, HADNV, HASARENA},
+    /* 20 */
+    {sizeof(xpvhv_allocated),
+     copy_length(XPVHV, xmg_stash)
+     + relative_STRUCT_OFFSET(XPVHV, xpvhv_allocated, xhv_fill),
+     - relative_STRUCT_OFFSET(XPVHV, xpvhv_allocated, xhv_fill),
+     TRUE, HADNV, HASARENA},
+    /* 76 */
+    {sizeof(XPVCV), sizeof(XPVCV), 0, TRUE, HADNV, HASARENA},
+    /* 80 */
+    {sizeof(XPVFM), sizeof(XPVFM), 0, TRUE, HADNV, NOARENA},
+    /* 84 */
+    {sizeof(XPVIO), sizeof(XPVIO), 0, TRUE, HADNV, NOARENA}
+};
 
-       /* get the av or hv, and optionally the gv */
-       sv = Nullsv;
-       if  (o->op_type == OP_PADAV || o->op_type == OP_PADHV) {
-           sv = PAD_SV(o->op_targ);
-       }
-       else if ((o->op_type == OP_RV2AV || o->op_type == OP_RV2HV)
-               && cUNOPo->op_first->op_type == OP_GV)
-       {
-           gv = cGVOPx_gv(cUNOPo->op_first);
-           if (!gv)
-               break;
-           sv = o->op_type == OP_RV2HV ? (SV*)GvHV(gv) : (SV*)GvAV(gv);
-       }
-       if (!sv)
-           break;
+#define new_body_type(sv_type)                 \
+    (void *)((char *)S_new_body(aTHX_ bodies_by_type[sv_type].size, sv_type)\
+            - bodies_by_type[sv_type].offset)
 
-       if (kid && kid->op_type == OP_CONST && SvOK(cSVOPx_sv(kid))) {
-           /* index is constant */
-           if (match) {
-               if (SvMAGICAL(sv))
-                   break;
-               if (obase->op_type == OP_HELEM) {
-                   HE* he = hv_fetch_ent((HV*)sv, cSVOPx_sv(kid), 0, 0);
-                   if (!he || HeVAL(he) != uninit_sv)
-                       break;
-               }
-               else {
-                   SV ** const svp = av_fetch((AV*)sv, SvIV(cSVOPx_sv(kid)), FALSE);
-                   if (!svp || *svp != uninit_sv)
-                       break;
-               }
-           }
-           if (obase->op_type == OP_HELEM)
-               return varname(gv, '%', o->op_targ,
-                           cSVOPx_sv(kid), 0, FUV_SUBSCRIPT_HASH);
-           else
-               return varname(gv, '@', o->op_targ, Nullsv,
-                           SvIV(cSVOPx_sv(kid)), FUV_SUBSCRIPT_ARRAY);
-           ;
-       }
-       else  {
-           /* index is an expression;
-            * attempt to find a match within the aggregate */
-           if (obase->op_type == OP_HELEM) {
-               SV * const keysv = S_find_hash_subscript(aTHX_ (HV*)sv, uninit_sv);
-               if (keysv)
-                   return varname(gv, '%', o->op_targ,
-                                               keysv, 0, FUV_SUBSCRIPT_HASH);
-           }
-           else {
-               const I32 index = S_find_array_subscript(aTHX_ (AV*)sv, uninit_sv);
-               if (index >= 0)
-                   return varname(gv, '@', o->op_targ,
-                                       Nullsv, index, FUV_SUBSCRIPT_ARRAY);
-           }
-           if (match)
-               break;
-           return varname(gv,
-               (o->op_type == OP_PADAV || o->op_type == OP_RV2AV)
-               ? '@' : '%',
-               o->op_targ, Nullsv, 0, FUV_SUBSCRIPT_WITHIN);
-       }
+#define del_body_type(p, sv_type)      \
+    del_body(p, &PL_body_roots[sv_type])
 
-       break;
 
-    case OP_AASSIGN:
-       /* only examine RHS */
-       return find_uninit_var(cBINOPx(obase)->op_first, uninit_sv, match);
+#define new_body_allocated(sv_type)            \
+    (void *)((char *)S_new_body(aTHX_ bodies_by_type[sv_type].size, sv_type)\
+            - bodies_by_type[sv_type].offset)
 
-    case OP_OPEN:
-       o = cUNOPx(obase)->op_first;
-       if (o->op_type == OP_PUSHMARK)
-           o = o->op_sibling;
+#define del_body_allocated(p, sv_type)         \
+    del_body(p + bodies_by_type[sv_type].offset, &PL_body_roots[sv_type])
 
-       if (!o->op_sibling) {
-           /* one-arg version of open is highly magical */
 
-           if (o->op_type == OP_GV) { /* open FOO; */
-               gv = cGVOPx_gv(o);
-               if (match && GvSV(gv) != uninit_sv)
-                   break;
-               return varname(gv, '$', 0,
-                           Nullsv, 0, FUV_SUBSCRIPT_NONE);
-           }
-           /* other possibilities not handled are:
-            * open $x; or open my $x;  should return '${*$x}'
-            * open expr;               should return '$'.expr ideally
-            */
-            break;
-       }
-       goto do_op;
+#define my_safemalloc(s)       (void*)safemalloc(s)
+#define my_safecalloc(s)       (void*)safecalloc(s, 1)
+#define my_safefree(p) safefree((char*)p)
 
-    /* ops where $_ may be an implicit arg */
-    case OP_TRANS:
-    case OP_SUBST:
-    case OP_MATCH:
-       if ( !(obase->op_flags & OPf_STACKED)) {
-           if (uninit_sv == ((obase->op_private & OPpTARGET_MY)
-                                ? PAD_SVl(obase->op_targ)
-                                : DEFSV))
-           {
-               sv = sv_newmortal();
-               sv_setpvn(sv, "$_", 2);
-               return sv;
-           }
-       }
-       goto do_op;
+#ifdef PURIFY
 
-    case OP_PRTF:
-    case OP_PRINT:
-       /* skip filehandle as it can't produce 'undef' warning  */
-       o = cUNOPx(obase)->op_first;
-       if ((obase->op_flags & OPf_STACKED) && o->op_type == OP_PUSHMARK)
-           o = o->op_sibling->op_sibling;
-       goto do_op2;
+#define new_XNV()      my_safemalloc(sizeof(XPVNV))
+#define del_XNV(p)     my_safefree(p)
 
+#define new_XPVNV()    my_safemalloc(sizeof(XPVNV))
+#define del_XPVNV(p)   my_safefree(p)
 
-    case OP_RV2SV:
-    case OP_CUSTOM:
-    case OP_ENTERSUB:
-       match = 1; /* XS or custom code could trigger random warnings */
-       goto do_op;
+#define new_XPVAV()    my_safemalloc(sizeof(XPVAV))
+#define del_XPVAV(p)   my_safefree(p)
 
-    case OP_SCHOMP:
-    case OP_CHOMP:
-       if (SvROK(PL_rs) && uninit_sv == SvRV(PL_rs))
-           return sv_2mortal(newSVpvn("${$/}", 5));
-       /* FALL THROUGH */
+#define new_XPVHV()    my_safemalloc(sizeof(XPVHV))
+#define del_XPVHV(p)   my_safefree(p)
 
-    default:
-    do_op:
-       if (!(obase->op_flags & OPf_KIDS))
-           break;
-       o = cUNOPx(obase)->op_first;
-       
-    do_op2:
-       if (!o)
-           break;
+#define new_XPVMG()    my_safemalloc(sizeof(XPVMG))
+#define del_XPVMG(p)   my_safefree(p)
 
-       /* if all except one arg are constant, or have no side-effects,
-        * or are optimized away, then it's unambiguous */
-       o2 = Nullop;
-       for (kid=o; kid; kid = kid->op_sibling) {
-           if (kid &&
-               (    (kid->op_type == OP_CONST && SvOK(cSVOPx_sv(kid)))
-                 || (kid->op_type == OP_NULL  && ! (kid->op_flags & OPf_KIDS))
-                 || (kid->op_type == OP_PUSHMARK)
-               )
-           )
-               continue;
-           if (o2) { /* more than one found */
-               o2 = Nullop;
-               break;
-           }
-           o2 = kid;
-       }
-       if (o2)
-           return find_uninit_var(o2, uninit_sv, match);
+#define new_XPVGV()    my_safemalloc(sizeof(XPVGV))
+#define del_XPVGV(p)   my_safefree(p)
 
-       /* scan all args */
-       while (o) {
-           sv = find_uninit_var(o, uninit_sv, 1);
-           if (sv)
-               return sv;
-           o = o->op_sibling;
-       }
-       break;
-    }
-    return Nullsv;
-}
+#else /* !PURIFY */
 
+#define new_XNV()      new_body_type(SVt_NV)
+#define del_XNV(p)     del_body_type(p, SVt_NV)
 
-/*
-=for apidoc report_uninit
+#define new_XPVNV()    new_body_type(SVt_PVNV)
+#define del_XPVNV(p)   del_body_type(p, SVt_PVNV)
 
-Print appropriate "Use of uninitialized variable" warning
+#define new_XPVAV()    new_body_allocated(SVt_PVAV)
+#define del_XPVAV(p)   del_body_allocated(p, SVt_PVAV)
 
-=cut
-*/
+#define new_XPVHV()    new_body_allocated(SVt_PVHV)
+#define del_XPVHV(p)   del_body_allocated(p, SVt_PVHV)
 
-void
-Perl_report_uninit(pTHX_ SV* uninit_sv)
-{
-    if (PL_op) {
-       SV* varname = Nullsv;
-       if (uninit_sv) {
-           varname = find_uninit_var(PL_op, uninit_sv,0);
-           if (varname)
-               sv_insert(varname, 0, 0, " ", 1);
-       }
-       Perl_warner(aTHX_ packWARN(WARN_UNINITIALIZED), PL_warn_uninit,
-               varname ? SvPV_nolen_const(varname) : "",
-               " in ", OP_DESC(PL_op));
-    }
-    else
-       Perl_warner(aTHX_ packWARN(WARN_UNINITIALIZED), PL_warn_uninit,
-                   "", "", "");
-}
+#define new_XPVMG()    new_body_type(SVt_PVMG)
+#define del_XPVMG(p)   del_body_type(p, SVt_PVMG)
 
-/*
-  Here are mid-level routines that manage the allocation of bodies out
-  of the various arenas.  There are 5 kinds of arenas:
+#define new_XPVGV()    new_body_type(SVt_PVGV)
+#define del_XPVGV(p)   del_body_type(p, SVt_PVGV)
 
-  1. SV-head arenas, which are discussed and handled above
-  2. regular body arenas
-  3. arenas for reduced-size bodies
-  4. Hash-Entry arenas
-  5. pte arenas (thread related)
+#endif /* PURIFY */
 
-  Arena types 2 & 3 are chained by body-type off an array of
-  arena-root pointers, which is indexed by svtype.  Some of the
-  larger/less used body types are malloced singly, since a large
-  unused block of them is wasteful.  Also, several svtypes dont have
-  bodies; the data fits into the sv-head itself.  The arena-root
-  pointer thus has a few unused root-pointers (which may be hijacked
-  later for arena types 4,5)
+/* no arena for you! */
 
-  3 differs from 2 as an optimization; some body types have several
-  unused fields in the front of the structure (which are kept in-place
-  for consistency).  These bodies can be allocated in smaller chunks,
-  because the leading fields arent accessed.  Pointers to such bodies
-  are decremented to point at the unused 'ghost' memory, knowing that
-  the pointers are used with offsets to the real memory.
+#define new_NOARENA(details) \
+       my_safemalloc((details)->size + (details)->offset)
+#define new_NOARENAZ(details) \
+       my_safecalloc((details)->size + (details)->offset)
 
-  HE, HEK arenas are managed separately, with separate code, but may
-  be merge-able later..
+/*
+=for apidoc sv_upgrade
 
-  PTE arenas are not sv-bodies, but they share these mid-level
-  mechanics, so are considered here.  The new mid-level mechanics rely
-  on the sv_type of the body being allocated, so we just reserve one
-  of the unused body-slots for PTEs, then use it in those (2) PTE
-  contexts below (line ~10k)
+Upgrade an SV to a more complex form.  Generally adds a new body type to the
+SV, then copies across as much information as possible from the old body.
+You generally want to use the C<SvUPGRADE> macro wrapper. See also C<svtype>.
+
+=cut
 */
 
-STATIC void *
-S_more_bodies (pTHX_ size_t size, svtype sv_type)
+void
+Perl_sv_upgrade(pTHX_ register SV *sv, U32 new_type)
 {
-    void **arena_root  = &PL_body_arenaroots[sv_type];
-    void **root                = &PL_body_roots[sv_type];
-    char *start;
-    const char *end;
-    const size_t count = PERL_ARENA_SIZE / size;
-
-    Newx(start, count*size, char);
-    *((void **) start) = *arena_root;
-    *arena_root = (void *)start;
-
-    end = start + (count-1) * size;
-
-    /* The initial slot is used to link the arenas together, so it isn't to be
-       linked into the list of ready-to-use bodies.  */
-
-    start += size;
-
-    *root = (void *)start;
+    void*      old_body;
+    void*      new_body;
+    const U32  old_type = SvTYPE(sv);
+    const struct body_details *const old_type_details
+       = bodies_by_type + old_type;
+    const struct body_details *new_type_details = bodies_by_type + new_type;
 
-    while (start < end) {
-       char * const next = start + size;
-       *(void**) start = (void *)next;
-       start = next;
+    if (new_type != SVt_PV && SvIsCOW(sv)) {
+       sv_force_normal_flags(sv, 0);
     }
-    *(void **)start = 0;
 
-    return *root;
-}
-
-/* grab a new thing from the free list, allocating more if necessary */
+    if (old_type == new_type)
+       return;
 
-/* 1st, the inline version  */
+    if (old_type > new_type)
+       Perl_croak(aTHX_ "sv_upgrade from type %d down to type %d",
+               (int)old_type, (int)new_type);
 
-#define new_body_inline(xpv, size, sv_type) \
-    STMT_START { \
-       void **r3wt = &PL_body_roots[sv_type]; \
-       LOCK_SV_MUTEX; \
-       xpv = *((void **)(r3wt)) \
-         ? *((void **)(r3wt)) : S_more_bodies(aTHX_ size, sv_type); \
-       *(r3wt) = *(void**)(xpv); \
-       UNLOCK_SV_MUTEX; \
-    } STMT_END
 
-/* now use the inline version in the proper function */
+    old_body = SvANY(sv);
 
-#ifndef PURIFY
+    /* Copying structures onto other structures that have been neatly zeroed
+       has a subtle gotcha. Consider XPVMG
 
-/* This isn't being used with -DPURIFY, so don't declare it. Otherwise
-   compilers issue warnings.  */
+       +------+------+------+------+------+-------+-------+
+       |     NV      | CUR  | LEN  |  IV  | MAGIC | STASH |
+       +------+------+------+------+------+-------+-------+
+       0      4      8     12     16     20      24      28
 
-STATIC void *
-S_new_body(pTHX_ size_t size, svtype sv_type)
-{
-    void *xpv;
-    new_body_inline(xpv, size, sv_type);
-    return xpv;
-}
+       where NVs are aligned to 8 bytes, so that sizeof that structure is
+       actually 32 bytes long, with 4 bytes of padding at the end:
 
-#endif
+       +------+------+------+------+------+-------+-------+------+
+       |     NV      | CUR  | LEN  |  IV  | MAGIC | STASH | ???  |
+       +------+------+------+------+------+-------+-------+------+
+       0      4      8     12     16     20      24      28     32
 
-/* return a thing to the free list */
+       so what happens if you allocate memory for this structure:
 
-#define del_body(thing, root)                  \
-    STMT_START {                               \
-       void **thing_copy = (void **)thing;     \
-       LOCK_SV_MUTEX;                          \
-       *thing_copy = *root;                    \
-       *root = (void*)thing_copy;              \
-       UNLOCK_SV_MUTEX;                        \
-    } STMT_END
+       +------+------+------+------+------+-------+-------+------+------+...
+       |     NV      | CUR  | LEN  |  IV  | MAGIC | STASH |  GP  | NAME |
+       +------+------+------+------+------+-------+-------+------+------+...
+       0      4      8     12     16     20      24      28     32     36
 
-/* 
-   Revisiting type 3 arenas, there are 4 body-types which have some
-   members that are never accessed.  They are XPV, XPVIV, XPVAV,
-   XPVHV, which have corresponding types: xpv_allocated,
-   xpviv_allocated, xpvav_allocated, xpvhv_allocated,
+       zero it, then copy sizeof(XPVMG) bytes on top of it? Not quite what you
+       expect, because you copy the area marked ??? onto GP. Now, ??? may have
+       started out as zero once, but it's quite possible that it isn't. So now,
+       rather than a nicely zeroed GP, you have it pointing somewhere random.
+       Bugs ensue.
 
-   For these types, the arenas are carved up into *_allocated size
-   chunks, we thus avoid wasted memory for those unaccessed members.
-   When bodies are allocated, we adjust the pointer back in memory by
-   the size of the bit not allocated, so it's as if we allocated the
-   full structure.  (But things will all go boom if you write to the
-   part that is "not there", because you'll be overwriting the last
-   members of the preceding structure in memory.)
+       (In fact, GP ends up pointing at a previous GP structure, because the
+       principle cause of the padding in XPVMG getting garbage is a copy of
+       sizeof(XPVMG) bytes from a XPVGV structure in sv_unglob)
 
-   We calculate the correction using the STRUCT_OFFSET macro. For example, if
-   xpv_allocated is the same structure as XPV then the two OFFSETs sum to zero,
-   and the pointer is unchanged. If the allocated structure is smaller (no
-   initial NV actually allocated) then the net effect is to subtract the size
-   of the NV from the pointer, to return a new pointer as if an initial NV were
-   actually allocated.
-
-   This is the same trick as was used for NV and IV bodies. Ironically it
-   doesn't need to be used for NV bodies any more, because NV is now at the
-   start of the structure. IV bodies don't need it either, because they are
-   no longer allocated.  */
-
-/* The following 2 arrays hide the above details in a pair of
-   lookup-tables, allowing us to be body-type agnostic.
-
-   size maps svtype to its body's allocated size.
-   offset maps svtype to the body-pointer adjustment needed
-
-   NB: elements in latter are 0 or <0, and are added during
-   allocation, and subtracted during deallocation.  It may be clearer
-   to invert the values, and call it shrinkage_by_svtype.
-*/
-
-struct body_details {
-    size_t size;       /* Size to allocate  */
-    size_t copy;       /* Size of structure to copy (may be shorter)  */
-    int offset;
-    bool cant_upgrade; /* Can upgrade this type */
-    bool zero_nv;      /* zero the NV when upgrading from this */
-    bool arena;                /* Allocated from an arena */
-};
-
-#define HADNV FALSE
-#define NONV TRUE
-
-#define HASARENA TRUE
-#define NOARENA FALSE
-
-static const struct body_details bodies_by_type[] = {
-    {0, 0, 0, FALSE, NONV, NOARENA},
-    /* IVs are in the head, so the allocation size is 0  */
-    {0, sizeof(IV), -STRUCT_OFFSET(XPVIV, xiv_iv), FALSE, NONV, NOARENA},
-    /* 8 bytes on most ILP32 with IEEE doubles */
-    {sizeof(NV), sizeof(NV), 0, FALSE, HADNV, HASARENA},
-    /* RVs are in the head now */
-    /* However, this slot is overloaded and used by the pte  */
-    {0, 0, 0, FALSE, NONV, NOARENA},
-    /* 8 bytes on most ILP32 with IEEE doubles */
-    {sizeof(xpv_allocated),
-     STRUCT_OFFSET(XPV, xpv_len) + sizeof (((XPV*)SvANY((SV*)0))->xpv_len)
-     + STRUCT_OFFSET(xpv_allocated, xpv_cur) - STRUCT_OFFSET(XPV, xpv_cur),
-     + STRUCT_OFFSET(xpv_allocated, xpv_cur) - STRUCT_OFFSET(XPV, xpv_cur)
-     , FALSE, NONV, HASARENA},
-    /* 12 */
-    {sizeof(xpviv_allocated),
-     STRUCT_OFFSET(XPVIV, xiv_u) + sizeof (((XPVIV*)SvANY((SV*)0))->xiv_u)
-     + STRUCT_OFFSET(xpviv_allocated, xpv_cur) - STRUCT_OFFSET(XPVIV, xpv_cur),
-     + STRUCT_OFFSET(xpviv_allocated, xpv_cur) - STRUCT_OFFSET(XPVIV, xpv_cur)
-    , FALSE, NONV, HASARENA},
-    /* 20 */
-    {sizeof(XPVNV),
-     STRUCT_OFFSET(XPVNV, xiv_u) + sizeof (((XPVNV*)SvANY((SV*)0))->xiv_u),
-     0, FALSE, HADNV, HASARENA},
-    /* 28 */
-    {sizeof(XPVMG),
-     STRUCT_OFFSET(XPVMG, xmg_stash) + sizeof (((XPVMG*)SvANY((SV*)0))->xmg_stash),
-     0, FALSE, HADNV, HASARENA},
-    /* 36 */
-    {sizeof(XPVBM), sizeof(XPVBM), 0, TRUE, HADNV, HASARENA},
-    /* 48 */
-    {sizeof(XPVGV), sizeof(XPVGV), 0, TRUE, HADNV, HASARENA},
-    /* 64 */
-    {sizeof(XPVLV), sizeof(XPVLV), 0, TRUE, HADNV, HASARENA},
-    /* 20 */
-    {sizeof(xpvav_allocated),
-     STRUCT_OFFSET(XPVAV, xmg_stash)
-     + sizeof (((XPVAV*)SvANY((SV *)0))->xmg_stash)
-     + STRUCT_OFFSET(xpvav_allocated, xav_fill)
-     - STRUCT_OFFSET(XPVAV, xav_fill),
-     STRUCT_OFFSET(xpvav_allocated, xav_fill)
-     - STRUCT_OFFSET(XPVAV, xav_fill), TRUE, HADNV, HASARENA},
-    /* 20 */
-    {sizeof(xpvhv_allocated),
-     STRUCT_OFFSET(XPVHV, xmg_stash)
-     + sizeof (((XPVHV*)SvANY((SV *)0))->xmg_stash)
-     + STRUCT_OFFSET(xpvhv_allocated, xhv_fill)
-     - STRUCT_OFFSET(XPVHV, xhv_fill),
-     STRUCT_OFFSET(xpvhv_allocated, xhv_fill)
-     - STRUCT_OFFSET(XPVHV, xhv_fill), TRUE, HADNV, HASARENA},
-    /* 76 */
-    {sizeof(XPVCV), sizeof(XPVCV), 0, TRUE, HADNV, HASARENA},
-    /* 80 */
-    {sizeof(XPVFM), sizeof(XPVFM), 0, TRUE, HADNV, NOARENA},
-    /* 84 */
-    {sizeof(XPVIO), sizeof(XPVIO), 0, TRUE, HADNV, NOARENA}
-};
-
-#define new_body_type(sv_type)                 \
-    (void *)((char *)S_new_body(aTHX_ bodies_by_type[sv_type].size, sv_type)\
-            + bodies_by_type[sv_type].offset)
-
-#define del_body_type(p, sv_type)      \
-    del_body(p, &PL_body_roots[sv_type])
-
-
-#define new_body_allocated(sv_type)            \
-    (void *)((char *)S_new_body(aTHX_ bodies_by_type[sv_type].size, sv_type)\
-            + bodies_by_type[sv_type].offset)
-
-#define del_body_allocated(p, sv_type)         \
-    del_body(p - bodies_by_type[sv_type].offset, &PL_body_roots[sv_type])
-
-
-#define my_safemalloc(s)       (void*)safemalloc(s)
-#define my_safecalloc(s)       (void*)safecalloc(s, 1)
-#define my_safefree(p) safefree((char*)p)
-
-#ifdef PURIFY
-
-#define new_XNV()      my_safemalloc(sizeof(XPVNV))
-#define del_XNV(p)     my_safefree(p)
-
-#define new_XPV()      my_safemalloc(sizeof(XPV))
-#define del_XPV(p)     my_safefree(p)
-
-#define new_XPVIV()    my_safemalloc(sizeof(XPVIV))
-#define del_XPVIV(p)   my_safefree(p)
-
-#define new_XPVNV()    my_safemalloc(sizeof(XPVNV))
-#define del_XPVNV(p)   my_safefree(p)
-
-#define new_XPVCV()    my_safemalloc(sizeof(XPVCV))
-#define del_XPVCV(p)   my_safefree(p)
-
-#define new_XPVAV()    my_safemalloc(sizeof(XPVAV))
-#define del_XPVAV(p)   my_safefree(p)
-
-#define new_XPVHV()    my_safemalloc(sizeof(XPVHV))
-#define del_XPVHV(p)   my_safefree(p)
-
-#define new_XPVMG()    my_safemalloc(sizeof(XPVMG))
-#define del_XPVMG(p)   my_safefree(p)
-
-#define new_XPVGV()    my_safemalloc(sizeof(XPVGV))
-#define del_XPVGV(p)   my_safefree(p)
-
-#define new_XPVLV()    my_safemalloc(sizeof(XPVLV))
-#define del_XPVLV(p)   my_safefree(p)
-
-#define new_XPVBM()    my_safemalloc(sizeof(XPVBM))
-#define del_XPVBM(p)   my_safefree(p)
-
-#else /* !PURIFY */
-
-#define new_XNV()      new_body_type(SVt_NV)
-#define del_XNV(p)     del_body_type(p, SVt_NV)
-
-#define new_XPV()      new_body_allocated(SVt_PV)
-#define del_XPV(p)     del_body_allocated(p, SVt_PV)
-
-#define new_XPVIV()    new_body_allocated(SVt_PVIV)
-#define del_XPVIV(p)   del_body_allocated(p, SVt_PVIV)
-
-#define new_XPVNV()    new_body_type(SVt_PVNV)
-#define del_XPVNV(p)   del_body_type(p, SVt_PVNV)
-
-#define new_XPVCV()    new_body_type(SVt_PVCV)
-#define del_XPVCV(p)   del_body_type(p, SVt_PVCV)
-
-#define new_XPVAV()    new_body_allocated(SVt_PVAV)
-#define del_XPVAV(p)   del_body_allocated(p, SVt_PVAV)
-
-#define new_XPVHV()    new_body_allocated(SVt_PVHV)
-#define del_XPVHV(p)   del_body_allocated(p, SVt_PVHV)
-
-#define new_XPVMG()    new_body_type(SVt_PVMG)
-#define del_XPVMG(p)   del_body_type(p, SVt_PVMG)
-
-#define new_XPVGV()    new_body_type(SVt_PVGV)
-#define del_XPVGV(p)   del_body_type(p, SVt_PVGV)
-
-#define new_XPVLV()    new_body_type(SVt_PVLV)
-#define del_XPVLV(p)   del_body_type(p, SVt_PVLV)
-
-#define new_XPVBM()    new_body_type(SVt_PVBM)
-#define del_XPVBM(p)   del_body_type(p, SVt_PVBM)
-
-#endif /* PURIFY */
-
-/* no arena for you! */
-
-#define new_NOARENA(details) \
-       my_safecalloc((details)->size - (details)->offset)
-
-#define new_XPVFM()    my_safemalloc(sizeof(XPVFM))
-#define del_XPVFM(p)   my_safefree(p)
-
-#define new_XPVIO()    my_safemalloc(sizeof(XPVIO))
-#define del_XPVIO(p)   my_safefree(p)
-
-
-
-/*
-=for apidoc sv_upgrade
-
-Upgrade an SV to a more complex form.  Generally adds a new body type to the
-SV, then copies across as much information as possible from the old body.
-You generally want to use the C<SvUPGRADE> macro wrapper. See also C<svtype>.
-
-=cut
-*/
-
-void
-Perl_sv_upgrade(pTHX_ register SV *sv, U32 new_type)
-{
-    void*      old_body;
-    void*      new_body;
-    const U32  old_type = SvTYPE(sv);
-    const struct body_details *const old_type_details
-       = bodies_by_type + old_type;
-    const struct body_details *new_type_details = bodies_by_type + new_type;
-
-    if (new_type != SVt_PV && SvIsCOW(sv)) {
-       sv_force_normal_flags(sv, 0);
-    }
-
-    if (old_type == new_type)
-       return;
-
-    if (old_type > new_type)
-       Perl_croak(aTHX_ "sv_upgrade from type %d down to type %d",
-               (int)old_type, (int)new_type);
-
-
-    old_body = SvANY(sv);
-
-    /* Copying structures onto other structures that have been neatly zeroed
-       has a subtle gotcha. Consider XPVMG
-
-       +------+------+------+------+------+-------+-------+
-       |     NV      | CUR  | LEN  |  IV  | MAGIC | STASH |
-       +------+------+------+------+------+-------+-------+
-       0      4      8     12     16     20      24      28
-
-       where NVs are aligned to 8 bytes, so that sizeof that structure is
-       actually 32 bytes long, with 4 bytes of padding at the end:
-
-       +------+------+------+------+------+-------+-------+------+
-       |     NV      | CUR  | LEN  |  IV  | MAGIC | STASH | ???  |
-       +------+------+------+------+------+-------+-------+------+
-       0      4      8     12     16     20      24      28     32
-
-       so what happens if you allocate memory for this structure:
-
-       +------+------+------+------+------+-------+-------+------+------+...
-       |     NV      | CUR  | LEN  |  IV  | MAGIC | STASH |  GP  | NAME |
-       +------+------+------+------+------+-------+-------+------+------+...
-       0      4      8     12     16     20      24      28     32     36
-
-       zero it, then copy sizeof(XPVMG) bytes on top of it? Not quite what you
-       expect, because you copy the area marked ??? onto GP. Now, ??? may have
-       started out as zero once, but it's quite possible that it isn't. So now,
-       rather than a nicely zeroed GP, you have it pointing somewhere random.
-       Bugs ensue.
-
-       (In fact, GP ends up pointing at a previous GP structure, because the
-       principle cause of the padding in XPVMG getting garbage is a copy of
-       sizeof(XPVMG) bytes from a XPVGV structure in sv_unglob)
-
-       So we are careful and work out the size of used parts of all the
-       structures.  */
+       So we are careful and work out the size of used parts of all the
+       structures.  */
 
     switch (old_type) {
     case SVt_NULL:
@@ -1590,24 +1104,21 @@ Perl_sv_upgrade(pTHX_ register SV *sv, U32 new_type)
     case SVt_PV:
 
        assert(new_type_details->size);
-#ifndef PURIFY
+       /* We always allocated the full length item with PURIFY. To do this
+          we fake things so that arena is false for all 16 types..  */
        if(new_type_details->arena) {
            /* This points to the start of the allocated area.  */
            new_body_inline(new_body, new_type_details->size, new_type);
            Zero(new_body, new_type_details->size, char);
-           new_body = ((char *)new_body) + new_type_details->offset;
+           new_body = ((char *)new_body) - new_type_details->offset;
        } else {
-           new_body = new_NOARENA(new_type_details);
+           new_body = new_NOARENAZ(new_type_details);
        }
-#else
-       /* We always allocated the full length item with PURIFY */
-       new_body = new_NOARENA(new_type_details);
-#endif
        SvANY(sv) = new_body;
 
        if (old_type_details->copy) {
-           Copy((char *)old_body - old_type_details->offset,
-                (char *)new_body - old_type_details->offset,
+           Copy((char *)old_body + old_type_details->offset,
+                (char *)new_body + old_type_details->offset,
                 old_type_details->copy, char);
        }
 
@@ -1632,7 +1143,7 @@ Perl_sv_upgrade(pTHX_ register SV *sv, U32 new_type)
 #ifdef PURIFY
        my_safefree(old_body);
 #else
-       del_body((void*)((char*)old_body - old_type_details->offset),
+       del_body((void*)((char*)old_body + old_type_details->offset),
                 &PL_body_roots[old_type]);
 #endif
     }
@@ -2241,13 +1752,11 @@ Perl_sv_2iv_flags(pTHX_ register SV *sv, I32 flags)
                )
                SvIOK_on(sv);
            SvIsUV_on(sv);
-         ret_iv_max:
            DEBUG_c(PerlIO_printf(Perl_debug_log,
                                  "0x%"UVxf" 2iv(%"UVuf" => %"IVdf") (as unsigned)\n",
                                  PTR2UV(sv),
                                  SvUVX(sv),
                                  SvUVX(sv)));
-           return (IV)SvUVX(sv);
        }
     }
     else if (SvPOKp(sv) && SvLEN(sv)) {
@@ -2361,7 +1870,6 @@ Perl_sv_2iv_flags(pTHX_ register SV *sv, I32 flags)
                        SvIsUV_on(sv);
                    }
                }
-               goto ret_iv_max;
            }
 #else /* NV_PRESERVES_UV */
             if ((numtype & (IS_NUMBER_IN_UV | IS_NUMBER_NOT_INT))
@@ -2395,9 +1903,7 @@ Perl_sv_2iv_flags(pTHX_ register SV *sv, I32 flags)
                          1      1       already read UV.
                        so there's no point in sv_2iuv_non_preserve() attempting
                        to use atol, strtol, strtoul etc.  */
-                    if (sv_2iuv_non_preserve (sv, numtype)
-                        >= IS_NUMBER_OVERFLOW_IV)
-                    goto ret_iv_max;
+                    sv_2iuv_non_preserve (sv, numtype);
                 }
             }
 #endif /* NV_PRESERVES_UV */
@@ -3081,114 +2587,92 @@ Perl_sv_2pv_flags(pTHX_ register SV *sv, STRLEN *lp, I32 flags)
            else {
                MAGIC *mg;
                
-               switch (SvTYPE(sv)) {
-               case SVt_PVMG:
-                   if ( ((SvFLAGS(sv) &
+               if (SvTYPE(sv) == SVt_PVMG && ((SvFLAGS(sv) &
                           (SVs_OBJECT|SVf_OK|SVs_GMG|SVs_SMG|SVs_RMG))
                          == (SVs_OBJECT|SVs_SMG))
-                        && (mg = mg_find(sv, PERL_MAGIC_qr))) {
-                        const regexp *re = (regexp *)mg->mg_obj;
-
-                       if (!mg->mg_ptr) {
-                            const char *fptr = "msix";
-                           char reflags[6];
-                           char ch;
-                           int left = 0;
-                           int right = 4;
-                            char need_newline = 0;
-                           U16 reganch = (U16)((re->reganch & PMf_COMPILETIME) >> 12);
-
-                           while((ch = *fptr++)) {
-                               if(reganch & 1) {
-                                   reflags[left++] = ch;
-                               }
-                               else {
-                                   reflags[right--] = ch;
-                               }
-                               reganch >>= 1;
-                           }
-                           if(left != 4) {
-                               reflags[left] = '-';
-                               left = 5;
-                           }
-
-                           mg->mg_len = re->prelen + 4 + left;
-                            /*
-                             * If /x was used, we have to worry about a regex
-                             * ending with a comment later being embedded
-                             * within another regex. If so, we don't want this
-                             * regex's "commentization" to leak out to the
-                             * right part of the enclosing regex, we must cap
-                             * it with a newline.
-                             *
-                             * So, if /x was used, we scan backwards from the
-                             * end of the regex. If we find a '#' before we
-                             * find a newline, we need to add a newline
-                             * ourself. If we find a '\n' first (or if we
-                             * don't find '#' or '\n'), we don't need to add
-                             * anything.  -jfriedl
-                             */
-                            if (PMf_EXTENDED & re->reganch)
-                            {
-                                const char *endptr = re->precomp + re->prelen;
-                                while (endptr >= re->precomp)
-                                {
-                                    const char c = *(endptr--);
-                                    if (c == '\n')
-                                        break; /* don't need another */
-                                    if (c == '#') {
-                                        /* we end while in a comment, so we
-                                           need a newline */
-                                        mg->mg_len++; /* save space for it */
-                                        need_newline = 1; /* note to add it */
-                                       break;
-                                    }
-                                }
-                            }
+                   && (mg = mg_find(sv, PERL_MAGIC_qr))) {
+                   const regexp *re = (regexp *)mg->mg_obj;
+
+                   if (!mg->mg_ptr) {
+                       const char *fptr = "msix";
+                       char reflags[6];
+                       char ch;
+                       int left = 0;
+                       int right = 4;
+                       char need_newline = 0;
+                       U16 reganch =
+                           (U16)((re->reganch & PMf_COMPILETIME) >> 12);
+
+                       while((ch = *fptr++)) {
+                           if(reganch & 1) {
+                               reflags[left++] = ch;
+                           }
+                           else {
+                               reflags[right--] = ch;
+                           }
+                           reganch >>= 1;
+                       }
+                       if(left != 4) {
+                           reflags[left] = '-';
+                           left = 5;
+                       }
 
-                           Newx(mg->mg_ptr, mg->mg_len + 1 + left, char);
-                           Copy("(?", mg->mg_ptr, 2, char);
-                           Copy(reflags, mg->mg_ptr+2, left, char);
-                           Copy(":", mg->mg_ptr+left+2, 1, char);
-                           Copy(re->precomp, mg->mg_ptr+3+left, re->prelen, char);
-                            if (need_newline)
-                                mg->mg_ptr[mg->mg_len - 2] = '\n';
-                           mg->mg_ptr[mg->mg_len - 1] = ')';
-                           mg->mg_ptr[mg->mg_len] = 0;
+                       mg->mg_len = re->prelen + 4 + left;
+                       /*
+                        * If /x was used, we have to worry about a regex
+                        * ending with a comment later being embedded
+                        * within another regex. If so, we don't want this
+                        * regex's "commentization" to leak out to the
+                        * right part of the enclosing regex, we must cap
+                        * it with a newline.
+                        *
+                        * So, if /x was used, we scan backwards from the
+                        * end of the regex. If we find a '#' before we
+                        * find a newline, we need to add a newline
+                        * ourself. If we find a '\n' first (or if we
+                        * don't find '#' or '\n'), we don't need to add
+                        * anything.  -jfriedl
+                        */
+                       if (PMf_EXTENDED & re->reganch) {
+                           const char *endptr = re->precomp + re->prelen;
+                           while (endptr >= re->precomp) {
+                               const char c = *(endptr--);
+                               if (c == '\n')
+                                   break; /* don't need another */
+                               if (c == '#') {
+                                   /* we end while in a comment, so we
+                                      need a newline */
+                                   mg->mg_len++; /* save space for it */
+                                   need_newline = 1; /* note to add it */
+                                   break;
+                               }
+                           }
                        }
-                       PL_reginterp_cnt += re->program[0].next_off;
-
-                       if (re->reganch & ROPT_UTF8)
-                           SvUTF8_on(origsv);
-                       else
-                           SvUTF8_off(origsv);
-                       if (lp)
-                           *lp = mg->mg_len;
-                       return mg->mg_ptr;
+
+                       Newx(mg->mg_ptr, mg->mg_len + 1 + left, char);
+                       Copy("(?", mg->mg_ptr, 2, char);
+                       Copy(reflags, mg->mg_ptr+2, left, char);
+                       Copy(":", mg->mg_ptr+left+2, 1, char);
+                       Copy(re->precomp, mg->mg_ptr+3+left, re->prelen, char);
+                       if (need_newline)
+                           mg->mg_ptr[mg->mg_len - 2] = '\n';
+                       mg->mg_ptr[mg->mg_len - 1] = ')';
+                       mg->mg_ptr[mg->mg_len] = 0;
                    }
-                                       /* Fall through */
-               case SVt_NULL:
-               case SVt_IV:
-               case SVt_NV:
-               case SVt_RV:
-               case SVt_PV:
-               case SVt_PVIV:
-               case SVt_PVNV:
-               case SVt_PVBM:  typestr = SvROK(sv) ? "REF" : "SCALAR"; break;
-               case SVt_PVLV:  typestr = SvROK(sv) ? "REF"
-                               /* tied lvalues should appear to be
-                                * scalars for backwards compatitbility */
-                               : (LvTYPE(sv) == 't' || LvTYPE(sv) == 'T')
-                                   ? "SCALAR" : "LVALUE";      break;
-               case SVt_PVAV:  typestr = "ARRAY";      break;
-               case SVt_PVHV:  typestr = "HASH";       break;
-               case SVt_PVCV:  typestr = "CODE";       break;
-               case SVt_PVGV:  typestr = "GLOB";       break;
-               case SVt_PVFM:  typestr = "FORMAT";     break;
-               case SVt_PVIO:  typestr = "IO";         break;
-               default:        typestr = "UNKNOWN";    break;
+                   PL_reginterp_cnt += re->program[0].next_off;
+
+                   if (re->reganch & ROPT_UTF8)
+                       SvUTF8_on(origsv);
+                   else
+                       SvUTF8_off(origsv);
+                   if (lp)
+                       *lp = mg->mg_len;
+                   return mg->mg_ptr;
                }
-               tsv = NEWSV(0,0);
+
+               typestr = sv_reftype(sv, 0);
+
+               tsv = sv_newmortal();
                if (SvOBJECT(sv)) {
                    const char * const name = HvNAME_get(SvSTASH(sv));
                    Perl_sv_setpvf(aTHX_ tsv, "%s=%s(0x%"UVxf")",
@@ -3196,7 +2680,9 @@ Perl_sv_2pv_flags(pTHX_ register SV *sv, STRLEN *lp, I32 flags)
                }
                else
                    Perl_sv_setpvf(aTHX_ tsv, "%s(0x%"UVxf")", typestr, PTR2UV(sv));
-               goto tokensaveref;
+               if (lp)
+                   *lp = SvCUR(tsv);
+               return SvPVX(tsv);
            }
            if (lp)
                *lp = strlen(typestr);
@@ -3294,7 +2780,6 @@ Perl_sv_2pv_flags(pTHX_ register SV *sv, STRLEN *lp, I32 flags)
     if (SvROK(sv)) {   /* XXX Skip this when sv_pvn_force calls */
        /* Sneaky stuff here */
 
-      tokensaveref:
        if (!tsv)
            tsv = newSVpvn(tmpbuf, len);
        sv_2mortal(tsv);
@@ -5349,9 +4834,9 @@ void
 Perl_sv_clear(pTHX_ register SV *sv)
 {
     dVAR;
-    void** old_body_arena;
-    size_t old_body_offset;
     const U32 type = SvTYPE(sv);
+    const struct body_details *const sv_type_details
+       = bodies_by_type + type;
 
     assert(sv);
     assert(SvREFCNT(sv) == 0);
@@ -5359,9 +4844,6 @@ Perl_sv_clear(pTHX_ register SV *sv)
     if (type <= SVt_IV)
        return;
 
-    old_body_arena = 0;
-    old_body_offset = 0;
-
     if (SvOBJECT(sv)) {
        if (PL_defstash) {              /* Still have a symbol table? */
            dSP;
@@ -5433,26 +4915,18 @@ Perl_sv_clear(pTHX_ register SV *sv)
        Safefree(IoTOP_NAME(sv));
        Safefree(IoFMT_NAME(sv));
        Safefree(IoBOTTOM_NAME(sv));
-       /* PVIOs aren't from arenas  */
        goto freescalar;
     case SVt_PVBM:
-       old_body_arena = &PL_body_roots[SVt_PVBM];
        goto freescalar;
     case SVt_PVCV:
-       old_body_arena = &PL_body_roots[SVt_PVCV];
     case SVt_PVFM:
-       /* PVFMs aren't from arenas  */
        cv_undef((CV*)sv);
        goto freescalar;
     case SVt_PVHV:
        hv_undef((HV*)sv);
-       old_body_arena = &PL_body_roots[SVt_PVHV];
-       old_body_offset = STRUCT_OFFSET(XPVHV, xhv_fill);
        break;
     case SVt_PVAV:
        av_undef((AV*)sv);
-       old_body_arena = &PL_body_roots[SVt_PVAV];
-       old_body_offset = STRUCT_OFFSET(XPVAV, xav_fill);
        break;
     case SVt_PVLV:
        if (LvTYPE(sv) == 'T') { /* for tie: return HE to pool */
@@ -5462,7 +4936,6 @@ Perl_sv_clear(pTHX_ register SV *sv)
        }
        else if (LvTYPE(sv) != 't') /* unless tie: unrefcnted fake SV**  */
            SvREFCNT_dec(LvTARG(sv));
-       old_body_arena = &PL_body_roots[SVt_PVLV];
        goto freescalar;
     case SVt_PVGV:
        gp_free((GV*)sv);
@@ -5471,29 +4944,17 @@ Perl_sv_clear(pTHX_ register SV *sv)
           have a back reference to us, which needs to be cleared.  */
        if (GvSTASH(sv))
            sv_del_backref((SV*)GvSTASH(sv), sv);
-       old_body_arena = &PL_body_roots[SVt_PVGV];
-       goto freescalar;
     case SVt_PVMG:
-       old_body_arena = &PL_body_roots[SVt_PVMG];
-       goto freescalar;
     case SVt_PVNV:
-       old_body_arena = &PL_body_roots[SVt_PVNV];
-       goto freescalar;
     case SVt_PVIV:
-       old_body_arena = &PL_body_roots[SVt_PVIV];
-       old_body_offset = STRUCT_OFFSET(XPVIV, xpv_cur);
       freescalar:
        /* Don't bother with SvOOK_off(sv); as we're only going to free it.  */
        if (SvOOK(sv)) {
            SvPV_set(sv, SvPVX_mutable(sv) - SvIVX(sv));
            /* Don't even bother with turning off the OOK flag.  */
        }
-       goto pvrv_common;
     case SVt_PV:
-       old_body_arena = &PL_body_roots[SVt_PV];
-       old_body_offset = STRUCT_OFFSET(XPV, xpv_cur);
     case SVt_RV:
-    pvrv_common:
        if (SvROK(sv)) {
            SV *target = SvRV(sv);
            if (SvWEAKREF(sv))
@@ -5528,22 +4989,19 @@ Perl_sv_clear(pTHX_ register SV *sv)
 #endif
        break;
     case SVt_NV:
-       old_body_arena = PL_body_roots[SVt_NV];
        break;
     }
 
     SvFLAGS(sv) &= SVf_BREAK;
     SvFLAGS(sv) |= SVTYPEMASK;
 
-#ifndef PURIFY
-    if (old_body_arena) {
-       del_body(((char *)SvANY(sv) + old_body_offset), old_body_arena);
+    if (sv_type_details->arena) {
+       del_body(((char *)SvANY(sv) + sv_type_details->offset),
+                &PL_body_roots[type]);
+    }
+    else if (sv_type_details->size) {
+       my_safefree(SvANY(sv));
     }
-    else
-#endif
-       if (type > SVt_RV) {
-           my_safefree(SvANY(sv));
-       }
 }
 
 /*
@@ -8494,8 +7952,6 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
        }
        else if (svix < svmax) {
            sv_catsv(sv, *svargs);
-           if (DO_UTF8(*svargs))
-               SvUTF8_on(sv);
        }
        return;
     }
@@ -8503,8 +7959,6 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
                pat[1] == '-' && pat[2] == 'p') {
        argsv = va_arg(*args, SV*);
        sv_catsv(sv, argsv);
-       if (DO_UTF8(argsv))
-           SvUTF8_on(sv);
        return;
     }
 
@@ -8879,9 +8333,15 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
 
        if (vectorize)
            argsv = vecsv;
-       else if (!args)
-           argsv = (efix ? efix <= svmax : svix < svmax) ?
-                   svargs[efix ? efix-1 : svix++] : &PL_sv_undef;
+       else if (!args) {
+           if (efix) {
+               const I32 i = efix-1;
+               argsv = (i >= 0 && i < svmax) ? svargs[i] : &PL_sv_undef;
+           } else {
+               argsv = (svix >= 0 && svix < svmax)
+                   ? svargs[svix++] : &PL_sv_undef;
+           }
+       }
 
        switch (c = *q++) {
 
@@ -9113,6 +8573,8 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
                        *--ptr = '0';
                    break;
                case 2:
+                   if (!uv)
+                       alt = FALSE;
                    do {
                        dig = uv & 1;
                        *--ptr = '0' + dig;
@@ -9409,6 +8871,8 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
 
        /* calculate width before utf8_upgrade changes it */
        have = esignlen + zeros + elen;
+       if (have < zeros)
+           Perl_croak_nocontext(PL_memory_wrap);
 
        if (is_utf8 != has_utf8) {
             if (is_utf8) {
@@ -9429,6 +8893,8 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
        need = (have > width ? have : width);
        gap = need - have;
 
+       if (need >= (((STRLEN)~0) - SvCUR(sv) - dotstrlen - 1))
+           Perl_croak_nocontext(PL_memory_wrap);
        SvGROW(sv, SvCUR(sv) + need + dotstrlen + 1);
        p = SvEND(sv);
        if (esignlen && fill == '0') {
@@ -10054,12 +9520,10 @@ Perl_sv_dup(pTHX_ SV *sstr, CLONE_PARAMS* param)
     default:
        {
            /* These are all the types that need complex bodies allocating.  */
-           size_t new_body_length;
            void *new_body;
            const svtype sv_type = SvTYPE(sstr);
            const struct body_details *const sv_type_details
                = bodies_by_type + sv_type;
-           
 
            switch (sv_type) {
            default:
@@ -10067,61 +9531,44 @@ Perl_sv_dup(pTHX_ SV *sstr, CLONE_PARAMS* param)
                           (IV)SvTYPE(sstr));
                break;
 
-           case SVt_PVIO:
-           case SVt_PVFM:
-               new_body = new_NOARENA(sv_type_details);
-               new_body_length = sv_type_details->copy;
-               break;
-
-           case SVt_PVHV:
-               new_body_length = sv_type_details->copy;
-               goto new_body;
-           case SVt_PVAV:
-               new_body_length = sv_type_details->copy;
-               goto new_body;
            case SVt_PVGV:
                if (GvUNIQUE((GV*)sstr)) {
                    /* Do sharing here, and fall through */
                }
+           case SVt_PVIO:
+           case SVt_PVFM:
+           case SVt_PVHV:
+           case SVt_PVAV:
            case SVt_PVBM:
            case SVt_PVCV:
            case SVt_PVLV:
            case SVt_PVMG:
            case SVt_PVNV:
-               new_body_length = sv_type_details->copy;
-               goto new_body;
-
            case SVt_PVIV:
-               new_body_length = sv_type_details->copy;
-               goto new_body; 
            case SVt_PV:
-               new_body_length = sv_type_details->copy;
-           new_body:
-               assert(new_body_length);
-#ifndef PURIFY
-               new_body_inline(new_body, new_body_length, SvTYPE(sstr));
-
-               new_body = (void*)((char*)new_body + sv_type_details->offset);
-#else
-               /* We always allocated the full length item with PURIFY */
-               new_body_length += - sv_type_details->offset;
-               new_body = my_safemalloc(new_body_length);
-#endif
+               assert(sv_type_details->copy);
+               if (sv_type_details->arena) {
+                   new_body_inline(new_body, sv_type_details->copy, sv_type);
+                   new_body
+                       = (void*)((char*)new_body - sv_type_details->offset);
+               } else {
+                   new_body = new_NOARENA(sv_type_details);
+               }
            }
            assert(new_body);
            SvANY(dstr) = new_body;
 
 #ifndef PURIFY
-           Copy(((char*)SvANY(sstr)) - sv_type_details->offset,
-                ((char*)SvANY(dstr)) - sv_type_details->offset,
-                new_body_length, char);
+           Copy(((char*)SvANY(sstr)) + sv_type_details->offset,
+                ((char*)SvANY(dstr)) + sv_type_details->offset,
+                sv_type_details->copy, char);
 #else
            Copy(((char*)SvANY(sstr)),
                 ((char*)SvANY(dstr)),
-                new_body_length, char);
+                sv_type_details->size + sv_type_details->offset, char);
 #endif
 
-           if (SvTYPE(sstr) != SVt_PVAV && SvTYPE(sstr) != SVt_PVHV)
+           if (sv_type != SVt_PVAV && sv_type != SVt_PVHV)
                Perl_rvpv_dup(aTHX_ dstr, sstr, param);
 
            /* The Copy above means that all the source (unduplicated) pointers
@@ -10129,14 +9576,15 @@ Perl_sv_dup(pTHX_ SV *sstr, CLONE_PARAMS* param)
               pointers in either, but it's possible that there's less cache
               missing by always going for the destination.
               FIXME - instrument and check that assumption  */
-           if (SvTYPE(sstr) >= SVt_PVMG) {
+           if (sv_type >= SVt_PVMG) {
                if (SvMAGIC(dstr))
                    SvMAGIC_set(dstr, mg_dup(SvMAGIC(dstr), param));
                if (SvSTASH(dstr))
                    SvSTASH_set(dstr, hv_dup_inc(SvSTASH(dstr), param));
            }
 
-           switch (SvTYPE(sstr)) {
+           /* The cast silences a GCC warning about unhandled types.  */
+           switch ((int)sv_type) {
            case SVt_PV:
                break;
            case SVt_PVIV:
@@ -10916,9 +10364,6 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
     Zero(&PL_body_arenaroots, 1, PL_body_arenaroots);
     Zero(&PL_body_roots, 1, PL_body_roots);
     
-    PL_he_arenaroot    = NULL;
-    PL_he_root         = NULL;
-
     PL_nice_chunk      = NULL;
     PL_nice_chunk_size = 0;
     PL_sv_count                = 0;
@@ -11591,130 +11036,604 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
            FREETMPS;
            LEAVE;
        }
-    }
+    }
+
+    SvREFCNT_dec(param->stashes);
+
+    /* orphaned? eg threads->new inside BEGIN or use */
+    if (PL_compcv && ! SvREFCNT(PL_compcv)) {
+       (void)SvREFCNT_inc(PL_compcv);
+       SAVEFREESV(PL_compcv);
+    }
+
+    return my_perl;
+}
+
+#endif /* USE_ITHREADS */
+
+/*
+=head1 Unicode Support
+
+=for apidoc sv_recode_to_utf8
+
+The encoding is assumed to be an Encode object, on entry the PV
+of the sv is assumed to be octets in that encoding, and the sv
+will be converted into Unicode (and UTF-8).
+
+If the sv already is UTF-8 (or if it is not POK), or if the encoding
+is not a reference, nothing is done to the sv.  If the encoding is not
+an C<Encode::XS> Encoding object, bad things will happen.
+(See F<lib/encoding.pm> and L<Encode>).
+
+The PV of the sv is returned.
+
+=cut */
+
+char *
+Perl_sv_recode_to_utf8(pTHX_ SV *sv, SV *encoding)
+{
+    dVAR;
+    if (SvPOK(sv) && !SvUTF8(sv) && !IN_BYTES && SvROK(encoding)) {
+       SV *uni;
+       STRLEN len;
+       const char *s;
+       dSP;
+       ENTER;
+       SAVETMPS;
+       save_re_context();
+       PUSHMARK(sp);
+       EXTEND(SP, 3);
+       XPUSHs(encoding);
+       XPUSHs(sv);
+/*
+  NI-S 2002/07/09
+  Passing sv_yes is wrong - it needs to be or'ed set of constants
+  for Encode::XS, while UTf-8 decode (currently) assumes a true value means
+  remove converted chars from source.
+
+  Both will default the value - let them.
+
+       XPUSHs(&PL_sv_yes);
+*/
+       PUTBACK;
+       call_method("decode", G_SCALAR);
+       SPAGAIN;
+       uni = POPs;
+       PUTBACK;
+       s = SvPV_const(uni, len);
+       if (s != SvPVX_const(sv)) {
+           SvGROW(sv, len + 1);
+           Move(s, SvPVX(sv), len + 1, char);
+           SvCUR_set(sv, len);
+       }
+       FREETMPS;
+       LEAVE;
+       SvUTF8_on(sv);
+       return SvPVX(sv);
+    }
+    return SvPOKp(sv) ? SvPVX(sv) : NULL;
+}
+
+/*
+=for apidoc sv_cat_decode
+
+The encoding is assumed to be an Encode object, the PV of the ssv is
+assumed to be octets in that encoding and decoding the input starts
+from the position which (PV + *offset) pointed to.  The dsv will be
+concatenated the decoded UTF-8 string from ssv.  Decoding will terminate
+when the string tstr appears in decoding output or the input ends on
+the PV of the ssv. The value which the offset points will be modified
+to the last input position on the ssv.
+
+Returns TRUE if the terminator was found, else returns FALSE.
+
+=cut */
+
+bool
+Perl_sv_cat_decode(pTHX_ SV *dsv, SV *encoding,
+                  SV *ssv, int *offset, char *tstr, int tlen)
+{
+    dVAR;
+    bool ret = FALSE;
+    if (SvPOK(ssv) && SvPOK(dsv) && SvROK(encoding) && offset) {
+       SV *offsv;
+       dSP;
+       ENTER;
+       SAVETMPS;
+       save_re_context();
+       PUSHMARK(sp);
+       EXTEND(SP, 6);
+       XPUSHs(encoding);
+       XPUSHs(dsv);
+       XPUSHs(ssv);
+       XPUSHs(offsv = sv_2mortal(newSViv(*offset)));
+       XPUSHs(sv_2mortal(newSVpvn(tstr, tlen)));
+       PUTBACK;
+       call_method("cat_decode", G_SCALAR);
+       SPAGAIN;
+       ret = SvTRUE(TOPs);
+       *offset = SvIV(offsv);
+       PUTBACK;
+       FREETMPS;
+       LEAVE;
+    }
+    else
+        Perl_croak(aTHX_ "Invalid argument to sv_cat_decode");
+    return ret;
+
+}
+
+/* ---------------------------------------------------------------------
+ *
+ * support functions for report_uninit()
+ */
+
+/* the maxiumum size of array or hash where we will scan looking
+ * for the undefined element that triggered the warning */
+
+#define FUV_MAX_SEARCH_SIZE 1000
+
+/* Look for an entry in the hash whose value has the same SV as val;
+ * If so, return a mortal copy of the key. */
+
+STATIC SV*
+S_find_hash_subscript(pTHX_ HV *hv, SV* val)
+{
+    dVAR;
+    register HE **array;
+    I32 i;
+
+    if (!hv || SvMAGICAL(hv) || !HvARRAY(hv) ||
+                       (HvTOTALKEYS(hv) > FUV_MAX_SEARCH_SIZE))
+       return Nullsv;
+
+    array = HvARRAY(hv);
+
+    for (i=HvMAX(hv); i>0; i--) {
+       register HE *entry;
+       for (entry = array[i]; entry; entry = HeNEXT(entry)) {
+           if (HeVAL(entry) != val)
+               continue;
+           if (    HeVAL(entry) == &PL_sv_undef ||
+                   HeVAL(entry) == &PL_sv_placeholder)
+               continue;
+           if (!HeKEY(entry))
+               return Nullsv;
+           if (HeKLEN(entry) == HEf_SVKEY)
+               return sv_mortalcopy(HeKEY_sv(entry));
+           return sv_2mortal(newSVpvn(HeKEY(entry), HeKLEN(entry)));
+       }
+    }
+    return Nullsv;
+}
+
+/* Look for an entry in the array whose value has the same SV as val;
+ * If so, return the index, otherwise return -1. */
+
+STATIC I32
+S_find_array_subscript(pTHX_ AV *av, SV* val)
+{
+    SV** svp;
+    I32 i;
+    if (!av || SvMAGICAL(av) || !AvARRAY(av) ||
+                       (AvFILLp(av) > FUV_MAX_SEARCH_SIZE))
+       return -1;
+
+    svp = AvARRAY(av);
+    for (i=AvFILLp(av); i>=0; i--) {
+       if (svp[i] == val && svp[i] != &PL_sv_undef)
+           return i;
+    }
+    return -1;
+}
+
+/* S_varname(): return the name of a variable, optionally with a subscript.
+ * If gv is non-zero, use the name of that global, along with gvtype (one
+ * of "$", "@", "%"); otherwise use the name of the lexical at pad offset
+ * targ.  Depending on the value of the subscript_type flag, return:
+ */
+
+#define FUV_SUBSCRIPT_NONE     1       /* "@foo"          */
+#define FUV_SUBSCRIPT_ARRAY    2       /* "$foo[aindex]"  */
+#define FUV_SUBSCRIPT_HASH     3       /* "$foo{keyname}" */
+#define FUV_SUBSCRIPT_WITHIN   4       /* "within @foo"   */
+
+STATIC SV*
+S_varname(pTHX_ GV *gv, const char gvtype, PADOFFSET targ,
+       SV* keyname, I32 aindex, int subscript_type)
+{
+
+    SV * const name = sv_newmortal();
+    if (gv) {
+       char buffer[2];
+       buffer[0] = gvtype;
+       buffer[1] = 0;
+
+       /* as gv_fullname4(), but add literal '^' for $^FOO names  */
+
+       gv_fullname4(name, gv, buffer, 0);
+
+       if ((unsigned int)SvPVX(name)[1] <= 26) {
+           buffer[0] = '^';
+           buffer[1] = SvPVX(name)[1] + 'A' - 1;
+
+           /* Swap the 1 unprintable control character for the 2 byte pretty
+              version - ie substr($name, 1, 1) = $buffer; */
+           sv_insert(name, 1, 1, buffer, 2);
+       }
+    }
+    else {
+       U32 unused;
+       CV * const cv = find_runcv(&unused);
+       SV *sv;
+       AV *av;
+
+       if (!cv || !CvPADLIST(cv))
+           return Nullsv;
+       av = (AV*)(*av_fetch(CvPADLIST(cv), 0, FALSE));
+       sv = *av_fetch(av, targ, FALSE);
+       /* SvLEN in a pad name is not to be trusted */
+       sv_setpv(name, SvPV_nolen_const(sv));
+    }
+
+    if (subscript_type == FUV_SUBSCRIPT_HASH) {
+       SV * const sv = NEWSV(0,0);
+       *SvPVX(name) = '$';
+       Perl_sv_catpvf(aTHX_ name, "{%s}",
+           pv_display(sv,SvPVX_const(keyname), SvCUR(keyname), 0, 32));
+       SvREFCNT_dec(sv);
+    }
+    else if (subscript_type == FUV_SUBSCRIPT_ARRAY) {
+       *SvPVX(name) = '$';
+       Perl_sv_catpvf(aTHX_ name, "[%"IVdf"]", (IV)aindex);
+    }
+    else if (subscript_type == FUV_SUBSCRIPT_WITHIN)
+       sv_insert(name, 0, 0,  "within ", 7);
+
+    return name;
+}
+
+
+/*
+=for apidoc find_uninit_var
+
+Find the name of the undefined variable (if any) that caused the operator o
+to issue a "Use of uninitialized value" warning.
+If match is true, only return a name if it's value matches uninit_sv.
+So roughly speaking, if a unary operator (such as OP_COS) generates a
+warning, then following the direct child of the op may yield an
+OP_PADSV or OP_GV that gives the name of the undefined variable. On the
+other hand, with OP_ADD there are two branches to follow, so we only print
+the variable name if we get an exact match.
+
+The name is returned as a mortal SV.
+
+Assumes that PL_op is the op that originally triggered the error, and that
+PL_comppad/PL_curpad points to the currently executing pad.
+
+=cut
+*/
+
+STATIC SV *
+S_find_uninit_var(pTHX_ OP* obase, SV* uninit_sv, bool match)
+{
+    dVAR;
+    SV *sv;
+    AV *av;
+    GV *gv;
+    OP *o, *o2, *kid;
+
+    if (!obase || (match && (!uninit_sv || uninit_sv == &PL_sv_undef ||
+                           uninit_sv == &PL_sv_placeholder)))
+       return Nullsv;
+
+    switch (obase->op_type) {
+
+    case OP_RV2AV:
+    case OP_RV2HV:
+    case OP_PADAV:
+    case OP_PADHV:
+      {
+       const bool pad  = (obase->op_type == OP_PADAV || obase->op_type == OP_PADHV);
+       const bool hash = (obase->op_type == OP_PADHV || obase->op_type == OP_RV2HV);
+       I32 index = 0;
+       SV *keysv = Nullsv;
+       int subscript_type = FUV_SUBSCRIPT_WITHIN;
+
+       if (pad) { /* @lex, %lex */
+           sv = PAD_SVl(obase->op_targ);
+           gv = Nullgv;
+       }
+       else {
+           if (cUNOPx(obase)->op_first->op_type == OP_GV) {
+           /* @global, %global */
+               gv = cGVOPx_gv(cUNOPx(obase)->op_first);
+               if (!gv)
+                   break;
+               sv = hash ? (SV*)GvHV(gv): (SV*)GvAV(gv);
+           }
+           else /* @{expr}, %{expr} */
+               return find_uninit_var(cUNOPx(obase)->op_first,
+                                                   uninit_sv, match);
+       }
+
+       /* attempt to find a match within the aggregate */
+       if (hash) {
+           keysv = S_find_hash_subscript(aTHX_ (HV*)sv, uninit_sv);
+           if (keysv)
+               subscript_type = FUV_SUBSCRIPT_HASH;
+       }
+       else {
+           index = S_find_array_subscript(aTHX_ (AV*)sv, uninit_sv);
+           if (index >= 0)
+               subscript_type = FUV_SUBSCRIPT_ARRAY;
+       }
+
+       if (match && subscript_type == FUV_SUBSCRIPT_WITHIN)
+           break;
+
+       return varname(gv, hash ? '%' : '@', obase->op_targ,
+                                   keysv, index, subscript_type);
+      }
+
+    case OP_PADSV:
+       if (match && PAD_SVl(obase->op_targ) != uninit_sv)
+           break;
+       return varname(Nullgv, '$', obase->op_targ,
+                                   Nullsv, 0, FUV_SUBSCRIPT_NONE);
+
+    case OP_GVSV:
+       gv = cGVOPx_gv(obase);
+       if (!gv || (match && GvSV(gv) != uninit_sv))
+           break;
+       return varname(gv, '$', 0, Nullsv, 0, FUV_SUBSCRIPT_NONE);
+
+    case OP_AELEMFAST:
+       if (obase->op_flags & OPf_SPECIAL) { /* lexical array */
+           if (match) {
+               SV **svp;
+               av = (AV*)PAD_SV(obase->op_targ);
+               if (!av || SvRMAGICAL(av))
+                   break;
+               svp = av_fetch(av, (I32)obase->op_private, FALSE);
+               if (!svp || *svp != uninit_sv)
+                   break;
+           }
+           return varname(Nullgv, '$', obase->op_targ,
+                   Nullsv, (I32)obase->op_private, FUV_SUBSCRIPT_ARRAY);
+       }
+       else {
+           gv = cGVOPx_gv(obase);
+           if (!gv)
+               break;
+           if (match) {
+               SV **svp;
+               av = GvAV(gv);
+               if (!av || SvRMAGICAL(av))
+                   break;
+               svp = av_fetch(av, (I32)obase->op_private, FALSE);
+               if (!svp || *svp != uninit_sv)
+                   break;
+           }
+           return varname(gv, '$', 0,
+                   Nullsv, (I32)obase->op_private, FUV_SUBSCRIPT_ARRAY);
+       }
+       break;
+
+    case OP_EXISTS:
+       o = cUNOPx(obase)->op_first;
+       if (!o || o->op_type != OP_NULL ||
+               ! (o->op_targ == OP_AELEM || o->op_targ == OP_HELEM))
+           break;
+       return find_uninit_var(cBINOPo->op_last, uninit_sv, match);
+
+    case OP_AELEM:
+    case OP_HELEM:
+       if (PL_op == obase)
+           /* $a[uninit_expr] or $h{uninit_expr} */
+           return find_uninit_var(cBINOPx(obase)->op_last, uninit_sv, match);
+
+       gv = Nullgv;
+       o = cBINOPx(obase)->op_first;
+       kid = cBINOPx(obase)->op_last;
+
+       /* get the av or hv, and optionally the gv */
+       sv = Nullsv;
+       if  (o->op_type == OP_PADAV || o->op_type == OP_PADHV) {
+           sv = PAD_SV(o->op_targ);
+       }
+       else if ((o->op_type == OP_RV2AV || o->op_type == OP_RV2HV)
+               && cUNOPo->op_first->op_type == OP_GV)
+       {
+           gv = cGVOPx_gv(cUNOPo->op_first);
+           if (!gv)
+               break;
+           sv = o->op_type == OP_RV2HV ? (SV*)GvHV(gv) : (SV*)GvAV(gv);
+       }
+       if (!sv)
+           break;
+
+       if (kid && kid->op_type == OP_CONST && SvOK(cSVOPx_sv(kid))) {
+           /* index is constant */
+           if (match) {
+               if (SvMAGICAL(sv))
+                   break;
+               if (obase->op_type == OP_HELEM) {
+                   HE* he = hv_fetch_ent((HV*)sv, cSVOPx_sv(kid), 0, 0);
+                   if (!he || HeVAL(he) != uninit_sv)
+                       break;
+               }
+               else {
+                   SV ** const svp = av_fetch((AV*)sv, SvIV(cSVOPx_sv(kid)), FALSE);
+                   if (!svp || *svp != uninit_sv)
+                       break;
+               }
+           }
+           if (obase->op_type == OP_HELEM)
+               return varname(gv, '%', o->op_targ,
+                           cSVOPx_sv(kid), 0, FUV_SUBSCRIPT_HASH);
+           else
+               return varname(gv, '@', o->op_targ, Nullsv,
+                           SvIV(cSVOPx_sv(kid)), FUV_SUBSCRIPT_ARRAY);
+           ;
+       }
+       else  {
+           /* index is an expression;
+            * attempt to find a match within the aggregate */
+           if (obase->op_type == OP_HELEM) {
+               SV * const keysv = S_find_hash_subscript(aTHX_ (HV*)sv, uninit_sv);
+               if (keysv)
+                   return varname(gv, '%', o->op_targ,
+                                               keysv, 0, FUV_SUBSCRIPT_HASH);
+           }
+           else {
+               const I32 index = S_find_array_subscript(aTHX_ (AV*)sv, uninit_sv);
+               if (index >= 0)
+                   return varname(gv, '@', o->op_targ,
+                                       Nullsv, index, FUV_SUBSCRIPT_ARRAY);
+           }
+           if (match)
+               break;
+           return varname(gv,
+               (o->op_type == OP_PADAV || o->op_type == OP_RV2AV)
+               ? '@' : '%',
+               o->op_targ, Nullsv, 0, FUV_SUBSCRIPT_WITHIN);
+       }
 
-    SvREFCNT_dec(param->stashes);
+       break;
 
-    /* orphaned? eg threads->new inside BEGIN or use */
-    if (PL_compcv && ! SvREFCNT(PL_compcv)) {
-       (void)SvREFCNT_inc(PL_compcv);
-       SAVEFREESV(PL_compcv);
-    }
+    case OP_AASSIGN:
+       /* only examine RHS */
+       return find_uninit_var(cBINOPx(obase)->op_first, uninit_sv, match);
 
-    return my_perl;
-}
+    case OP_OPEN:
+       o = cUNOPx(obase)->op_first;
+       if (o->op_type == OP_PUSHMARK)
+           o = o->op_sibling;
 
-#endif /* USE_ITHREADS */
+       if (!o->op_sibling) {
+           /* one-arg version of open is highly magical */
 
-/*
-=head1 Unicode Support
+           if (o->op_type == OP_GV) { /* open FOO; */
+               gv = cGVOPx_gv(o);
+               if (match && GvSV(gv) != uninit_sv)
+                   break;
+               return varname(gv, '$', 0,
+                           Nullsv, 0, FUV_SUBSCRIPT_NONE);
+           }
+           /* other possibilities not handled are:
+            * open $x; or open my $x;  should return '${*$x}'
+            * open expr;               should return '$'.expr ideally
+            */
+            break;
+       }
+       goto do_op;
 
-=for apidoc sv_recode_to_utf8
+    /* ops where $_ may be an implicit arg */
+    case OP_TRANS:
+    case OP_SUBST:
+    case OP_MATCH:
+       if ( !(obase->op_flags & OPf_STACKED)) {
+           if (uninit_sv == ((obase->op_private & OPpTARGET_MY)
+                                ? PAD_SVl(obase->op_targ)
+                                : DEFSV))
+           {
+               sv = sv_newmortal();
+               sv_setpvn(sv, "$_", 2);
+               return sv;
+           }
+       }
+       goto do_op;
 
-The encoding is assumed to be an Encode object, on entry the PV
-of the sv is assumed to be octets in that encoding, and the sv
-will be converted into Unicode (and UTF-8).
+    case OP_PRTF:
+    case OP_PRINT:
+       /* skip filehandle as it can't produce 'undef' warning  */
+       o = cUNOPx(obase)->op_first;
+       if ((obase->op_flags & OPf_STACKED) && o->op_type == OP_PUSHMARK)
+           o = o->op_sibling->op_sibling;
+       goto do_op2;
 
-If the sv already is UTF-8 (or if it is not POK), or if the encoding
-is not a reference, nothing is done to the sv.  If the encoding is not
-an C<Encode::XS> Encoding object, bad things will happen.
-(See F<lib/encoding.pm> and L<Encode>).
 
-The PV of the sv is returned.
+    case OP_RV2SV:
+    case OP_CUSTOM:
+    case OP_ENTERSUB:
+       match = 1; /* XS or custom code could trigger random warnings */
+       goto do_op;
 
-=cut */
+    case OP_SCHOMP:
+    case OP_CHOMP:
+       if (SvROK(PL_rs) && uninit_sv == SvRV(PL_rs))
+           return sv_2mortal(newSVpvn("${$/}", 5));
+       /* FALL THROUGH */
 
-char *
-Perl_sv_recode_to_utf8(pTHX_ SV *sv, SV *encoding)
-{
-    dVAR;
-    if (SvPOK(sv) && !SvUTF8(sv) && !IN_BYTES && SvROK(encoding)) {
-       SV *uni;
-       STRLEN len;
-       const char *s;
-       dSP;
-       ENTER;
-       SAVETMPS;
-       save_re_context();
-       PUSHMARK(sp);
-       EXTEND(SP, 3);
-       XPUSHs(encoding);
-       XPUSHs(sv);
-/*
-  NI-S 2002/07/09
-  Passing sv_yes is wrong - it needs to be or'ed set of constants
-  for Encode::XS, while UTf-8 decode (currently) assumes a true value means
-  remove converted chars from source.
+    default:
+    do_op:
+       if (!(obase->op_flags & OPf_KIDS))
+           break;
+       o = cUNOPx(obase)->op_first;
+       
+    do_op2:
+       if (!o)
+           break;
 
-  Both will default the value - let them.
+       /* if all except one arg are constant, or have no side-effects,
+        * or are optimized away, then it's unambiguous */
+       o2 = Nullop;
+       for (kid=o; kid; kid = kid->op_sibling) {
+           if (kid &&
+               (    (kid->op_type == OP_CONST && SvOK(cSVOPx_sv(kid)))
+                 || (kid->op_type == OP_NULL  && ! (kid->op_flags & OPf_KIDS))
+                 || (kid->op_type == OP_PUSHMARK)
+               )
+           )
+               continue;
+           if (o2) { /* more than one found */
+               o2 = Nullop;
+               break;
+           }
+           o2 = kid;
+       }
+       if (o2)
+           return find_uninit_var(o2, uninit_sv, match);
 
-       XPUSHs(&PL_sv_yes);
-*/
-       PUTBACK;
-       call_method("decode", G_SCALAR);
-       SPAGAIN;
-       uni = POPs;
-       PUTBACK;
-       s = SvPV_const(uni, len);
-       if (s != SvPVX_const(sv)) {
-           SvGROW(sv, len + 1);
-           Move(s, SvPVX(sv), len + 1, char);
-           SvCUR_set(sv, len);
+       /* scan all args */
+       while (o) {
+           sv = find_uninit_var(o, uninit_sv, 1);
+           if (sv)
+               return sv;
+           o = o->op_sibling;
        }
-       FREETMPS;
-       LEAVE;
-       SvUTF8_on(sv);
-       return SvPVX(sv);
+       break;
     }
-    return SvPOKp(sv) ? SvPVX(sv) : NULL;
+    return Nullsv;
 }
 
-/*
-=for apidoc sv_cat_decode
 
-The encoding is assumed to be an Encode object, the PV of the ssv is
-assumed to be octets in that encoding and decoding the input starts
-from the position which (PV + *offset) pointed to.  The dsv will be
-concatenated the decoded UTF-8 string from ssv.  Decoding will terminate
-when the string tstr appears in decoding output or the input ends on
-the PV of the ssv. The value which the offset points will be modified
-to the last input position on the ssv.
+/*
+=for apidoc report_uninit
 
-Returns TRUE if the terminator was found, else returns FALSE.
+Print appropriate "Use of uninitialized variable" warning
 
-=cut */
+=cut
+*/
 
-bool
-Perl_sv_cat_decode(pTHX_ SV *dsv, SV *encoding,
-                  SV *ssv, int *offset, char *tstr, int tlen)
+void
+Perl_report_uninit(pTHX_ SV* uninit_sv)
 {
-    dVAR;
-    bool ret = FALSE;
-    if (SvPOK(ssv) && SvPOK(dsv) && SvROK(encoding) && offset) {
-       SV *offsv;
-       dSP;
-       ENTER;
-       SAVETMPS;
-       save_re_context();
-       PUSHMARK(sp);
-       EXTEND(SP, 6);
-       XPUSHs(encoding);
-       XPUSHs(dsv);
-       XPUSHs(ssv);
-       XPUSHs(offsv = sv_2mortal(newSViv(*offset)));
-       XPUSHs(sv_2mortal(newSVpvn(tstr, tlen)));
-       PUTBACK;
-       call_method("cat_decode", G_SCALAR);
-       SPAGAIN;
-       ret = SvTRUE(TOPs);
-       *offset = SvIV(offsv);
-       PUTBACK;
-       FREETMPS;
-       LEAVE;
+    if (PL_op) {
+       SV* varname = Nullsv;
+       if (uninit_sv) {
+           varname = find_uninit_var(PL_op, uninit_sv,0);
+           if (varname)
+               sv_insert(varname, 0, 0, " ", 1);
+       }
+       Perl_warner(aTHX_ packWARN(WARN_UNINITIALIZED), PL_warn_uninit,
+               varname ? SvPV_nolen_const(varname) : "",
+               " in ", OP_DESC(PL_op));
     }
     else
-        Perl_croak(aTHX_ "Invalid argument to sv_cat_decode");
-    return ret;
+       Perl_warner(aTHX_ packWARN(WARN_UNINITIALIZED), PL_warn_uninit,
+                   "", "", "");
 }
 
 /*