[patch] perl.gprof control
[p5sagit/p5-mst-13.2.git] / sv.c
diff --git a/sv.c b/sv.c
index a89c49a..3ae98ef 100644 (file)
--- a/sv.c
+++ b/sv.c
@@ -1,6 +1,6 @@
 /*    sv.c
  *
- *    Copyright (c) 1991-2000, Larry Wall
+ *    Copyright (c) 1991-2001, Larry Wall
  *
  *    You may distribute under the terms of either the GNU General Public
  *    License or the Artistic License, as specified in the README file.
@@ -63,7 +63,7 @@ static void do_clean_all(pTHXo_ SV *sv);
 #define del_SV(p) \
     STMT_START {                                       \
        LOCK_SV_MUTEX;                                  \
-       if (PL_debug & 32768)                           \
+       if (DEBUG_D_TEST)                               \
            del_sv(p);                                  \
        else                                            \
            plant_SV(p);                                \
@@ -73,7 +73,7 @@ static void do_clean_all(pTHXo_ SV *sv);
 STATIC void
 S_del_sv(pTHX_ SV *p)
 {
-    if (PL_debug & 32768) {
+    if (DEBUG_D_TEST) {
        SV* sva;
        SV* sv;
        SV* svend;
@@ -137,6 +137,7 @@ S_more_sv(pTHX)
     if (PL_nice_chunk) {
        sv_add_arena(PL_nice_chunk, PL_nice_chunk_size, 0);
        PL_nice_chunk = Nullch;
+        PL_nice_chunk_size = 0;
     }
     else {
        char *chunk;                /* must use New here to match call to */
@@ -147,20 +148,24 @@ S_more_sv(pTHX)
     return sv;
 }
 
-STATIC void
+STATIC I32
 S_visit(pTHX_ SVFUNC_t f)
 {
     SV* sva;
     SV* sv;
     register SV* svend;
+    I32 visited = 0;
 
     for (sva = PL_sv_arenaroot; sva; sva = (SV*)SvANY(sva)) {
        svend = &sva[SvREFCNT(sva)];
        for (sv = sva + 1; sv < svend; ++sv) {
-           if (SvTYPE(sv) != SVTYPEMASK)
+           if (SvTYPE(sv) != SVTYPEMASK && SvREFCNT(sv)) {
                (FCALL)(aTHXo_ sv);
+               ++visited;
+           }
        }
     }
+    return visited;
 }
 
 void
@@ -181,12 +186,14 @@ Perl_sv_clean_objs(pTHX)
     PL_in_clean_objs = FALSE;
 }
 
-void
+I32
 Perl_sv_clean_all(pTHX)
 {
+    I32 cleaned;
     PL_in_clean_all = TRUE;
-    visit(do_clean_all);
+    cleaned = visit(do_clean_all);
     PL_in_clean_all = FALSE;
+    return cleaned;
 }
 
 void
@@ -913,6 +920,10 @@ Perl_sv_upgrade(pTHX_ register SV *sv, U32 mt)
     MAGIC*     magic;
     HV*                stash;
 
+    if (mt != SVt_PV && SvREADONLY(sv) && SvFAKE(sv)) {
+       sv_force_normal(sv);
+    }
+
     if (SvTYPE(sv) == mt)
        return TRUE;
 
@@ -1281,11 +1292,8 @@ Perl_sv_setiv(pTHX_ register SV *sv, IV i)
     case SVt_PVCV:
     case SVt_PVFM:
     case SVt_PVIO:
-       {
-           dTHR;
-           Perl_croak(aTHX_ "Can't coerce %s to integer in %s", sv_reftype(sv,0),
-                 PL_op_desc[PL_op->op_type]);
-       }
+       Perl_croak(aTHX_ "Can't coerce %s to integer in %s", sv_reftype(sv,0),
+                  PL_op_desc[PL_op->op_type]);
     }
     (void)SvIOK_only(sv);                      /* validate number */
     SvIVX(sv) = i;
@@ -1319,6 +1327,18 @@ See C<sv_setuv_mg>.
 void
 Perl_sv_setuv(pTHX_ register SV *sv, UV u)
 {
+    /* With these two if statements:
+       u=1.49  s=0.52  cu=72.49  cs=10.64  scripts=270  tests=20865
+
+       without
+       u=1.35  s=0.47  cu=73.45  cs=11.43  scripts=270  tests=20865
+
+       If you wish to remove them, please benchmark to see what the effect is
+    */
+    if (u <= (UV)IV_MAX) {
+       sv_setiv(sv, (IV)u);
+       return;
+    }
     sv_setiv(sv, 0);
     SvIsUV_on(sv);
     SvUVX(sv) = u;
@@ -1335,7 +1355,21 @@ Like C<sv_setuv>, but also handles 'set' magic.
 void
 Perl_sv_setuv_mg(pTHX_ register SV *sv, UV u)
 {
-    sv_setuv(sv,u);
+    /* With these two if statements:
+       u=1.49  s=0.52  cu=72.49  cs=10.64  scripts=270  tests=20865
+
+       without
+       u=1.35  s=0.47  cu=73.45  cs=11.43  scripts=270  tests=20865
+
+       If you wish to remove them, please benchmark to see what the effect is
+    */
+    if (u <= (UV)IV_MAX) {
+       sv_setiv(sv, (IV)u);
+    } else {
+       sv_setiv(sv, 0);
+       SvIsUV_on(sv);
+       sv_setuv(sv,u);
+    }
     SvSETMAGIC(sv);
 }
 
@@ -1369,11 +1403,8 @@ Perl_sv_setnv(pTHX_ register SV *sv, NV num)
     case SVt_PVCV:
     case SVt_PVFM:
     case SVt_PVIO:
-       {
-           dTHR;
-           Perl_croak(aTHX_ "Can't coerce %s to number in %s", sv_reftype(sv,0),
-                 PL_op_name[PL_op->op_type]);
-       }
+       Perl_croak(aTHX_ "Can't coerce %s to number in %s", sv_reftype(sv,0),
+                  PL_op_name[PL_op->op_type]);
     }
     SvNVX(sv) = num;
     (void)SvNOK_only(sv);                      /* validate number */
@@ -1398,15 +1429,14 @@ Perl_sv_setnv_mg(pTHX_ register SV *sv, NV num)
 STATIC void
 S_not_a_number(pTHX_ SV *sv)
 {
-    dTHR;
     char tmpbuf[64];
     char *d = tmpbuf;
-    char *s;
     char *limit = tmpbuf + sizeof(tmpbuf) - 8;
                   /* each *s can expand to 4 chars + "...\0",
                      i.e. need room for 8 chars */
 
-    for (s = SvPVX(sv); *s && d < limit; s++) {
+    char *s, *end;
+    for (s = SvPVX(sv), end = s + SvCUR(sv); s < end && d < limit; s++) {
        int ch = *s & 0xFF;
        if (ch & 128 && !isPRINT_LC(ch)) {
            *d++ = 'M';
@@ -1429,6 +1459,10 @@ S_not_a_number(pTHX_ SV *sv)
            *d++ = '\\';
            *d++ = '\\';
        }
+       else if (ch == '\0') {
+           *d++ = '\\';
+           *d++ = '0';
+       }
        else if (isPRINT_LC(ch))
            *d++ = ch;
        else {
@@ -1436,7 +1470,7 @@ S_not_a_number(pTHX_ SV *sv)
            *d++ = toCTRL(ch);
        }
     }
-    if (*s) {
+    if (s < end) {
        *d++ = '.';
        *d++ = '.';
        *d++ = '.';
@@ -1452,16 +1486,159 @@ S_not_a_number(pTHX_ SV *sv)
                    "Argument \"%s\" isn't numeric", tmpbuf);
 }
 
-/* the number can be converted to integer with atol() or atoll() */
-#define IS_NUMBER_TO_INT_BY_ATOL 0x01
-#define IS_NUMBER_TO_INT_BY_ATOF 0x02 /* atol() may be != atof() */
-#define IS_NUMBER_NOT_IV        0x04 /* (IV)atof() may be != atof() */
-#define IS_NUMBER_NEG           0x08 /* not good to cache UV */
-#define IS_NUMBER_INFINITY      0x10 /* this is big */
+/*
+=for apidoc looks_like_number
+
+Test if an the content of an SV looks like a number (or is a
+number). C<Inf> and C<Infinity> are treated as numbers (so will not
+issue a non-numeric warning), even if your atof() doesn't grok them.
+
+=cut
+*/
+
+I32
+Perl_looks_like_number(pTHX_ SV *sv)
+{
+    register char *sbegin;
+    STRLEN len;
+
+    if (SvPOK(sv)) {
+       sbegin = SvPVX(sv);
+       len = SvCUR(sv);
+    }
+    else if (SvPOKp(sv))
+       sbegin = SvPV(sv, len);
+    else
+       return 1; /* Historic.  Wrong?  */
+    return grok_number(sbegin, len, NULL);
+}
 
 /* Actually, ISO C leaves conversion of UV to IV undefined, but
    until proven guilty, assume that things are not that bad... */
 
+/* As 64 bit platforms often have an NV that doesn't preserve all bits of
+   an IV (an assumption perl has been based on to date) it becomes necessary
+   to remove the assumption that the NV always carries enough precision to
+   recreate the IV whenever needed, and that the NV is the canonical form.
+   Instead, IV/UV and NV need to be given equal rights. So as to not lose
+   precision as an side effect of conversion (which would lead to insanity
+   and the dragon(s) in t/op/numconvert.t getting very angry) the intent is
+   1) to distinguish between IV/UV/NV slots that have cached a valid
+      conversion where precision was lost and IV/UV/NV slots that have a
+      valid conversion which has lost no precision
+   2) to ensure that if a numeric conversion to one form is request that
+      would lose precision, the precise conversion (or differently
+      imprecise conversion) is also performed and cached, to prevent
+      requests for different numeric formats on the same SV causing
+      lossy conversion chains. (lossless conversion chains are perfectly
+      acceptable (still))
+
+
+   flags are used:
+   SvIOKp is true if the IV slot contains a valid value
+   SvIOK  is true only if the IV value is accurate (UV if SvIOK_UV true)
+   SvNOKp is true if the NV slot contains a valid value
+   SvNOK  is true only if the NV value is accurate
+
+   so
+   while converting from PV to NV check to see if converting that NV to an
+   IV(or UV) would lose accuracy over a direct conversion from PV to
+   IV(or UV). If it would, cache both conversions, return NV, but mark
+   SV as IOK NOKp (ie not NOK).
+
+   while converting from PV to IV check to see if converting that IV to an
+   NV would lose accuracy over a direct conversion from PV to NV. If it
+   would, cache both conversions, flag similarly.
+
+   Before, the SV value "3.2" could become NV=3.2 IV=3 NOK, IOK quite
+   correctly because if IV & NV were set NV *always* overruled.
+   Now, "3.2" will become NV=3.2 IV=3 NOK, IOKp, because the flags meaning
+   changes - now IV and NV together means that the two are interchangeable
+   SvIVX == (IV) SvNVX && SvNVX == (NV) SvIVX;
+
+   The benefit of this is operations such as pp_add know that if SvIOK is
+   true for both left and right operands, then integer addition can be
+   used instead of floating point. (for cases where the result won't
+   overflow) Before, floating point was always used, which could lead to
+   loss of precision compared with integer addition.
+
+   * making IV and NV equal status should make maths accurate on 64 bit
+     platforms
+   * may speed up maths somewhat if pp_add and friends start to use
+     integers when possible instead of fp. (hopefully the overhead in
+     looking for SvIOK and checking for overflow will not outweigh the
+     fp to integer speedup)
+   * will slow down integer operations (callers of SvIV) on "inaccurate"
+     values, as the change from SvIOK to SvIOKp will cause a call into
+     sv_2iv each time rather than a macro access direct to the IV slot
+   * should speed up number->string conversion on integers as IV is
+     favoured when IV and NV equally accurate
+
+   ####################################################################
+   You had better be using SvIOK_notUV if you want an IV for arithmetic
+   SvIOK is true if (IV or UV), so you might be getting (IV)SvUV
+   SvUOK is true iff UV.
+   ####################################################################
+
+   Your mileage will vary depending your CPUs relative fp to integer
+   performance ratio.
+*/
+
+#ifndef NV_PRESERVES_UV
+#define IS_NUMBER_UNDERFLOW_IV 1
+#define IS_NUMBER_UNDERFLOW_UV 2
+#define IS_NUMBER_IV_AND_UV 2
+#define IS_NUMBER_OVERFLOW_IV 4
+#define IS_NUMBER_OVERFLOW_UV 5
+
+/* For sv_2nv these three cases are "SvNOK and don't bother casting"  */
+STATIC int
+S_sv_2iuv_non_preserve (pTHX_ register SV *sv, I32 numtype)
+{
+    DEBUG_c(PerlIO_printf(Perl_debug_log,"sv_2iuv_non '%s', IV=0x%"UVxf" NV=%g inttype=%"UVXf"\n", SvPVX(sv), SvIVX(sv), SvNVX(sv), (UV)numtype));
+    if (SvNVX(sv) < (NV)IV_MIN) {
+       (void)SvIOKp_on(sv);
+       (void)SvNOK_on(sv);
+       SvIVX(sv) = IV_MIN;
+       return IS_NUMBER_UNDERFLOW_IV;
+    }
+    if (SvNVX(sv) > (NV)UV_MAX) {
+       (void)SvIOKp_on(sv);
+       (void)SvNOK_on(sv);
+       SvIsUV_on(sv);
+       SvUVX(sv) = UV_MAX;
+       return IS_NUMBER_OVERFLOW_UV;
+    }
+    (void)SvIOKp_on(sv);
+    (void)SvNOK_on(sv);
+    /* Can't use strtol etc to convert this string.  (See truth table in
+       sv_2iv  */
+    if (SvNVX(sv) <= (UV)IV_MAX) {
+        SvIVX(sv) = I_V(SvNVX(sv));
+        if ((NV)(SvIVX(sv)) == SvNVX(sv)) {
+            SvIOK_on(sv); /* Integer is precise. NOK, IOK */
+        } else {
+            /* Integer is imprecise. NOK, IOKp */
+        }
+        return SvNVX(sv) < 0 ? IS_NUMBER_UNDERFLOW_UV : IS_NUMBER_IV_AND_UV;
+    }
+    SvIsUV_on(sv);
+    SvUVX(sv) = U_V(SvNVX(sv));
+    if ((NV)(SvUVX(sv)) == SvNVX(sv)) {
+        if (SvUVX(sv) == UV_MAX) {
+            /* As we know that NVs don't preserve UVs, UV_MAX cannot
+               possibly be preserved by NV. Hence, it must be overflow.
+               NOK, IOKp */
+            return IS_NUMBER_OVERFLOW_UV;
+        }
+        SvIOK_on(sv); /* Integer is precise. NOK, UOK */
+    } else {
+        /* Integer is imprecise. NOK, IOKp */
+    }
+    return IS_NUMBER_OVERFLOW_IV;
+}
+#endif /* NV_PRESERVES_UV*/
+
 IV
 Perl_sv_2iv(pTHX_ register SV *sv)
 {
@@ -1478,7 +1655,6 @@ Perl_sv_2iv(pTHX_ register SV *sv)
            return asIV(sv);
        if (!SvROK(sv)) {
            if (!(SvFLAGS(sv) & SVs_PADTMP)) {
-               dTHR;
                if (ckWARN(WARN_UNINITIALIZED) && !PL_localizing)
                    report_uninit();
            }
@@ -1488,12 +1664,15 @@ Perl_sv_2iv(pTHX_ register SV *sv)
     if (SvTHINKFIRST(sv)) {
        if (SvROK(sv)) {
          SV* tmpstr;
-         if (SvAMAGIC(sv) && (tmpstr=AMG_CALLun(sv, numer)))
+          if (SvAMAGIC(sv) && (tmpstr=AMG_CALLun(sv,numer)) &&
+                (SvTYPE(tmpstr) != SVt_RV || (SvRV(tmpstr) != SvRV(sv))))
              return SvIV(tmpstr);
          return PTR2IV(SvRV(sv));
        }
+       if (SvREADONLY(sv) && SvFAKE(sv)) {
+           sv_force_normal(sv);
+       }
        if (SvREADONLY(sv) && !SvOK(sv)) {
-           dTHR;
            if (ckWARN(WARN_UNINITIALIZED))
                report_uninit();
            return 0;
@@ -1508,19 +1687,71 @@ Perl_sv_2iv(pTHX_ register SV *sv)
        }
     }
     if (SvNOKp(sv)) {
-       /* We can cache the IV/UV value even if it not good enough
-        * to reconstruct NV, since the conversion to PV will prefer
-        * NV over IV/UV.
-        */
+       /* erm. not sure. *should* never get NOKp (without NOK) from sv_2nv
+        * without also getting a cached IV/UV from it at the same time
+        * (ie PV->NV conversion should detect loss of accuracy and cache
+        * IV or UV at same time to avoid this.  NWC */
 
        if (SvTYPE(sv) == SVt_NV)
            sv_upgrade(sv, SVt_PVNV);
 
-       (void)SvIOK_on(sv);
-       if (SvNVX(sv) < (NV)IV_MAX + 0.5)
+       (void)SvIOKp_on(sv);    /* Must do this first, to clear any SvOOK */
+       /* < not <= as for NV doesn't preserve UV, ((NV)IV_MAX+1) will almost
+          certainly cast into the IV range at IV_MAX, whereas the correct
+          answer is the UV IV_MAX +1. Hence < ensures that dodgy boundary
+          cases go to UV */
+       if (SvNVX(sv) < (NV)IV_MAX + 0.5) {
            SvIVX(sv) = I_V(SvNVX(sv));
+           if (SvNVX(sv) == (NV) SvIVX(sv)
+#ifndef NV_PRESERVES_UV
+               && (((UV)1 << NV_PRESERVES_UV_BITS) >
+                   (UV)(SvIVX(sv) > 0 ? SvIVX(sv) : -SvIVX(sv)))
+               /* Don't flag it as "accurately an integer" if the number
+                  came from a (by definition imprecise) NV operation, and
+                  we're outside the range of NV integer precision */
+#endif
+               ) {
+               SvIOK_on(sv);  /* Can this go wrong with rounding? NWC */
+               DEBUG_c(PerlIO_printf(Perl_debug_log,
+                                     "0x%"UVxf" iv(%g => %"IVdf") (precise)\n",
+                                     PTR2UV(sv),
+                                     SvNVX(sv),
+                                     SvIVX(sv)));
+
+           } else {
+               /* IV not precise.  No need to convert from PV, as NV
+                  conversion would already have cached IV if it detected
+                  that PV->IV would be better than PV->NV->IV
+                  flags already correct - don't set public IOK.  */
+               DEBUG_c(PerlIO_printf(Perl_debug_log,
+                                     "0x%"UVxf" iv(%g => %"IVdf") (imprecise)\n",
+                                     PTR2UV(sv),
+                                     SvNVX(sv),
+                                     SvIVX(sv)));
+           }
+           /* Can the above go wrong if SvIVX == IV_MIN and SvNVX < IV_MIN,
+              but the cast (NV)IV_MIN rounds to a the value less (more
+              negative) than IV_MIN which happens to be equal to SvNVX ??
+              Analogous to 0xFFFFFFFFFFFFFFFF rounding up to NV (2**64) and
+              NV rounding back to 0xFFFFFFFFFFFFFFFF, so UVX == UV(NVX) and
+              (NV)UVX == NVX are both true, but the values differ. :-(
+              Hopefully for 2s complement IV_MIN is something like
+              0x8000000000000000 which will be exact. NWC */
+       }
        else {
            SvUVX(sv) = U_V(SvNVX(sv));
+           if (
+               (SvNVX(sv) == (NV) SvUVX(sv))
+#ifndef  NV_PRESERVES_UV
+               /* Make sure it's not 0xFFFFFFFFFFFFFFFF */
+               /*&& (SvUVX(sv) != UV_MAX) irrelevant with code below */
+               && (((UV)1 << NV_PRESERVES_UV_BITS) > SvUVX(sv))
+               /* Don't flag it as "accurately an integer" if the number
+                  came from a (by definition imprecise) NV operation, and
+                  we're outside the range of NV integer precision */
+#endif
+               )
+               SvIOK_on(sv);
            SvIsUV_on(sv);
          ret_iv_max:
            DEBUG_c(PerlIO_printf(Perl_debug_log,
@@ -1532,55 +1763,158 @@ Perl_sv_2iv(pTHX_ register SV *sv)
        }
     }
     else if (SvPOKp(sv) && SvLEN(sv)) {
-       I32 numtype = looks_like_number(sv);
-
+       UV value;
+       int numtype = grok_number(SvPVX(sv), SvCUR(sv), &value);
        /* We want to avoid a possible problem when we cache an IV which
           may be later translated to an NV, and the resulting NV is not
-          the translation of the initial data.
+          the same as the direct translation of the initial string
+          (eg 123.456 can shortcut to the IV 123 with atol(), but we must
+          be careful to ensure that the value with the .456 is around if the
+          NV value is requested in the future).
        
           This means that if we cache such an IV, we need to cache the
           NV as well.  Moreover, we trade speed for space, and do not
-          cache the NV if not needed.
+          cache the NV if we are sure it's not needed.
         */
-       if (numtype & IS_NUMBER_NOT_IV) {
-           /* May be not an integer.  Need to cache NV if we cache IV
-            * - otherwise future conversion to NV will be wrong.  */
-           NV d;
-
-           d = Atof(SvPVX(sv));
 
-           if (SvTYPE(sv) < SVt_PVNV)
-               sv_upgrade(sv, SVt_PVNV);
-           SvNVX(sv) = d;
-           (void)SvNOK_on(sv);
+       /* SVt_PVNV is one higher than SVt_PVIV, hence this order  */
+       if ((numtype & (IS_NUMBER_IN_UV | IS_NUMBER_NOT_INT))
+            == IS_NUMBER_IN_UV) {
+           /* It's defintately an integer, only upgrade to PVIV */
+           if (SvTYPE(sv) < SVt_PVIV)
+               sv_upgrade(sv, SVt_PVIV);
            (void)SvIOK_on(sv);
+       } else if (SvTYPE(sv) < SVt_PVNV)
+           sv_upgrade(sv, SVt_PVNV);
+
+       /* If NV preserves UV then we only use the UV value if we know that
+          we aren't going to call atof() below. If NVs don't preserve UVs
+          then the value returned may have more precision than atof() will
+          return, even though value isn't perfectly accurate.  */
+       if ((numtype & (IS_NUMBER_IN_UV
+#ifdef NV_PRESERVES_UV
+                       | IS_NUMBER_NOT_INT
+#endif
+           )) == IS_NUMBER_IN_UV) {
+           /* This won't turn off the public IOK flag if it was set above  */
+           (void)SvIOKp_on(sv);
+
+           if (!(numtype & IS_NUMBER_NEG)) {
+               /* positive */;
+               if (value <= (UV)IV_MAX) {
+                   SvIVX(sv) = (IV)value;
+               } else {
+                   SvUVX(sv) = value;
+                   SvIsUV_on(sv);
+               }
+           } else {
+               /* 2s complement assumption  */
+               if (value <= (UV)IV_MIN) {
+                   SvIVX(sv) = -(IV)value;
+               } else {
+                   /* Too negative for an IV.  This is a double upgrade, but
+                      I'm assuming it will be be rare.  */
+                   if (SvTYPE(sv) < SVt_PVNV)
+                       sv_upgrade(sv, SVt_PVNV);
+                   SvNOK_on(sv);
+                   SvIOK_off(sv);
+                   SvIOKp_on(sv);
+                   SvNVX(sv) = -(NV)value;
+                   SvIVX(sv) = IV_MIN;
+               }
+           }
+       }
+       /* For !NV_PRESERVES_UV and IS_NUMBER_IN_UV and IS_NUMBER_NOT_INT we
+           will be in the previous block to set the IV slot, and the next
+           block to set the NV slot.  So no else here.  */
+       
+       if ((numtype & (IS_NUMBER_IN_UV | IS_NUMBER_NOT_INT))
+           != IS_NUMBER_IN_UV) {
+           /* It wasn't an (integer that doesn't overflow the UV). */
+           SvNVX(sv) = Atof(SvPVX(sv));
+
+           if (! numtype && ckWARN(WARN_NUMERIC))
+               not_a_number(sv);
+
 #if defined(USE_LONG_DOUBLE)
-           DEBUG_c(PerlIO_printf(Perl_debug_log, "0x%"UVxf" 2nv(%" PERL_PRIgldbl ")\n",
+           DEBUG_c(PerlIO_printf(Perl_debug_log, "0x%"UVxf" 2iv(%" PERL_PRIgldbl ")\n",
                                  PTR2UV(sv), SvNVX(sv)));
 #else
-           DEBUG_c(PerlIO_printf(Perl_debug_log, "0x%"UVxf" 2nv(%g)\n",
+           DEBUG_c(PerlIO_printf(Perl_debug_log, "0x%"UVxf" 2iv(%g)\n",
                                  PTR2UV(sv), SvNVX(sv)));
 #endif
-           if (SvNVX(sv) < (NV)IV_MAX + 0.5)
+
+
+#ifdef NV_PRESERVES_UV
+           (void)SvIOKp_on(sv);
+           (void)SvNOK_on(sv);
+           if (SvNVX(sv) < (NV)IV_MAX + 0.5) {
                SvIVX(sv) = I_V(SvNVX(sv));
-           else {
-               SvUVX(sv) = U_V(SvNVX(sv));
-               SvIsUV_on(sv);
+               if ((NV)(SvIVX(sv)) == SvNVX(sv)) {
+                   SvIOK_on(sv);
+               } else {
+                   /* Integer is imprecise. NOK, IOKp */
+               }
+               /* UV will not work better than IV */
+           } else {
+               if (SvNVX(sv) > (NV)UV_MAX) {
+                   SvIsUV_on(sv);
+                   /* Integer is inaccurate. NOK, IOKp, is UV */
+                   SvUVX(sv) = UV_MAX;
+                   SvIsUV_on(sv);
+               } else {
+                   SvUVX(sv) = U_V(SvNVX(sv));
+                   /* 0xFFFFFFFFFFFFFFFF not an issue in here */
+                   if ((NV)(SvUVX(sv)) == SvNVX(sv)) {
+                       SvIOK_on(sv);
+                       SvIsUV_on(sv);
+                   } else {
+                       /* Integer is imprecise. NOK, IOKp, is UV */
+                       SvIsUV_on(sv);
+                   }
+               }
                goto ret_iv_max;
            }
-       }
-       else {  /* The NV may be reconstructed from IV - safe to cache IV,
-                  which may be calculated by atol(). */
-           if (SvTYPE(sv) < SVt_PVIV)
-               sv_upgrade(sv, SVt_PVIV);
-           (void)SvIOK_on(sv);
-           SvIVX(sv) = Atol(SvPVX(sv));
-           if (! numtype && ckWARN(WARN_NUMERIC))
-               not_a_number(sv);
-       }
-    }
-    else  {
-       dTHR;
+#else /* NV_PRESERVES_UV */
+            if ((numtype & (IS_NUMBER_IN_UV | IS_NUMBER_NOT_INT))
+                == (IS_NUMBER_IN_UV | IS_NUMBER_NOT_INT)) {
+                /* The IV slot will have been set from value returned by
+                   grok_number above.  The NV slot has just been set using
+                   Atof.  */
+               SvNOK_on(sv);
+                assert (SvIOKp(sv));
+            } else {
+                if (((UV)1 << NV_PRESERVES_UV_BITS) >
+                    U_V(SvNVX(sv) > 0 ? SvNVX(sv) : -SvNVX(sv))) {
+                    /* Small enough to preserve all bits. */
+                    (void)SvIOKp_on(sv);
+                    SvNOK_on(sv);
+                    SvIVX(sv) = I_V(SvNVX(sv));
+                    if ((NV)(SvIVX(sv)) == SvNVX(sv))
+                        SvIOK_on(sv);
+                    /* Assumption: first non-preserved integer is < IV_MAX,
+                       this NV is in the preserved range, therefore: */
+                    if (!(U_V(SvNVX(sv) > 0 ? SvNVX(sv) : -SvNVX(sv))
+                          < (UV)IV_MAX)) {
+                        Perl_croak(aTHX_ "sv_2iv assumed (U_V(fabs(SvNVX(sv))) < (UV)IV_MAX) but SvNVX(sv)=%g U_V is 0x%"UVxf", IV_MAX is 0x%"UVxf"\n", SvNVX(sv), U_V(SvNVX(sv)), (UV)IV_MAX);
+                    }
+                } else {
+                    /* IN_UV NOT_INT
+                         0      0      already failed to read UV.
+                         0      1       already failed to read UV.
+                         1      0       you won't get here in this case. IV/UV
+                                       slot set, public IOK, Atof() unneeded.
+                         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;
+                }
+            }
+#endif /* NV_PRESERVES_UV */
+       }
+    } else  {
        if (ckWARN(WARN_UNINITIALIZED) && !PL_localizing && !(SvFLAGS(sv) & SVs_PADTMP))
            report_uninit();
        if (SvTYPE(sv) < SVt_IV)
@@ -1608,7 +1942,6 @@ Perl_sv_2uv(pTHX_ register SV *sv)
            return asUV(sv);
        if (!SvROK(sv)) {
            if (!(SvFLAGS(sv) & SVs_PADTMP)) {
-               dTHR;
                if (ckWARN(WARN_UNINITIALIZED) && !PL_localizing)
                    report_uninit();
            }
@@ -1618,12 +1951,15 @@ Perl_sv_2uv(pTHX_ register SV *sv)
     if (SvTHINKFIRST(sv)) {
        if (SvROK(sv)) {
          SV* tmpstr;
-         if (SvAMAGIC(sv) && (tmpstr=AMG_CALLun(sv, numer)))
+          if (SvAMAGIC(sv) && (tmpstr=AMG_CALLun(sv,numer)) &&
+                (SvTYPE(tmpstr) != SVt_RV || (SvRV(tmpstr) != SvRV(sv))))
              return SvUV(tmpstr);
          return PTR2UV(SvRV(sv));
        }
+       if (SvREADONLY(sv) && SvFAKE(sv)) {
+           sv_force_normal(sv);
+       }
        if (SvREADONLY(sv) && !SvOK(sv)) {
-           dTHR;
            if (ckWARN(WARN_UNINITIALIZED))
                report_uninit();
            return 0;
@@ -1638,30 +1974,79 @@ Perl_sv_2uv(pTHX_ register SV *sv)
        }
     }
     if (SvNOKp(sv)) {
-       /* We can cache the IV/UV value even if it not good enough
-        * to reconstruct NV, since the conversion to PV will prefer
-        * NV over IV/UV.
-        */
+       /* erm. not sure. *should* never get NOKp (without NOK) from sv_2nv
+        * without also getting a cached IV/UV from it at the same time
+        * (ie PV->NV conversion should detect loss of accuracy and cache
+        * IV or UV at same time to avoid this. */
+       /* IV-over-UV optimisation - choose to cache IV if possible */
+
        if (SvTYPE(sv) == SVt_NV)
            sv_upgrade(sv, SVt_PVNV);
-       (void)SvIOK_on(sv);
-       if (SvNVX(sv) >= -0.5) {
-           SvIsUV_on(sv);
-           SvUVX(sv) = U_V(SvNVX(sv));
+
+       (void)SvIOKp_on(sv);    /* Must do this first, to clear any SvOOK */
+       if (SvNVX(sv) < (NV)IV_MAX + 0.5) {
+           SvIVX(sv) = I_V(SvNVX(sv));
+           if (SvNVX(sv) == (NV) SvIVX(sv)
+#ifndef NV_PRESERVES_UV
+               && (((UV)1 << NV_PRESERVES_UV_BITS) >
+                   (UV)(SvIVX(sv) > 0 ? SvIVX(sv) : -SvIVX(sv)))
+               /* Don't flag it as "accurately an integer" if the number
+                  came from a (by definition imprecise) NV operation, and
+                  we're outside the range of NV integer precision */
+#endif
+               ) {
+               SvIOK_on(sv);  /* Can this go wrong with rounding? NWC */
+               DEBUG_c(PerlIO_printf(Perl_debug_log,
+                                     "0x%"UVxf" uv(%g => %"IVdf") (precise)\n",
+                                     PTR2UV(sv),
+                                     SvNVX(sv),
+                                     SvIVX(sv)));
+
+           } else {
+               /* IV not precise.  No need to convert from PV, as NV
+                  conversion would already have cached IV if it detected
+                  that PV->IV would be better than PV->NV->IV
+                  flags already correct - don't set public IOK.  */
+               DEBUG_c(PerlIO_printf(Perl_debug_log,
+                                     "0x%"UVxf" uv(%g => %"IVdf") (imprecise)\n",
+                                     PTR2UV(sv),
+                                     SvNVX(sv),
+                                     SvIVX(sv)));
+           }
+           /* Can the above go wrong if SvIVX == IV_MIN and SvNVX < IV_MIN,
+              but the cast (NV)IV_MIN rounds to a the value less (more
+              negative) than IV_MIN which happens to be equal to SvNVX ??
+              Analogous to 0xFFFFFFFFFFFFFFFF rounding up to NV (2**64) and
+              NV rounding back to 0xFFFFFFFFFFFFFFFF, so UVX == UV(NVX) and
+              (NV)UVX == NVX are both true, but the values differ. :-(
+              Hopefully for 2s complement IV_MIN is something like
+              0x8000000000000000 which will be exact. NWC */
        }
        else {
-           SvIVX(sv) = I_V(SvNVX(sv));
-         ret_zero:
+           SvUVX(sv) = U_V(SvNVX(sv));
+           if (
+               (SvNVX(sv) == (NV) SvUVX(sv))
+#ifndef  NV_PRESERVES_UV
+               /* Make sure it's not 0xFFFFFFFFFFFFFFFF */
+               /*&& (SvUVX(sv) != UV_MAX) irrelevant with code below */
+               && (((UV)1 << NV_PRESERVES_UV_BITS) > SvUVX(sv))
+               /* Don't flag it as "accurately an integer" if the number
+                  came from a (by definition imprecise) NV operation, and
+                  we're outside the range of NV integer precision */
+#endif
+               )
+               SvIOK_on(sv);
+           SvIsUV_on(sv);
            DEBUG_c(PerlIO_printf(Perl_debug_log,
-                                 "0x%"UVxf" 2uv(%"IVdf" => %"IVdf") (as signed)\n",
+                                 "0x%"UVxf" 2uv(%"UVuf" => %"IVdf") (as unsigned)\n",
                                  PTR2UV(sv),
-                                 SvIVX(sv),
-                                 (IV)(UV)SvIVX(sv)));
-           return (UV)SvIVX(sv);
+                                 SvUVX(sv),
+                                 SvUVX(sv)));
        }
     }
     else if (SvPOKp(sv) && SvLEN(sv)) {
-       I32 numtype = looks_like_number(sv);
+       UV value;
+       int numtype = grok_number(SvPVX(sv), SvCUR(sv), &value);
 
        /* We want to avoid a possible problem when we cache a UV which
           may be later translated to an NV, and the resulting NV is not
@@ -1671,73 +2056,133 @@ Perl_sv_2uv(pTHX_ register SV *sv)
           NV as well.  Moreover, we trade speed for space, and do not
           cache the NV if not needed.
         */
-       if (numtype & IS_NUMBER_NOT_IV) {
-           /* May be not an integer.  Need to cache NV if we cache IV
-            * - otherwise future conversion to NV will be wrong.  */
-           NV d;
 
-           d = Atof(SvPVX(sv));
-
-           if (SvTYPE(sv) < SVt_PVNV)
-               sv_upgrade(sv, SVt_PVNV);
-           SvNVX(sv) = d;
-           (void)SvNOK_on(sv);
+       /* SVt_PVNV is one higher than SVt_PVIV, hence this order  */
+       if ((numtype & (IS_NUMBER_IN_UV | IS_NUMBER_NOT_INT))
+            == IS_NUMBER_IN_UV) {
+           /* It's defintately an integer, only upgrade to PVIV */
+           if (SvTYPE(sv) < SVt_PVIV)
+               sv_upgrade(sv, SVt_PVIV);
            (void)SvIOK_on(sv);
-#if defined(USE_LONG_DOUBLE)
-           DEBUG_c(PerlIO_printf(Perl_debug_log,
-                                 "0x%"UVxf" 2nv(%" PERL_PRIgldbl ")\n",
-                                 PTR2UV(sv), SvNVX(sv)));
-#else
-           DEBUG_c(PerlIO_printf(Perl_debug_log,
-                                 "0x%"UVxf" 2nv(%g)\n",
-                                 PTR2UV(sv), SvNVX(sv)));
+       } else if (SvTYPE(sv) < SVt_PVNV)
+           sv_upgrade(sv, SVt_PVNV);
+
+       /* If NV preserves UV then we only use the UV value if we know that
+          we aren't going to call atof() below. If NVs don't preserve UVs
+          then the value returned may have more precision than atof() will
+          return, even though it isn't accurate.  */
+       if ((numtype & (IS_NUMBER_IN_UV
+#ifdef NV_PRESERVES_UV
+                       | IS_NUMBER_NOT_INT
 #endif
-           if (SvNVX(sv) < -0.5) {
-               SvIVX(sv) = I_V(SvNVX(sv));
-               goto ret_zero;
+           )) == IS_NUMBER_IN_UV) {
+           /* This won't turn off the public IOK flag if it was set above  */
+           (void)SvIOKp_on(sv);
+
+           if (!(numtype & IS_NUMBER_NEG)) {
+               /* positive */;
+               if (value <= (UV)IV_MAX) {
+                   SvIVX(sv) = (IV)value;
+               } else {
+                   /* it didn't overflow, and it was positive. */
+                   SvUVX(sv) = value;
+                   SvIsUV_on(sv);
+               }
            } else {
-               SvUVX(sv) = U_V(SvNVX(sv));
-               SvIsUV_on(sv);
+               /* 2s complement assumption  */
+               if (value <= (UV)IV_MIN) {
+                   SvIVX(sv) = -(IV)value;
+               } else {
+                   /* Too negative for an IV.  This is a double upgrade, but
+                      I'm assuming it will be be rare.  */
+                   if (SvTYPE(sv) < SVt_PVNV)
+                       sv_upgrade(sv, SVt_PVNV);
+                   SvNOK_on(sv);
+                   SvIOK_off(sv);
+                   SvIOKp_on(sv);
+                   SvNVX(sv) = -(NV)value;
+                   SvIVX(sv) = IV_MIN;
+               }
            }
        }
-       else if (numtype & IS_NUMBER_NEG) {
-           /* The NV may be reconstructed from IV - safe to cache IV,
-              which may be calculated by atol(). */
-           if (SvTYPE(sv) == SVt_PV)
-               sv_upgrade(sv, SVt_PVIV);
-           (void)SvIOK_on(sv);
-           SvIVX(sv) = (IV)Atol(SvPVX(sv));
-       }
-       else if (numtype) {             /* Non-negative */
-           /* The NV may be reconstructed from UV - safe to cache UV,
-              which may be calculated by strtoul()/atol. */
-           if (SvTYPE(sv) == SVt_PV)
-               sv_upgrade(sv, SVt_PVIV);
-           (void)SvIOK_on(sv);
-           (void)SvIsUV_on(sv);
-#ifdef HAS_STRTOUL
-           SvUVX(sv) = Strtoul(SvPVX(sv), Null(char**), 10);
-#else                  /* no atou(), but we know the number fits into IV... */
-                       /* The only problem may be if it is negative... */
-           SvUVX(sv) = (UV)Atol(SvPVX(sv));
+       
+       if ((numtype & (IS_NUMBER_IN_UV | IS_NUMBER_NOT_INT))
+           != IS_NUMBER_IN_UV) {
+           /* It wasn't an integer, or it overflowed the UV. */
+           SvNVX(sv) = Atof(SvPVX(sv));
+
+            if (! numtype && ckWARN(WARN_NUMERIC))
+                   not_a_number(sv);
+
+#if defined(USE_LONG_DOUBLE)
+            DEBUG_c(PerlIO_printf(Perl_debug_log, "0x%"UVxf" 2uv(%" PERL_PRIgldbl ")\n",
+                                  PTR2UV(sv), SvNVX(sv)));
+#else
+            DEBUG_c(PerlIO_printf(Perl_debug_log, "0x%"UVxf" 2uv(%g)\n",
+                                  PTR2UV(sv), SvNVX(sv)));
 #endif
-       }
-       else {                          /* Not a number.  Cache 0. */
-           dTHR;
 
-           if (SvTYPE(sv) < SVt_PVIV)
-               sv_upgrade(sv, SVt_PVIV);
-           (void)SvIOK_on(sv);
-           (void)SvIsUV_on(sv);
-           SvUVX(sv) = 0;              /* We assume that 0s have the
-                                          same bitmap in IV and UV. */
-           if (ckWARN(WARN_NUMERIC))
-               not_a_number(sv);
+#ifdef NV_PRESERVES_UV
+            (void)SvIOKp_on(sv);
+            (void)SvNOK_on(sv);
+            if (SvNVX(sv) < (NV)IV_MAX + 0.5) {
+                SvIVX(sv) = I_V(SvNVX(sv));
+                if ((NV)(SvIVX(sv)) == SvNVX(sv)) {
+                    SvIOK_on(sv);
+                } else {
+                    /* Integer is imprecise. NOK, IOKp */
+                }
+                /* UV will not work better than IV */
+            } else {
+                if (SvNVX(sv) > (NV)UV_MAX) {
+                    SvIsUV_on(sv);
+                    /* Integer is inaccurate. NOK, IOKp, is UV */
+                    SvUVX(sv) = UV_MAX;
+                    SvIsUV_on(sv);
+                } else {
+                    SvUVX(sv) = U_V(SvNVX(sv));
+                    /* 0xFFFFFFFFFFFFFFFF not an issue in here, NVs
+                       NV preservse UV so can do correct comparison.  */
+                    if ((NV)(SvUVX(sv)) == SvNVX(sv)) {
+                        SvIOK_on(sv);
+                        SvIsUV_on(sv);
+                    } else {
+                        /* Integer is imprecise. NOK, IOKp, is UV */
+                        SvIsUV_on(sv);
+                    }
+                }
+            }
+#else /* NV_PRESERVES_UV */
+            if ((numtype & (IS_NUMBER_IN_UV | IS_NUMBER_NOT_INT))
+                == (IS_NUMBER_IN_UV | IS_NUMBER_NOT_INT)) {
+                /* The UV slot will have been set from value returned by
+                   grok_number above.  The NV slot has just been set using
+                   Atof.  */
+               SvNOK_on(sv);
+                assert (SvIOKp(sv));
+            } else {
+                if (((UV)1 << NV_PRESERVES_UV_BITS) >
+                    U_V(SvNVX(sv) > 0 ? SvNVX(sv) : -SvNVX(sv))) {
+                    /* Small enough to preserve all bits. */
+                    (void)SvIOKp_on(sv);
+                    SvNOK_on(sv);
+                    SvIVX(sv) = I_V(SvNVX(sv));
+                    if ((NV)(SvIVX(sv)) == SvNVX(sv))
+                        SvIOK_on(sv);
+                    /* Assumption: first non-preserved integer is < IV_MAX,
+                       this NV is in the preserved range, therefore: */
+                    if (!(U_V(SvNVX(sv) > 0 ? SvNVX(sv) : -SvNVX(sv))
+                          < (UV)IV_MAX)) {
+                        Perl_croak(aTHX_ "sv_2uv assumed (U_V(fabs(SvNVX(sv))) < (UV)IV_MAX) but SvNVX(sv)=%g U_V is 0x%"UVxf", IV_MAX is 0x%"UVxf"\n", SvNVX(sv), U_V(SvNVX(sv)), (UV)IV_MAX);
+                    }
+                } else
+                    sv_2iuv_non_preserve (sv, numtype);
+            }
+#endif /* NV_PRESERVES_UV */
        }
     }
     else  {
        if (!(SvFLAGS(sv) & SVs_PADTMP)) {
-           dTHR;
            if (ckWARN(WARN_UNINITIALIZED) && !PL_localizing)
                report_uninit();
        }
@@ -1762,8 +2207,8 @@ Perl_sv_2nv(pTHX_ register SV *sv)
        if (SvNOKp(sv))
            return SvNVX(sv);
        if (SvPOKp(sv) && SvLEN(sv)) {
-           dTHR;
-           if (ckWARN(WARN_NUMERIC) && !SvIOKp(sv) && !looks_like_number(sv))
+           if (ckWARN(WARN_NUMERIC) && !SvIOKp(sv) &&
+               !grok_number(SvPVX(sv), SvCUR(sv), NULL))
                not_a_number(sv);
            return Atof(SvPVX(sv));
        }
@@ -1775,7 +2220,6 @@ Perl_sv_2nv(pTHX_ register SV *sv)
        }       
         if (!SvROK(sv)) {
            if (!(SvFLAGS(sv) & SVs_PADTMP)) {
-               dTHR;
                if (ckWARN(WARN_UNINITIALIZED) && !PL_localizing)
                    report_uninit();
            }
@@ -1785,12 +2229,15 @@ Perl_sv_2nv(pTHX_ register SV *sv)
     if (SvTHINKFIRST(sv)) {
        if (SvROK(sv)) {
          SV* tmpstr;
-         if (SvAMAGIC(sv) && (tmpstr=AMG_CALLun(sv,numer)))
+          if (SvAMAGIC(sv) && (tmpstr=AMG_CALLun(sv,numer)) &&
+                (SvTYPE(tmpstr) != SVt_RV || (SvRV(tmpstr) != SvRV(sv))))
              return SvNV(tmpstr);
          return PTR2NV(SvRV(sv));
        }
+       if (SvREADONLY(sv) && SvFAKE(sv)) {
+           sv_force_normal(sv);
+       }
        if (SvREADONLY(sv) && !SvOK(sv)) {
-           dTHR;
            if (ckWARN(WARN_UNINITIALIZED))
                report_uninit();
            return 0.0;
@@ -1801,7 +2248,7 @@ Perl_sv_2nv(pTHX_ register SV *sv)
            sv_upgrade(sv, SVt_PVNV);
        else
            sv_upgrade(sv, SVt_NV);
-#if defined(USE_LONG_DOUBLE)
+#ifdef USE_LONG_DOUBLE
        DEBUG_c({
            STORE_NUMERIC_LOCAL_SET_STANDARD();
            PerlIO_printf(Perl_debug_log,
@@ -1820,27 +2267,117 @@ Perl_sv_2nv(pTHX_ register SV *sv)
     }
     else if (SvTYPE(sv) < SVt_PVNV)
        sv_upgrade(sv, SVt_PVNV);
-    if (SvIOKp(sv) &&
-           (!SvPOKp(sv) || !strchr(SvPVX(sv),'.') || !looks_like_number(sv)))
-    {
+    if (SvNOKp(sv) && !(SvIOK(sv) || SvPOK(sv))) {
+       SvNOK_on(sv);
+    }
+    else if (SvIOKp(sv)) {
        SvNVX(sv) = SvIsUV(sv) ? (NV)SvUVX(sv) : (NV)SvIVX(sv);
+#ifdef NV_PRESERVES_UV
+       SvNOK_on(sv);
+#else
+       /* Only set the public NV OK flag if this NV preserves the IV  */
+       /* Check it's not 0xFFFFFFFFFFFFFFFF */
+       if (SvIsUV(sv) ? ((SvUVX(sv) != UV_MAX)&&(SvUVX(sv) == U_V(SvNVX(sv))))
+                      : (SvIVX(sv) == I_V(SvNVX(sv))))
+           SvNOK_on(sv);
+       else
+           SvNOKp_on(sv);
+#endif
     }
     else if (SvPOKp(sv) && SvLEN(sv)) {
-       dTHR;
-       if (ckWARN(WARN_NUMERIC) && !SvIOKp(sv) && !looks_like_number(sv))
+       UV value;
+       int numtype = grok_number(SvPVX(sv), SvCUR(sv), &value);
+       if (ckWARN(WARN_NUMERIC) && !SvIOKp(sv) && !numtype)
            not_a_number(sv);
+#ifdef NV_PRESERVES_UV
+       if ((numtype & (IS_NUMBER_IN_UV | IS_NUMBER_NOT_INT))
+           == IS_NUMBER_IN_UV) {
+           /* It's defintately an integer */
+           SvNVX(sv) = (numtype & IS_NUMBER_NEG) ? -(NV)value : (NV)value;
+       } else
+           SvNVX(sv) = Atof(SvPVX(sv));
+       SvNOK_on(sv);
+#else
        SvNVX(sv) = Atof(SvPVX(sv));
+       /* Only set the public NV OK flag if this NV preserves the value in
+          the PV at least as well as an IV/UV would.
+          Not sure how to do this 100% reliably. */
+       /* if that shift count is out of range then Configure's test is
+          wonky. We shouldn't be in here with NV_PRESERVES_UV_BITS ==
+          UV_BITS */
+       if (((UV)1 << NV_PRESERVES_UV_BITS) >
+           U_V(SvNVX(sv) > 0 ? SvNVX(sv) : -SvNVX(sv))) {
+           SvNOK_on(sv); /* Definitely small enough to preserve all bits */
+       } else if (!(numtype & IS_NUMBER_IN_UV)) {
+            /* Can't use strtol etc to convert this string, so don't try.
+               sv_2iv and sv_2uv will use the NV to convert, not the PV.  */
+            SvNOK_on(sv);
+        } else {
+            /* value has been set.  It may not be precise.  */
+           if ((numtype & IS_NUMBER_NEG) && (value > (UV)IV_MIN)) {
+               /* 2s complement assumption for (UV)IV_MIN  */
+                SvNOK_on(sv); /* Integer is too negative.  */
+            } else {
+                SvNOKp_on(sv);
+                SvIOKp_on(sv);
+
+                if (numtype & IS_NUMBER_NEG) {
+                    SvIVX(sv) = -(IV)value;
+                } else if (value <= (UV)IV_MAX) {
+                   SvIVX(sv) = (IV)value;
+               } else {
+                   SvUVX(sv) = value;
+                   SvIsUV_on(sv);
+               }
+
+                if (numtype & IS_NUMBER_NOT_INT) {
+                    /* I believe that even if the original PV had decimals,
+                       they are lost beyond the limit of the FP precision.
+                       However, neither is canonical, so both only get p
+                       flags.  NWC, 2000/11/25 */
+                    /* Both already have p flags, so do nothing */
+                } else {
+                    NV nv = SvNVX(sv);
+                    if (SvNVX(sv) < (NV)IV_MAX + 0.5) {
+                        if (SvIVX(sv) == I_V(nv)) {
+                            SvNOK_on(sv);
+                            SvIOK_on(sv);
+                        } else {
+                            SvIOK_on(sv);
+                            /* It had no "." so it must be integer.  */
+                        }
+                    } else {
+                        /* between IV_MAX and NV(UV_MAX).
+                           Could be slightly > UV_MAX */
+
+                        if (numtype & IS_NUMBER_NOT_INT) {
+                            /* UV and NV both imprecise.  */
+                        } else {
+                            UV nv_as_uv = U_V(nv);
+
+                            if (value == nv_as_uv && SvUVX(sv) != UV_MAX) {
+                                SvNOK_on(sv);
+                                SvIOK_on(sv);
+                            } else {
+                                SvIOK_on(sv);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+#endif /* NV_PRESERVES_UV */
     }
     else  {
-       dTHR;
        if (ckWARN(WARN_UNINITIALIZED) && !PL_localizing && !(SvFLAGS(sv) & SVs_PADTMP))
            report_uninit();
        if (SvTYPE(sv) < SVt_NV)
            /* Typically the caller expects that sv_any is not NULL now.  */
+           /* XXX Ilya implies that this is a bug in callers that assume this
+              and ideally should be fixed.  */
            sv_upgrade(sv, SVt_NV);
        return 0.0;
     }
-    SvNOK_on(sv);
 #if defined(USE_LONG_DOUBLE)
     DEBUG_c({
        STORE_NUMERIC_LOCAL_SET_STANDARD();
@@ -1859,181 +2396,50 @@ Perl_sv_2nv(pTHX_ register SV *sv)
     return SvNVX(sv);
 }
 
+/* Caller must validate PVX  */
 STATIC IV
 S_asIV(pTHX_ SV *sv)
 {
-    I32 numtype = looks_like_number(sv);
-    NV d;
+    UV value;
+    int numtype = grok_number(SvPVX(sv), SvCUR(sv), &value);
 
-    if (numtype & IS_NUMBER_TO_INT_BY_ATOL)
-       return Atol(SvPVX(sv));
+    if ((numtype & (IS_NUMBER_IN_UV | IS_NUMBER_NOT_INT))
+       == IS_NUMBER_IN_UV) {
+       /* It's defintately an integer */
+       if (numtype & IS_NUMBER_NEG) {
+           if (value < (UV)IV_MIN)
+               return -(IV)value;
+       } else {
+           if (value < (UV)IV_MAX)
+               return (IV)value;
+       }
+    }
     if (!numtype) {
-       dTHR;
        if (ckWARN(WARN_NUMERIC))
            not_a_number(sv);
     }
-    d = Atof(SvPVX(sv));
-    return I_V(d);
+    return I_V(Atof(SvPVX(sv)));
 }
 
 STATIC UV
 S_asUV(pTHX_ SV *sv)
 {
-    I32 numtype = looks_like_number(sv);
+    UV value;
+    int numtype = grok_number(SvPVX(sv), SvCUR(sv), &value);
 
-#ifdef HAS_STRTOUL
-    if (numtype & IS_NUMBER_TO_INT_BY_ATOL)
-       return Strtoul(SvPVX(sv), Null(char**), 10);
-#endif
+    if ((numtype & (IS_NUMBER_IN_UV | IS_NUMBER_NOT_INT))
+       == IS_NUMBER_IN_UV) {
+       /* It's defintately an integer */
+       if (!(numtype & IS_NUMBER_NEG))
+           return value;
+    }
     if (!numtype) {
-       dTHR;
        if (ckWARN(WARN_NUMERIC))
            not_a_number(sv);
     }
     return U_V(Atof(SvPVX(sv)));
 }
 
-/*
- * Returns a combination of (advisory only - can get false negatives)
- *     IS_NUMBER_TO_INT_BY_ATOL, IS_NUMBER_TO_INT_BY_ATOF, IS_NUMBER_NOT_IV,
- *     IS_NUMBER_NEG
- * 0 if does not look like number.
- *
- * In fact possible values are 0 and
- * IS_NUMBER_TO_INT_BY_ATOL                            123
- * IS_NUMBER_TO_INT_BY_ATOL | IS_NUMBER_NOT_IV         123.1
- * IS_NUMBER_TO_INT_BY_ATOF | IS_NUMBER_NOT_IV         123e0
- * IS_NUMBER_INFINITY
- * with a possible addition of IS_NUMBER_NEG.
- */
-
-/*
-=for apidoc looks_like_number
-
-Test if an the content of an SV looks like a number (or is a
-number).
-
-=cut
-*/
-
-I32
-Perl_looks_like_number(pTHX_ SV *sv)
-{
-    register char *s;
-    register char *send;
-    register char *sbegin;
-    register char *nbegin;
-    I32 numtype = 0;
-    I32 sawinf  = 0;
-    STRLEN len;
-
-    if (SvPOK(sv)) {
-       sbegin = SvPVX(sv);
-       len = SvCUR(sv);
-    }
-    else if (SvPOKp(sv))
-       sbegin = SvPV(sv, len);
-    else
-       return 1;
-    send = sbegin + len;
-
-    s = sbegin;
-    while (isSPACE(*s))
-       s++;
-    if (*s == '-') {
-       s++;
-       numtype = IS_NUMBER_NEG;
-    }
-    else if (*s == '+')
-       s++;
-
-    nbegin = s;
-    /*
-     * we return IS_NUMBER_TO_INT_BY_ATOL if the number can be converted
-     * to _integer_ with atol() and IS_NUMBER_TO_INT_BY_ATOF if you need
-     * (int)atof().
-     */
-
-    /* next must be digit or the radix separator or beginning of infinity */
-    if (isDIGIT(*s)) {
-        do {
-           s++;
-        } while (isDIGIT(*s));
-
-       if (s - nbegin >= TYPE_DIGITS(IV))      /* Cannot cache ato[ul]() */
-           numtype |= IS_NUMBER_TO_INT_BY_ATOF | IS_NUMBER_NOT_IV;
-       else
-           numtype |= IS_NUMBER_TO_INT_BY_ATOL;
-
-        if (*s == '.'
-#ifdef USE_LOCALE_NUMERIC
-           || IS_NUMERIC_RADIX(*s)
-#endif
-           ) {
-           s++;
-           numtype |= IS_NUMBER_NOT_IV;
-            while (isDIGIT(*s))  /* optional digits after the radix */
-                s++;
-        }
-    }
-    else if (*s == '.'
-#ifdef USE_LOCALE_NUMERIC
-           || IS_NUMERIC_RADIX(*s)
-#endif
-           ) {
-        s++;
-       numtype |= IS_NUMBER_TO_INT_BY_ATOL | IS_NUMBER_NOT_IV;
-        /* no digits before the radix means we need digits after it */
-        if (isDIGIT(*s)) {
-           do {
-               s++;
-            } while (isDIGIT(*s));
-        }
-        else
-           return 0;
-    }
-    else if (*s == 'I' || *s == 'i') {
-       s++; if (*s != 'N' && *s != 'n') return 0;
-       s++; if (*s != 'F' && *s != 'f') return 0;
-       s++; if (*s == 'I' || *s == 'i') {
-           s++; if (*s != 'N' && *s != 'n') return 0;
-           s++; if (*s != 'I' && *s != 'i') return 0;
-           s++; if (*s != 'T' && *s != 't') return 0;
-           s++; if (*s != 'Y' && *s != 'y') return 0;
-       }
-       sawinf = 1;
-    }
-    else
-        return 0;
-
-    if (sawinf)
-       numtype = IS_NUMBER_INFINITY;
-    else {
-       /* we can have an optional exponent part */
-       if (*s == 'e' || *s == 'E') {
-           numtype &= ~IS_NUMBER_NEG;
-           numtype |= IS_NUMBER_TO_INT_BY_ATOF | IS_NUMBER_NOT_IV;
-           s++;
-           if (*s == '+' || *s == '-')
-               s++;
-           if (isDIGIT(*s)) {
-               do {
-                   s++;
-               } while (isDIGIT(*s));
-           }
-           else
-               return 0;
-       }
-    }
-    while (isSPACE(*s))
-       s++;
-    if (s >= send)
-       return numtype;
-    if (len == 10 && memEQ(sbegin, "0 but true", 10))
-       return IS_NUMBER_TO_INT_BY_ATOL;
-    return 0;
-}
-
 char *
 Perl_sv_2pv_nolen(pTHX_ register SV *sv)
 {
@@ -2070,6 +2476,12 @@ uiv_2buf(char *buf, IV iv, UV uv, int is_uv, char **peob)
 char *
 Perl_sv_2pv(pTHX_ register SV *sv, STRLEN *lp)
 {
+    return sv_2pv_flags(sv, lp, SV_GMAGIC);
+}
+
+char *
+Perl_sv_2pv_flags(pTHX_ register SV *sv, STRLEN *lp, I32 flags)
+{
     register char *s;
     int olderrno;
     SV *tsv;
@@ -2081,7 +2493,8 @@ Perl_sv_2pv(pTHX_ register SV *sv, STRLEN *lp)
        return "";
     }
     if (SvGMAGICAL(sv)) {
-       mg_get(sv);
+       if (flags & SV_GMAGIC)
+           mg_get(sv);
        if (SvPOKp(sv)) {
            *lp = SvCUR(sv);
            return SvPVX(sv);
@@ -2101,7 +2514,6 @@ Perl_sv_2pv(pTHX_ register SV *sv, STRLEN *lp)
        }
         if (!SvROK(sv)) {
            if (!(SvFLAGS(sv) & SVs_PADTMP)) {
-               dTHR;
                if (ckWARN(WARN_UNINITIALIZED) && !PL_localizing)
                    report_uninit();
            }
@@ -2112,7 +2524,8 @@ Perl_sv_2pv(pTHX_ register SV *sv, STRLEN *lp)
     if (SvTHINKFIRST(sv)) {
        if (SvROK(sv)) {
            SV* tmpstr;
-           if (SvAMAGIC(sv) && (tmpstr=AMG_CALLun(sv,string)))
+            if (SvAMAGIC(sv) && (tmpstr=AMG_CALLun(sv,string)) &&
+                (SvTYPE(tmpstr) != SVt_RV || (SvRV(tmpstr) != SvRV(sv))))
                return SvPV(tmpstr,*lp);
            sv = (SV*)SvRV(sv);
            if (!sv)
@@ -2126,8 +2539,7 @@ Perl_sv_2pv(pTHX_ register SV *sv, STRLEN *lp)
                           (SVs_OBJECT|SVf_OK|SVs_GMG|SVs_SMG|SVs_RMG))
                          == (SVs_OBJECT|SVs_RMG))
                         && strEQ(s=HvNAME(SvSTASH(sv)), "Regexp")
-                        && (mg = mg_find(sv, 'r'))) {
-                       dTHR;
+                        && (mg = mg_find(sv, PERL_MAGIC_qr))) {
                        regexp *re = (regexp *)mg->mg_obj;
 
                        if (!mg->mg_ptr) {
@@ -2198,18 +2610,39 @@ Perl_sv_2pv(pTHX_ register SV *sv, STRLEN *lp)
            return s;
        }
        if (SvREADONLY(sv) && !SvOK(sv)) {
-           dTHR;
            if (ckWARN(WARN_UNINITIALIZED))
                report_uninit();
            *lp = 0;
            return "";
        }
     }
-    if (SvNOKp(sv)) {                  /* See note in sv_2uv() */
-       /* XXXX 64-bit?  IV may have better precision... */
-       /* I tried changing this to be 64-bit-aware and
-        * the t/op/numconvert.t became very, very, angry.
-        * --jhi Sep 1999 */
+    if (SvIOK(sv) || ((SvIOKp(sv) && !SvNOKp(sv)))) {
+       /* I'm assuming that if both IV and NV are equally valid then
+          converting the IV is going to be more efficient */
+       U32 isIOK = SvIOK(sv);
+       U32 isUIOK = SvIsUV(sv);
+       char buf[TYPE_CHARS(UV)];
+       char *ebuf, *ptr;
+
+       if (SvTYPE(sv) < SVt_PVIV)
+           sv_upgrade(sv, SVt_PVIV);
+       if (isUIOK)
+           ptr = uiv_2buf(buf, 0, SvUVX(sv), 1, &ebuf);
+       else
+           ptr = uiv_2buf(buf, SvIVX(sv), 0, 0, &ebuf);
+       SvGROW(sv, ebuf - ptr + 1);     /* inlined from sv_setpvn */
+       Move(ptr,SvPVX(sv),ebuf - ptr,char);
+       SvCUR_set(sv, ebuf - ptr);
+       s = SvEND(sv);
+       *s = '\0';
+       if (isIOK)
+           SvIOK_on(sv);
+       else
+           SvIOKp_on(sv);
+       if (isUIOK)
+           SvIsUV_on(sv);
+    }
+    else if (SvNOKp(sv)) {
        if (SvTYPE(sv) < SVt_PVNV)
            sv_upgrade(sv, SVt_PVNV);
        /* The +20 is pure guesswork.  Configure test needed. --jhi */
@@ -2235,38 +2668,10 @@ Perl_sv_2pv(pTHX_ register SV *sv, STRLEN *lp)
            *--s = '\0';
 #endif
     }
-    else if (SvIOKp(sv)) {
-       U32 isIOK = SvIOK(sv);
-       U32 isUIOK = SvIsUV(sv);
-       char buf[TYPE_CHARS(UV)];
-       char *ebuf, *ptr;
-
-       if (SvTYPE(sv) < SVt_PVIV)
-           sv_upgrade(sv, SVt_PVIV);
-       if (isUIOK)
-           ptr = uiv_2buf(buf, 0, SvUVX(sv), 1, &ebuf);
-       else
-           ptr = uiv_2buf(buf, SvIVX(sv), 0, 0, &ebuf);
-       SvGROW(sv, ebuf - ptr + 1);     /* inlined from sv_setpvn */
-       Move(ptr,SvPVX(sv),ebuf - ptr,char);
-       SvCUR_set(sv, ebuf - ptr);
-       s = SvEND(sv);
-       *s = '\0';
-       if (isIOK)
-           SvIOK_on(sv);
-       else
-           SvIOKp_on(sv);
-       if (isUIOK)
-           SvIsUV_on(sv);
-       SvPOK_on(sv);
-    }
     else {
-       dTHR;
        if (ckWARN(WARN_UNINITIALIZED)
            && !PL_localizing && !(SvFLAGS(sv) & SVs_PADTMP))
-       {
            report_uninit();
-       }
        *lp = 0;
        if (SvTYPE(sv) < SVt_PV)
            /* Typically the caller expects that sv_any is not NULL now.  */
@@ -2330,7 +2735,8 @@ Perl_sv_2pvbyte_nolen(pTHX_ register SV *sv)
 char *
 Perl_sv_2pvbyte(pTHX_ register SV *sv, STRLEN *lp)
 {
-    return sv_2pv(sv,lp);
+    sv_utf8_downgrade(sv,0);
+    return SvPV(sv,*lp);
 }
 
 char *
@@ -2344,7 +2750,7 @@ char *
 Perl_sv_2pvutf8(pTHX_ register SV *sv, STRLEN *lp)
 {
     sv_utf8_upgrade(sv);
-    return sv_2pv(sv,lp);
+    return SvPV(sv,*lp);
 }
 
 /* This function is only called on magical items */
@@ -2357,9 +2763,9 @@ Perl_sv_2bool(pTHX_ register SV *sv)
     if (!SvOK(sv))
        return 0;
     if (SvROK(sv)) {
-       dTHR;
        SV* tmpsv;
-       if (SvAMAGIC(sv) && (tmpsv = AMG_CALLun(sv,bool_)))
+        if (SvAMAGIC(sv) && (tmpsv=AMG_CALLun(sv,bool_)) &&
+                (SvTYPE(tmpsv) != SVt_RV || (SvRV(tmpsv) != SvRV(sv))))
            return SvTRUE(tmpsv);
       return SvRV(sv) != 0;
     }
@@ -2389,33 +2795,80 @@ Perl_sv_2bool(pTHX_ register SV *sv)
 =for apidoc sv_utf8_upgrade
 
 Convert the PV of an SV to its UTF8-encoded form.
+Forces the SV to string form it it is not already.
+Always sets the SvUTF8 flag to avoid future validity checks even
+if all the bytes have hibit clear.
 
 =cut
 */
 
-void
+STRLEN
 Perl_sv_utf8_upgrade(pTHX_ register SV *sv)
 {
-    char *s, *t;
-    bool hibit;
+    return sv_utf8_upgrade_flags(sv, SV_GMAGIC);
+}
 
-    if (!sv || !SvPOK(sv) || SvUTF8(sv))
-       return;
+/*
+=for apidoc sv_utf8_upgrade_flags
+
+Convert the PV of an SV to its UTF8-encoded form.
+Forces the SV to string form it it is not already.
+Always sets the SvUTF8 flag to avoid future validity checks even
+if all the bytes have hibit clear. If C<flags> has C<SV_GMAGIC> bit set,
+will C<mg_get> on C<sv> if appropriate, else not. C<sv_utf8_upgrade> and
+C<sv_utf8_upgrade_nomg> are implemented in terms of this function.
+
+=cut
+*/
+
+STRLEN
+Perl_sv_utf8_upgrade_flags(pTHX_ register SV *sv, I32 flags)
+{
+    U8 *s, *t, *e;
+    int  hibit = 0;
+
+    if (!sv)
+       return 0;
+
+    if (!SvPOK(sv)) {
+       STRLEN len = 0;
+       (void) sv_2pv_flags(sv,&len, flags);
+       if (!SvPOK(sv))
+            return len;
+    }
+
+    if (SvUTF8(sv))
+       return SvCUR(sv);
+
+    if (SvREADONLY(sv) && SvFAKE(sv)) {
+       sv_force_normal(sv);
+    }
 
     /* This function could be much more efficient if we had a FLAG in SVs
      * to signal if there are any hibit chars in the PV.
+     * Given that there isn't make loop fast as possible
      */
-    for (s = t = SvPVX(sv), hibit = FALSE; t < SvEND(sv) && !hibit; t++)
-       if (*t & 0x80)
-           hibit = TRUE;
-
+    s = (U8 *) SvPVX(sv);
+    e = (U8 *) SvEND(sv);
+    t = s;
+    while (t < e) {
+       U8 ch = *t++;
+       if ((hibit = !NATIVE_IS_INVARIANT(ch)))
+           break;
+    }
     if (hibit) {
-       STRLEN len = SvCUR(sv) + 1; /* Plus the \0 */
+       STRLEN len;
+
+       len = SvCUR(sv) + 1; /* Plus the \0 */
        SvPVX(sv) = (char*)bytes_to_utf8((U8*)s, &len);
        SvCUR(sv) = len - 1;
-       SvUTF8_on(sv);
-       Safefree(s); /* No longer using what was there before */
+       if (SvLEN(sv) != 0)
+           Safefree(s); /* No longer using what was there before. */
+       SvLEN(sv) = len; /* No longer know the real size. */
     }
+    /* Mark as UTF-8 even if no hibit - saves scanning loop */
+    SvUTF8_on(sv);
+    return SvCUR(sv);
 }
 
 /*
@@ -2433,17 +2886,50 @@ bool
 Perl_sv_utf8_downgrade(pTHX_ register SV* sv, bool fail_ok)
 {
     if (SvPOK(sv) && SvUTF8(sv)) {
-        char *c = SvPVX(sv);
-       STRLEN len = SvCUR(sv) + 1;     /* include trailing NUL */
-        if (!utf8_to_bytes((U8*)c, &len)) {
-           if (fail_ok)
-               return FALSE;
-           else
-               Perl_croak("big byte");
+        if (SvCUR(sv)) {
+           U8 *s;
+           STRLEN len;
+
+           if (SvREADONLY(sv) && SvFAKE(sv))
+               sv_force_normal(sv);
+           s = (U8 *) SvPV(sv, len);
+           if (!utf8_to_bytes(s, &len)) {
+               if (fail_ok)
+                   return FALSE;
+#ifdef USE_BYTES_DOWNGRADES
+               else if (IN_BYTES) {
+                   U8 *d = s;
+                   U8 *e = (U8 *) SvEND(sv);
+                   int first = 1;
+                   while (s < e) {
+                       UV ch = utf8n_to_uvchr(s,(e-s),&len,0);
+                       if (first && ch > 255) {
+                           if (PL_op)
+                               Perl_warner(aTHX_ WARN_UTF8, "Wide character in byte %s",
+                                          PL_op_desc[PL_op->op_type]);
+                           else
+                               Perl_warner(aTHX_ WARN_UTF8, "Wide character in byte");
+                           first = 0;
+                       }
+                       *d++ = ch;
+                       s += len;
+                   }
+                   *d = '\0';
+                   len = (d - (U8 *) SvPVX(sv));
+               }
+#endif
+               else {
+                   if (PL_op)
+                       Perl_croak(aTHX_ "Wide character in %s",
+                                  PL_op_desc[PL_op->op_type]);
+                   else
+                       Perl_croak(aTHX_ "Wide character");
+               }
+           }
+           SvCUR(sv) = len;
        }
-       SvCUR(sv) = len - 1;
-       SvUTF8_off(sv);
     }
+    SvUTF8_off(sv);
     return TRUE;
 }
 
@@ -2451,7 +2937,8 @@ Perl_sv_utf8_downgrade(pTHX_ register SV* sv, bool fail_ok)
 =for apidoc sv_utf8_encode
 
 Convert the PV of an SV to UTF8-encoded, but then turn off the C<SvUTF8>
-flag so that it looks like bytes again. Nothing calls this.
+flag so that it looks like octets again. Used as a building block
+for encode_utf8 in Encode.xs
 
 =cut
 */
@@ -2459,28 +2946,43 @@ flag so that it looks like bytes again. Nothing calls this.
 void
 Perl_sv_utf8_encode(pTHX_ register SV *sv)
 {
-    sv_utf8_upgrade(sv);
+    (void) sv_utf8_upgrade(sv);
     SvUTF8_off(sv);
 }
 
+/*
+=for apidoc sv_utf8_decode
+
+Convert the octets in the PV from UTF-8 to chars. Scan for validity and then
+turn of SvUTF8 if needed so that we see characters. Used as a building block
+for decode_utf8 in Encode.xs
+
+=cut
+*/
+
+
+
 bool
 Perl_sv_utf8_decode(pTHX_ register SV *sv)
 {
     if (SvPOK(sv)) {
-        char *c;
-        bool has_utf = FALSE;
+        U8 *c;
+        U8 *e;
+
+       /* The octets may have got themselves encoded - get them back as bytes */
         if (!sv_utf8_downgrade(sv, TRUE))
            return FALSE;
 
         /* it is actually just a matter of turning the utf8 flag on, but
          * we want to make sure everything inside is valid utf8 first.
          */
-        c = SvPVX(sv);
-       if (!is_utf8_string((U8*)c, SvCUR(sv)+1))
+        c = (U8 *) SvPVX(sv);
+       if (!is_utf8_string(c, SvCUR(sv)+1))
            return FALSE;
-
-        while (c < SvEND(sv)) {
-            if (*c++ & 0x80) {
+        e = (U8 *) SvEND(sv);
+        while (c < e) {
+           U8 ch = *c++;
+            if (!UTF8_IS_INVARIANT(ch)) {
                SvUTF8_on(sv);
                break;
            }
@@ -2506,10 +3008,30 @@ C<sv_setsv_mg>.
 =cut
 */
 
+/* sv_setsv() is aliased to Perl_sv_setsv_macro; this function provided
+   for binary compatibility only
+*/
 void
 Perl_sv_setsv(pTHX_ SV *dstr, register SV *sstr)
 {
-    dTHR;
+    sv_setsv_flags(dstr, sstr, SV_GMAGIC);
+}
+
+/*
+=for apidoc sv_setsv_flags
+
+Copies the contents of the source SV C<ssv> into the destination SV C<dsv>.
+The source SV may be destroyed if it is mortal.  Does not handle 'set'
+magic.  If C<flags> has C<SV_GMAGIC> bit set, will C<mg_get> on C<ssv> if
+appropriate, else not. C<sv_setsv> and C<sv_setsv_nomg> are implemented
+in terms of this function.
+
+=cut
+*/
+
+void
+Perl_sv_setsv_flags(pTHX_ SV *dstr, register SV *sstr, I32 flags)
+{
     register U32 sflags;
     register int dtype;
     register int stype;
@@ -2552,7 +3074,8 @@ Perl_sv_setsv(pTHX_ SV *dstr, register SV *sstr)
            SvIVX(dstr) = SvIVX(sstr);
            if (SvIsUV(sstr))
                SvIsUV_on(dstr);
-           SvTAINT(dstr);
+           if (SvTAINTED(sstr))
+               SvTAINT(dstr);
            return;
        }
        goto undef_sstr;
@@ -2572,7 +3095,8 @@ Perl_sv_setsv(pTHX_ SV *dstr, register SV *sstr)
            }
            SvNVX(dstr) = SvNVX(sstr);
            (void)SvNOK_only(dstr);
-           SvTAINT(dstr);
+           if (SvTAINTED(sstr))
+               SvTAINT(dstr);
            return;
        }
        goto undef_sstr;
@@ -2626,7 +3150,7 @@ Perl_sv_setsv(pTHX_ SV *dstr, register SV *sstr)
                char *name = GvNAME(sstr);
                STRLEN len = GvNAMELEN(sstr);
                sv_upgrade(dstr, SVt_PVGV);
-               sv_magic(dstr, dstr, '*', Nullch, 0);
+               sv_magic(dstr, dstr, PERL_MAGIC_glob, Nullch, 0);
                GvSTASH(dstr) = (HV*)SvREFCNT_inc(GvSTASH(sstr));
                GvNAME(dstr) = savepvn(name, len);
                GvNAMELEN(dstr) = len;
@@ -2637,11 +3161,19 @@ Perl_sv_setsv(pTHX_ SV *dstr, register SV *sstr)
                     && GvCV(dstr) && PL_sortcop == CvSTART(GvCV(dstr)))
                Perl_croak(aTHX_ "Can't redefine active sort subroutine %s",
                      GvNAME(dstr));
+
+#ifdef GV_SHARED_CHECK
+                if (GvSHARED((GV*)dstr)) {
+                    Perl_croak(aTHX_ PL_no_modify);
+                }
+#endif
+
            (void)SvOK_off(dstr);
            GvINTRO_off(dstr);          /* one-shot flag */
            gp_free((GV*)dstr);
            GvGP(dstr) = gp_ref(GvGP(sstr));
-           SvTAINT(dstr);
+           if (SvTAINTED(sstr))
+               SvTAINT(dstr);
            if (GvIMPORTED(dstr) != GVf_IMPORTED
                && CopSTASH_ne(PL_curcop, GvSTASH(dstr)))
            {
@@ -2653,7 +3185,7 @@ Perl_sv_setsv(pTHX_ SV *dstr, register SV *sstr)
        /* FALL THROUGH */
 
     default:
-       if (SvGMAGICAL(sstr)) {
+       if (SvGMAGICAL(sstr) && (flags & SV_GMAGIC)) {
            mg_get(sstr);
            if (SvTYPE(sstr) != stype) {
                stype = SvTYPE(sstr);
@@ -2676,6 +3208,12 @@ Perl_sv_setsv(pTHX_ SV *dstr, register SV *sstr)
                SV *dref = 0;
                int intro = GvINTRO(dstr);
 
+#ifdef GV_SHARED_CHECK
+                if (GvSHARED((GV*)dstr)) {
+                    Perl_croak(aTHX_ PL_no_modify);
+                }
+#endif
+
                if (intro) {
                    GP *gp;
                    gp_free((GV*)dstr);
@@ -2730,12 +3268,6 @@ Perl_sv_setsv(pTHX_ SV *dstr, register SV *sstr)
                            if (!GvCVGEN((GV*)dstr) &&
                                (CvROOT(cv) || CvXSUB(cv)))
                            {
-                               SV *const_sv = cv_const_sv(cv);
-                               bool const_changed = TRUE;
-                               if(const_sv)
-                                   const_changed = sv_cmp(const_sv,
-                                          op_const_sv(CvSTART((CV*)sref),
-                                                      (CV*)sref));
                                /* ahem, death to those who redefine
                                 * active sort subs */
                                if (PL_curstackinfo->si_type == PERLSI_SORT &&
@@ -2743,11 +3275,20 @@ Perl_sv_setsv(pTHX_ SV *dstr, register SV *sstr)
                                    Perl_croak(aTHX_
                                    "Can't redefine active sort subroutine %s",
                                          GvENAME((GV*)dstr));
-                               if ((const_changed && const_sv) || ckWARN(WARN_REDEFINE))
-                                   Perl_warner(aTHX_ WARN_REDEFINE, const_sv ?
-                                            "Constant subroutine %s redefined"
-                                            : "Subroutine %s redefined",
-                                            GvENAME((GV*)dstr));
+                               /* Redefining a sub - warning is mandatory if
+                                  it was a const and its value changed. */
+                               if (ckWARN(WARN_REDEFINE)
+                                   || (CvCONST(cv)
+                                       && (!CvCONST((CV*)sref)
+                                           || sv_cmp(cv_const_sv(cv),
+                                                     cv_const_sv((CV*)sref)))))
+                               {
+                                   Perl_warner(aTHX_ WARN_REDEFINE,
+                                       CvCONST(cv)
+                                       ? "Constant subroutine %s redefined"
+                                       : "Subroutine %s redefined",
+                                       GvENAME((GV*)dstr));
+                               }
                            }
                            cv_ckproto(cv, (GV*)dstr,
                                       SvPOK(sref) ? SvPVX(sref) : Nullch);
@@ -2794,7 +3335,8 @@ Perl_sv_setsv(pTHX_ SV *dstr, register SV *sstr)
                    SvREFCNT_dec(dref);
                if (intro)
                    SAVEFREESV(sref);
-               SvTAINT(dstr);
+               if (SvTAINTED(sstr))
+                   SvTAINT(dstr);
                return;
            }
            if (SvPVX(dstr)) {
@@ -2808,14 +3350,19 @@ Perl_sv_setsv(pTHX_ SV *dstr, register SV *sstr)
        SvRV(dstr) = SvREFCNT_inc(SvRV(sstr));
        SvROK_on(dstr);
        if (sflags & SVp_NOK) {
-           SvNOK_on(dstr);
+           SvNOKp_on(dstr);
+           /* Only set the public OK flag if the source has public OK.  */
+           if (sflags & SVf_NOK)
+               SvFLAGS(dstr) |= SVf_NOK;
            SvNVX(dstr) = SvNVX(sstr);
        }
        if (sflags & SVp_IOK) {
-           (void)SvIOK_on(dstr);
-           SvIVX(dstr) = SvIVX(sstr);
+           (void)SvIOKp_on(dstr);
+           if (sflags & SVf_IOK)
+               SvFLAGS(dstr) |= SVf_IOK;
            if (sflags & SVf_IVisUV)
                SvIsUV_on(dstr);
+           SvIVX(dstr) = SvIVX(sstr);
        }
        if (SvAMAGIC(sstr)) {
            SvAMAGIC_on(dstr);
@@ -2833,7 +3380,8 @@ Perl_sv_setsv(pTHX_ SV *dstr, register SV *sstr)
        if (SvTEMP(sstr) &&             /* slated for free anyway? */
            SvREFCNT(sstr) == 1 &&      /* and no other references to it? */
            !(sflags & SVf_OOK) &&      /* and not involved in OOK hack? */
-           SvLEN(sstr))                        /* and really is a string */
+           SvLEN(sstr)         &&      /* and really is a string */
+           !(PL_op && PL_op->op_type == OP_AASSIGN)) /* and won't be needed again, potentially */
        {
            if (SvPVX(dstr)) {          /* we know that dtype >= SVt_PV */
                if (SvOOK(dstr)) {
@@ -2864,36 +3412,51 @@ Perl_sv_setsv(pTHX_ SV *dstr, register SV *sstr)
            *SvEND(dstr) = '\0';
            (void)SvPOK_only(dstr);
        }
-       if ((sflags & SVf_UTF8) && !IN_BYTE)
+       if (sflags & SVf_UTF8)
            SvUTF8_on(dstr);
        /*SUPPRESS 560*/
        if (sflags & SVp_NOK) {
-           SvNOK_on(dstr);
+           SvNOKp_on(dstr);
+           if (sflags & SVf_NOK)
+               SvFLAGS(dstr) |= SVf_NOK;
            SvNVX(dstr) = SvNVX(sstr);
        }
        if (sflags & SVp_IOK) {
-           (void)SvIOK_on(dstr);
-           SvIVX(dstr) = SvIVX(sstr);
+           (void)SvIOKp_on(dstr);
+           if (sflags & SVf_IOK)
+               SvFLAGS(dstr) |= SVf_IOK;
            if (sflags & SVf_IVisUV)
                SvIsUV_on(dstr);
-       }
-    }
-    else if (sflags & SVp_NOK) {
-       SvNVX(dstr) = SvNVX(sstr);
-       (void)SvNOK_only(dstr);
-       if (sflags & SVf_IOK) {
-           (void)SvIOK_on(dstr);
            SvIVX(dstr) = SvIVX(sstr);
-           /* XXXX Do we want to set IsUV for IV(ROK)?  Be extra safe... */
-           if (sflags & SVf_IVisUV)
-               SvIsUV_on(dstr);
        }
     }
     else if (sflags & SVp_IOK) {
-       (void)SvIOK_only(dstr);
-       SvIVX(dstr) = SvIVX(sstr);
+       if (sflags & SVf_IOK)
+           (void)SvIOK_only(dstr);
+       else {
+           (void)SvOK_off(dstr);
+           (void)SvIOKp_on(dstr);
+       }
+       /* XXXX Do we want to set IsUV for IV(ROK)?  Be extra safe... */
        if (sflags & SVf_IVisUV)
            SvIsUV_on(dstr);
+       SvIVX(dstr) = SvIVX(sstr);
+       if (sflags & SVp_NOK) {
+           if (sflags & SVf_NOK)
+               (void)SvNOK_on(dstr);
+           else
+               (void)SvNOKp_on(dstr);
+           SvNVX(dstr) = SvNVX(sstr);
+       }
+    }
+    else if (sflags & SVp_NOK) {
+       if (sflags & SVf_NOK)
+           (void)SvNOK_only(dstr);
+       else {
+           (void)SvOK_off(dstr);
+           SvNOKp_on(dstr);
+       }
+       SvNVX(dstr) = SvNVX(sstr);
     }
     else {
        if (dtype == SVt_PVGV) {
@@ -2903,7 +3466,8 @@ Perl_sv_setsv(pTHX_ SV *dstr, register SV *sstr)
        else
            (void)SvOK_off(dstr);
     }
-    SvTAINT(dstr);
+    if (SvTAINTED(sstr))
+       SvTAINT(dstr);
 }
 
 /*
@@ -2934,13 +3498,18 @@ void
 Perl_sv_setpvn(pTHX_ register SV *sv, register const char *ptr, register STRLEN len)
 {
     register char *dptr;
-    assert(len >= 0);  /* STRLEN is probably unsigned, so this may
-                         elicit a warning, but it won't hurt. */
+
     SV_CHECK_THINKFIRST(sv);
     if (!ptr) {
        (void)SvOK_off(sv);
        return;
     }
+    else {
+        /* len is STRLEN which is unsigned, need to copy to signed */
+       IV iv = len;
+       if (iv < 0)
+           Perl_croak(aTHX_ "panic: sv_setpvn called with negative strlen");
+    }
     (void)SvUPGRADE(sv, SVt_PV);
 
     SvGROW(sv, len + 1);
@@ -2948,7 +3517,7 @@ Perl_sv_setpvn(pTHX_ register SV *sv, register const char *ptr, register STRLEN
     Move(ptr,dptr,len,char);
     dptr[len] = '\0';
     SvCUR_set(sv, len);
-    (void)SvPOK_only(sv);              /* validate pointer */
+    (void)SvPOK_only_UTF8(sv);         /* validate pointer */
     SvTAINT(sv);
 }
 
@@ -2992,7 +3561,7 @@ Perl_sv_setpv(pTHX_ register SV *sv, register const char *ptr)
     SvGROW(sv, len + 1);
     Move(ptr,SvPVX(sv),len+1,char);
     SvCUR_set(sv, len);
-    (void)SvPOK_only(sv);              /* validate pointer */
+    (void)SvPOK_only_UTF8(sv);         /* validate pointer */
     SvTAINT(sv);
 }
 
@@ -3042,7 +3611,7 @@ Perl_sv_usepvn(pTHX_ register SV *sv, register char *ptr, register STRLEN len)
     SvCUR_set(sv, len);
     SvLEN_set(sv, len+1);
     *SvEND(sv) = '\0';
-    (void)SvPOK_only(sv);              /* validate pointer */
+    (void)SvPOK_only_UTF8(sv);         /* validate pointer */
     SvTAINT(sv);
 }
 
@@ -3062,10 +3631,9 @@ Perl_sv_usepvn_mg(pTHX_ register SV *sv, register char *ptr, register STRLEN len
 }
 
 void
-Perl_sv_force_normal(pTHX_ register SV *sv)
+Perl_sv_force_normal_flags(pTHX_ register SV *sv, U32 flags)
 {
     if (SvREADONLY(sv)) {
-       dTHR;
        if (SvFAKE(sv)) {
            char *pvx = SvPVX(sv);
            STRLEN len = SvCUR(sv);
@@ -3075,17 +3643,23 @@ Perl_sv_force_normal(pTHX_ register SV *sv)
            *SvEND(sv) = '\0';
            SvFAKE_off(sv);
            SvREADONLY_off(sv);
-           unsharepvn(pvx,len,hash);
+           unsharepvn(pvx,SvUTF8(sv)?-len:len,hash);
        }
        else if (PL_curcop != &PL_compiling)
            Perl_croak(aTHX_ PL_no_modify);
     }
     if (SvROK(sv))
-       sv_unref(sv);
+       sv_unref_flags(sv, flags);
     else if (SvFAKE(sv) && SvTYPE(sv) == SVt_PVGV)
        sv_unglob(sv);
 }
 
+void
+Perl_sv_force_normal(pTHX_ register SV *sv)
+{
+    sv_force_normal_flags(sv, 0);
+}
+
 /*
 =for apidoc sv_chop
 
@@ -3133,27 +3707,50 @@ Perl_sv_chop(pTHX_ register SV *sv, register char *ptr) /* like set but assuming
 =for apidoc sv_catpvn
 
 Concatenates the string onto the end of the string which is in the SV.  The
-C<len> indicates number of bytes to copy.  Handles 'get' magic, but not
-'set' magic.  See C<sv_catpvn_mg>.
+C<len> indicates number of bytes to copy.  If the SV has the UTF8
+status set, then the bytes appended should be valid UTF8.
+Handles 'get' magic, but not 'set' magic.  See C<sv_catpvn_mg>.
 
 =cut
 */
 
+/* sv_catpvn() is aliased to Perl_sv_catpvn_macro; this function provided
+   for binary compatibility only
+*/
 void
-Perl_sv_catpvn(pTHX_ register SV *sv, register const char *ptr, register STRLEN len)
+Perl_sv_catpvn(pTHX_ SV *dsv, const char* sstr, STRLEN slen)
 {
-    STRLEN tlen;
-    char *junk;
+    sv_catpvn_flags(dsv, sstr, slen, SV_GMAGIC);
+}
 
-    junk = SvPV_force(sv, tlen);
-    SvGROW(sv, tlen + len + 1);
-    if (ptr == junk)
-       ptr = SvPVX(sv);
-    Move(ptr,SvPVX(sv)+tlen,len,char);
-    SvCUR(sv) += len;
-    *SvEND(sv) = '\0';
-    (void)SvPOK_only_UTF8(sv);         /* validate pointer */
-    SvTAINT(sv);
+/*
+=for apidoc sv_catpvn_flags
+
+Concatenates the string onto the end of the string which is in the SV.  The
+C<len> indicates number of bytes to copy.  If the SV has the UTF8
+status set, then the bytes appended should be valid UTF8.
+If C<flags> has C<SV_GMAGIC> bit set, will C<mg_get> on C<dsv> if
+appropriate, else not. C<sv_catpvn> and C<sv_catpvn_nomg> are implemented
+in terms of this function.
+
+=cut
+*/
+
+void
+Perl_sv_catpvn_flags(pTHX_ register SV *dsv, register const char *sstr, register STRLEN slen, I32 flags)
+{
+    STRLEN dlen;
+    char *dstr;
+
+    dstr = SvPV_force_flags(dsv, dlen, flags);
+    SvGROW(dsv, dlen + slen + 1);
+    if (sstr == dstr)
+       sstr = SvPVX(dsv);
+    Move(sstr, SvPVX(dsv) + dlen, slen, char);
+    SvCUR(dsv) += slen;
+    *SvEND(dsv) = '\0';
+    (void)SvPOK_only_UTF8(dsv);                /* validate pointer */
+    SvTAINT(dsv);
 }
 
 /*
@@ -3174,27 +3771,58 @@ Perl_sv_catpvn_mg(pTHX_ register SV *sv, register const char *ptr, register STRL
 /*
 =for apidoc sv_catsv
 
-Concatenates the string from SV C<ssv> onto the end of the string in SV
-C<dsv>.  Handles 'get' magic, but not 'set' magic.  See C<sv_catsv_mg>.
+Concatenates the string from SV C<ssv> onto the end of the string in
+SV C<dsv>.  Modifies C<dsv> but not C<ssv>.  Handles 'get' magic, but
+not 'set' magic.  See C<sv_catsv_mg>.
 
-=cut
-*/
+=cut */
 
+/* sv_catsv() is aliased to Perl_sv_catsv_macro; this function provided
+   for binary compatibility only
+*/
 void
 Perl_sv_catsv(pTHX_ SV *dstr, register SV *sstr)
 {
-    char *s;
-    STRLEN len;
-    if (!sstr)
+    sv_catsv_flags(dstr, sstr, SV_GMAGIC);
+}
+
+/*
+=for apidoc sv_catsv_flags
+
+Concatenates the string from SV C<ssv> onto the end of the string in
+SV C<dsv>.  Modifies C<dsv> but not C<ssv>.  If C<flags> has C<SV_GMAGIC>
+bit set, will C<mg_get> on the SVs if appropriate, else not. C<sv_catsv>
+and C<sv_catsv_nomg> are implemented in terms of this function.
+
+=cut */
+
+void
+Perl_sv_catsv_flags(pTHX_ SV *dsv, register SV *ssv, I32 flags)
+{
+    char *spv;
+    STRLEN slen;
+    if (!ssv)
        return;
-    if ((s = SvPV(sstr, len))) {
-       if (DO_UTF8(sstr)) {
-           sv_utf8_upgrade(dstr);
-           sv_catpvn(dstr,s,len);
-           SvUTF8_on(dstr);
+    if ((spv = SvPV(ssv, slen))) {
+       bool sutf8 = DO_UTF8(ssv);
+       bool dutf8;
+
+       if (SvGMAGICAL(dsv) && (flags & SV_GMAGIC))
+           mg_get(dsv);
+       dutf8 = DO_UTF8(dsv);
+
+       if (dutf8 != sutf8) {
+           if (dutf8) {
+               /* Not modifying source SV, so taking a temporary copy. */
+               SV* csv = sv_2mortal(newSVpvn(spv, slen));
+
+               sv_utf8_upgrade(csv);
+               spv = SvPV(csv, slen);
+           }
+           else
+               sv_utf8_upgrade_nomg(dsv);
        }
-       else
-           sv_catpvn(dstr,s,len);
+       sv_catpvn_nomg(dsv, spv, slen);
     }
 }
 
@@ -3207,20 +3835,20 @@ Like C<sv_catsv>, but also handles 'set' magic.
 */
 
 void
-Perl_sv_catsv_mg(pTHX_ SV *dstr, register SV *sstr)
+Perl_sv_catsv_mg(pTHX_ SV *dsv, register SV *ssv)
 {
-    sv_catsv(dstr,sstr);
-    SvSETMAGIC(dstr);
+    sv_catsv(dsv,ssv);
+    SvSETMAGIC(dsv);
 }
 
 /*
 =for apidoc sv_catpv
 
 Concatenates the string onto the end of the string which is in the SV.
-Handles 'get' magic, but not 'set' magic.  See C<sv_catpv_mg>.
+If the SV has the UTF8 status set, then the bytes appended should be
+valid UTF8.  Handles 'get' magic, but not 'set' magic.  See C<sv_catpv_mg>.
 
-=cut
-*/
+=cut */
 
 void
 Perl_sv_catpv(pTHX_ register SV *sv, register const char *ptr)
@@ -3286,13 +3914,23 @@ Perl_sv_magic(pTHX_ register SV *sv, SV *obj, int how, const char *name, I32 nam
     MAGIC* mg;
 
     if (SvREADONLY(sv)) {
-       dTHR;
-       if (PL_curcop != &PL_compiling && !strchr("gBf", how))
+       if (PL_curcop != &PL_compiling
+           /* XXX this used to be !strchr("gBf", how), which seems to
+            * implicity be equal to !strchr("gBf\0", how), ie \0 matches
+            * too. I find this suprising, but have hadded PERL_MAGIC_sv
+            * to the list of things to check - DAPM 19-May-01 */
+           && how != PERL_MAGIC_regex_global
+           && how != PERL_MAGIC_bm
+           && how != PERL_MAGIC_fm
+           && how != PERL_MAGIC_sv
+          )
+       {
            Perl_croak(aTHX_ PL_no_modify);
+       }
     }
-    if (SvMAGICAL(sv) || (how == 't' && SvTYPE(sv) >= SVt_PVMG)) {
+    if (SvMAGICAL(sv) || (how == PERL_MAGIC_taint && SvTYPE(sv) >= SVt_PVMG)) {
        if (SvMAGIC(sv) && (mg = mg_find(sv, how))) {
-           if (how == 't')
+           if (how == PERL_MAGIC_taint)
                mg->mg_len |= 1;
            return;
        }
@@ -3302,135 +3940,148 @@ Perl_sv_magic(pTHX_ register SV *sv, SV *obj, int how, const char *name, I32 nam
     }
     Newz(702,mg, 1, MAGIC);
     mg->mg_moremagic = SvMAGIC(sv);
-
     SvMAGIC(sv) = mg;
-    if (!obj || obj == sv || how == '#' || how == 'r')
+
+    /* Some magic sontains a reference loop, where the sv and object refer to
+       each other.  To prevent a avoid a reference loop that would prevent such
+       objects being freed, we look for such loops and if we find one we avoid
+       incrementing the object refcount. */
+    if (!obj || obj == sv ||
+       how == PERL_MAGIC_arylen ||
+       how == PERL_MAGIC_qr ||
+       (SvTYPE(obj) == SVt_PVGV &&
+           (GvSV(obj) == sv || GvHV(obj) == (HV*)sv || GvAV(obj) == (AV*)sv ||
+           GvCV(obj) == (CV*)sv || GvIOp(obj) == (IO*)sv ||
+           GvFORM(obj) == (CV*)sv)))
+    {
        mg->mg_obj = obj;
+    }
     else {
-       dTHR;
        mg->mg_obj = SvREFCNT_inc(obj);
        mg->mg_flags |= MGf_REFCOUNTED;
     }
     mg->mg_type = how;
     mg->mg_len = namlen;
-    if (name)
+    if (name) {
        if (namlen >= 0)
            mg->mg_ptr = savepvn(name, namlen);
        else if (namlen == HEf_SVKEY)
            mg->mg_ptr = (char*)SvREFCNT_inc((SV*)name);
+    }
 
     switch (how) {
-    case 0:
+    case PERL_MAGIC_sv:
        mg->mg_virtual = &PL_vtbl_sv;
        break;
-    case 'A':
+    case PERL_MAGIC_overload:
         mg->mg_virtual = &PL_vtbl_amagic;
         break;
-    case 'a':
+    case PERL_MAGIC_overload_elem:
         mg->mg_virtual = &PL_vtbl_amagicelem;
         break;
-    case 'c':
-        mg->mg_virtual = 0;
+    case PERL_MAGIC_overload_table:
+        mg->mg_virtual = &PL_vtbl_ovrld;
         break;
-    case 'B':
+    case PERL_MAGIC_bm:
        mg->mg_virtual = &PL_vtbl_bm;
        break;
-    case 'D':
+    case PERL_MAGIC_regdata:
        mg->mg_virtual = &PL_vtbl_regdata;
        break;
-    case 'd':
+    case PERL_MAGIC_regdatum:
        mg->mg_virtual = &PL_vtbl_regdatum;
        break;
-    case 'E':
+    case PERL_MAGIC_env:
        mg->mg_virtual = &PL_vtbl_env;
        break;
-    case 'f':
+    case PERL_MAGIC_fm:
        mg->mg_virtual = &PL_vtbl_fm;
        break;
-    case 'e':
+    case PERL_MAGIC_envelem:
        mg->mg_virtual = &PL_vtbl_envelem;
        break;
-    case 'g':
+    case PERL_MAGIC_regex_global:
        mg->mg_virtual = &PL_vtbl_mglob;
        break;
-    case 'I':
+    case PERL_MAGIC_isa:
        mg->mg_virtual = &PL_vtbl_isa;
        break;
-    case 'i':
+    case PERL_MAGIC_isaelem:
        mg->mg_virtual = &PL_vtbl_isaelem;
        break;
-    case 'k':
+    case PERL_MAGIC_nkeys:
        mg->mg_virtual = &PL_vtbl_nkeys;
        break;
-    case 'L':
+    case PERL_MAGIC_dbfile:
        SvRMAGICAL_on(sv);
        mg->mg_virtual = 0;
        break;
-    case 'l':
+    case PERL_MAGIC_dbline:
        mg->mg_virtual = &PL_vtbl_dbline;
        break;
 #ifdef USE_THREADS
-    case 'm':
+    case PERL_MAGIC_mutex:
        mg->mg_virtual = &PL_vtbl_mutex;
        break;
 #endif /* USE_THREADS */
 #ifdef USE_LOCALE_COLLATE
-    case 'o':
+    case PERL_MAGIC_collxfrm:
         mg->mg_virtual = &PL_vtbl_collxfrm;
         break;
 #endif /* USE_LOCALE_COLLATE */
-    case 'P':
+    case PERL_MAGIC_tied:
        mg->mg_virtual = &PL_vtbl_pack;
        break;
-    case 'p':
-    case 'q':
+    case PERL_MAGIC_tiedelem:
+    case PERL_MAGIC_tiedscalar:
        mg->mg_virtual = &PL_vtbl_packelem;
        break;
-    case 'r':
+    case PERL_MAGIC_qr:
        mg->mg_virtual = &PL_vtbl_regexp;
        break;
-    case 'S':
+    case PERL_MAGIC_sig:
        mg->mg_virtual = &PL_vtbl_sig;
        break;
-    case 's':
+    case PERL_MAGIC_sigelem:
        mg->mg_virtual = &PL_vtbl_sigelem;
        break;
-    case 't':
+    case PERL_MAGIC_taint:
        mg->mg_virtual = &PL_vtbl_taint;
        mg->mg_len = 1;
        break;
-    case 'U':
+    case PERL_MAGIC_uvar:
        mg->mg_virtual = &PL_vtbl_uvar;
        break;
-    case 'v':
+    case PERL_MAGIC_vec:
        mg->mg_virtual = &PL_vtbl_vec;
        break;
-    case 'x':
+    case PERL_MAGIC_substr:
        mg->mg_virtual = &PL_vtbl_substr;
        break;
-    case 'y':
+    case PERL_MAGIC_defelem:
        mg->mg_virtual = &PL_vtbl_defelem;
        break;
-    case '*':
+    case PERL_MAGIC_glob:
        mg->mg_virtual = &PL_vtbl_glob;
        break;
-    case '#':
+    case PERL_MAGIC_arylen:
        mg->mg_virtual = &PL_vtbl_arylen;
        break;
-    case '.':
+    case PERL_MAGIC_pos:
        mg->mg_virtual = &PL_vtbl_pos;
        break;
-    case '<':
+    case PERL_MAGIC_backref:
        mg->mg_virtual = &PL_vtbl_backref;
        break;
-    case '~':  /* Reserved for use by extensions not perl internals.   */
+    case PERL_MAGIC_ext:
+       /* Reserved for use by extensions not perl internals.           */
        /* Useful for attaching extension internal data to perl vars.   */
        /* Note that multiple extensions may clash if magical scalars   */
        /* etc holding private data from one are passed to another.     */
        SvRMAGICAL_on(sv);
        break;
     default:
-       Perl_croak(aTHX_ "Don't know how to handle magic of type '%c'", how);
+       Perl_croak(aTHX_ "Don't know how to handle magic of type \\%o", how);
     }
     mg_magical(sv);
     if (SvGMAGICAL(sv))
@@ -3459,11 +4110,12 @@ Perl_sv_unmagic(pTHX_ SV *sv, int type)
            *mgp = mg->mg_moremagic;
            if (vtbl && vtbl->svt_free)
                CALL_FPTR(vtbl->svt_free)(aTHX_ sv, mg);
-           if (mg->mg_ptr && mg->mg_type != 'g')
+           if (mg->mg_ptr && mg->mg_type != PERL_MAGIC_regex_global) {
                if (mg->mg_len >= 0)
                    Safefree(mg->mg_ptr);
                else if (mg->mg_len == HEf_SVKEY)
                    SvREFCNT_dec((SV*)mg->mg_ptr);
+            }
            if (mg->mg_flags & MGf_REFCOUNTED)
                SvREFCNT_dec(mg->mg_obj);
            Safefree(mg);
@@ -3473,7 +4125,7 @@ Perl_sv_unmagic(pTHX_ SV *sv, int type)
     }
     if (!SvMAGIC(sv)) {
        SvMAGICAL_off(sv);
-       SvFLAGS(sv) |= (SvFLAGS(sv) & (SVp_IOK|SVp_NOK|SVp_POK)) >> PRIVSHIFT;
+       SvFLAGS(sv) |= (SvFLAGS(sv) & (SVp_NOK|SVp_POK)) >> PRIVSHIFT;
     }
 
     return 0;
@@ -3496,7 +4148,6 @@ Perl_sv_rvweaken(pTHX_ SV *sv)
     if (!SvROK(sv))
        Perl_croak(aTHX_ "Can't weaken a nonreference");
     else if (SvWEAKREF(sv)) {
-       dTHR;
        if (ckWARN(WARN_MISC))
            Perl_warner(aTHX_ WARN_MISC, "Reference is already weak");
        return sv;
@@ -3513,11 +4164,11 @@ S_sv_add_backref(pTHX_ SV *tsv, SV *sv)
 {
     AV *av;
     MAGIC *mg;
-    if (SvMAGICAL(tsv) && (mg = mg_find(tsv, '<')))
+    if (SvMAGICAL(tsv) && (mg = mg_find(tsv, PERL_MAGIC_backref)))
        av = (AV*)mg->mg_obj;
     else {
        av = newAV();
-       sv_magic(tsv, (SV*)av, '<', NULL, 0);
+       sv_magic(tsv, (SV*)av, PERL_MAGIC_backref, NULL, 0);
        SvREFCNT_dec(av);           /* for sv_magic */
     }
     av_push(av,sv);
@@ -3531,7 +4182,7 @@ S_sv_del_backref(pTHX_ SV *sv)
     I32 i;
     SV *tsv = SvRV(sv);
     MAGIC *mg;
-    if (!SvMAGICAL(tsv) || !(mg = mg_find(tsv, '<')))
+    if (!SvMAGICAL(tsv) || !(mg = mg_find(tsv, PERL_MAGIC_backref)))
        Perl_croak(aTHX_ "panic: del_backref");
     av = (AV *)mg->mg_obj;
     svp = AvARRAY(av);
@@ -3649,7 +4300,6 @@ Make the first argument a copy of the second, then delete the original.
 void
 Perl_sv_replace(pTHX_ register SV *sv, register SV *nsv)
 {
-    dTHR;
     U32 refcnt = SvREFCNT(sv);
     SV_CHECK_THINKFIRST(sv);
     if (SvREFCNT(nsv) != 1 && ckWARN_d(WARN_INTERNAL))
@@ -3690,10 +4340,9 @@ Perl_sv_clear(pTHX_ register SV *sv)
     assert(SvREFCNT(sv) == 0);
 
     if (SvOBJECT(sv)) {
-       dTHR;
        if (PL_defstash) {              /* Still have a symbol table? */
-           djSP;
-           GV* destructor;
+           dSP;
+           CV* destructor;
            SV tmpref;
 
            Zero(&tmpref, 1, SV);
@@ -3702,9 +4351,9 @@ Perl_sv_clear(pTHX_ register SV *sv)
            SvREADONLY_on(&tmpref);     /* DESTROY() could be naughty */
            SvREFCNT(&tmpref) = 1;
 
-           do {
+           do {        
                stash = SvSTASH(sv);
-               destructor = gv_fetchmethod(SvSTASH(sv), "DESTROY");
+               destructor = StashHANDLER(stash,DESTROY);
                if (destructor) {
                    ENTER;
                    PUSHSTACKi(PERLSI_DESTROY);
@@ -3713,8 +4362,7 @@ Perl_sv_clear(pTHX_ register SV *sv)
                    PUSHMARK(SP);
                    PUSHs(&tmpref);
                    PUTBACK;
-                   call_sv((SV*)GvCV(destructor),
-                           G_DISCARD|G_EVAL|G_KEEPERR);
+                   call_sv((SV*)destructor, G_DISCARD|G_EVAL|G_KEEPERR);
                    SvREFCNT(sv)--;
                    POPSTACK;
                    SPAGAIN;
@@ -3740,8 +4388,12 @@ Perl_sv_clear(pTHX_ register SV *sv)
                --PL_sv_objcount;       /* XXX Might want something more general */
        }
     }
-    if (SvTYPE(sv) >= SVt_PVMG && SvMAGIC(sv))
-       mg_free(sv);
+    if (SvTYPE(sv) >= SVt_PVMG) {
+       if (SvMAGIC(sv))
+           mg_free(sv);
+       if (SvFLAGS(sv) & SVpad_TYPED)
+           SvREFCNT_dec(SvSTASH(sv));
+    }
     stash = NULL;
     switch (SvTYPE(sv)) {
     case SVt_PVIO:
@@ -3800,7 +4452,7 @@ Perl_sv_clear(pTHX_ register SV *sv)
        else if (SvPVX(sv) && SvLEN(sv))
            Safefree(SvPVX(sv));
        else if (SvPVX(sv) && SvREADONLY(sv) && SvFAKE(sv)) {
-           unsharepvn(SvPVX(sv),SvCUR(sv),SvUVX(sv));
+           unsharepvn(SvPVX(sv),SvUTF8(sv)?-SvCUR(sv):SvCUR(sv),SvUVX(sv));
            SvFAKE_off(sv);
        }
        break;
@@ -3890,7 +4542,6 @@ Free the memory used by an SV.
 void
 Perl_sv_free(pTHX_ SV *sv)
 {
-    dTHR;
     int refcount_is_zero;
 
     if (!sv)
@@ -3967,26 +4618,18 @@ UTF8 bytes as a single character.
 STRLEN
 Perl_sv_len_utf8(pTHX_ register SV *sv)
 {
-    U8 *s;
-    U8 *send;
-    STRLEN len;
-
     if (!sv)
        return 0;
 
-#ifdef NOTYET
     if (SvGMAGICAL(sv))
-       len = mg_length(sv);
+       return mg_length(sv);
     else
-#endif
-       s = (U8*)SvPV(sv, len);
-    send = s + len;
-    len = 0;
-    while (s < send) {
-       s += UTF8SKIP(s);
-       len++;
+    {
+       STRLEN len;
+       U8 *s = (U8*)SvPV(sv, len);
+
+       return Perl_utf8_length(aTHX_ s, s + len);
     }
-    return len;
 }
 
 void
@@ -4032,18 +4675,19 @@ Perl_sv_pos_b2u(pTHX_ register SV *sv, I32* offsetp)
 
     s = (U8*)SvPV(sv, len);
     if (len < *offsetp)
-       Perl_croak(aTHX_ "panic: bad byte offset");
+       Perl_croak(aTHX_ "panic: sv_pos_b2u: bad byte offset");
     send = s + *offsetp;
     len = 0;
     while (s < send) {
-       s += UTF8SKIP(s);
-       ++len;
-    }
-    if (s != send) {
-        dTHR;
-       if (ckWARN_d(WARN_UTF8))
-           Perl_warner(aTHX_ WARN_UTF8, "Malformed UTF-8 character");
-       --len;
+       STRLEN n;
+       /* Call utf8n_to_uvchr() to validate the sequence */
+       utf8n_to_uvchr(s, UTF8SKIP(s), &n, 0);
+       if (n > 0) {
+           s += n;
+           len++;
+       }
+       else
+           break;
     }
     *offsetp = len;
     return;
@@ -4066,8 +4710,7 @@ Perl_sv_eq(pTHX_ register SV *sv1, register SV *sv2)
     char *pv2;
     STRLEN cur2;
     I32  eq     = 0;
-    bool pv1tmp = FALSE;
-    bool pv2tmp = FALSE;
+    char *tpv   = Nullch;
 
     if (!sv1) {
        pv1 = "";
@@ -4084,24 +4727,35 @@ Perl_sv_eq(pTHX_ register SV *sv1, register SV *sv2)
        pv2 = SvPV(sv2, cur2);
 
     /* do not utf8ize the comparands as a side-effect */
-    if (cur1 && cur2 && SvUTF8(sv1) != SvUTF8(sv2) && !IN_BYTE && 0) {
+    if (cur1 && cur2 && SvUTF8(sv1) != SvUTF8(sv2) && !IN_BYTES) {
+       bool is_utf8 = TRUE;
+        /* UTF-8ness differs */
+       if (PL_hints & HINT_UTF8_DISTINCT)
+           return FALSE;
+
        if (SvUTF8(sv1)) {
-           pv2 = (char*)bytes_to_utf8((U8*)pv2, &cur2);
-           pv2tmp = TRUE;
+           /* sv1 is the UTF-8 one , If is equal it must be downgrade-able */
+           char *pv = (char*)bytes_from_utf8((U8*)pv1, &cur1, &is_utf8);
+           if (pv != pv1)
+               pv1 = tpv = pv;
        }
        else {
-           pv1 = (char*)bytes_to_utf8((U8*)pv1, &cur1);
-           pv1tmp = TRUE;
+           /* sv2 is the UTF-8 one , If is equal it must be downgrade-able */
+           char *pv = (char *)bytes_from_utf8((U8*)pv2, &cur2, &is_utf8);
+           if (pv != pv2)
+               pv2 = tpv = pv;
+       }
+       if (is_utf8) {
+           /* Downgrade not possible - cannot be eq */
+           return FALSE;
        }
     }
 
     if (cur1 == cur2)
        eq = memEQ(pv1, pv2, cur1);
        
-    if (pv1tmp)
-       Safefree(pv1);
-    if (pv2tmp)
-       Safefree(pv2);
+    if (tpv != Nullch)
+       Safefree(tpv);
 
     return eq;
 }
@@ -4140,7 +4794,10 @@ Perl_sv_cmp(pTHX_ register SV *sv1, register SV *sv2)
        pv2 = SvPV(sv2, cur2);
 
     /* do not utf8ize the comparands as a side-effect */
-    if (cur1 && cur2 && SvUTF8(sv1) != SvUTF8(sv2) && !IN_BYTE) {
+    if (cur1 && cur2 && SvUTF8(sv1) != SvUTF8(sv2) && !IN_BYTES) {
+       if (PL_hints & HINT_UTF8_DISTINCT)
+           return SvUTF8(sv1) ? 1 : -1;
+
        if (SvUTF8(sv1)) {
            pv2 = (char*)bytes_to_utf8((U8*)pv2, &cur2);
            pv2tmp = TRUE;
@@ -4234,7 +4891,7 @@ Perl_sv_cmp_locale(pTHX_ register SV *sv1, register SV *sv2)
 
 #ifdef USE_LOCALE_COLLATE
 /*
- * Any scalar variable may carry an 'o' magic that contains the
+ * Any scalar variable may carry PERL_MAGIC_collxfrm magic that contains the
  * scalar data of the variable transformed to such a format that
  * a normal memory comparison can be used to compare the data
  * according to the locale settings.
@@ -4244,7 +4901,7 @@ Perl_sv_collxfrm(pTHX_ SV *sv, STRLEN *nxp)
 {
     MAGIC *mg;
 
-    mg = SvMAGICAL(sv) ? mg_find(sv, 'o') : (MAGIC *) NULL;
+    mg = SvMAGICAL(sv) ? mg_find(sv, PERL_MAGIC_collxfrm) : (MAGIC *) NULL;
     if (!mg || !mg->mg_ptr || *(U32*)mg->mg_ptr != PL_collation_ix) {
        char *s, *xf;
        STRLEN len, xlen;
@@ -4259,8 +4916,8 @@ Perl_sv_collxfrm(pTHX_ SV *sv, STRLEN *nxp)
                return xf + sizeof(PL_collation_ix);
            }
            if (! mg) {
-               sv_magic(sv, 0, 'o', 0, 0);
-               mg = mg_find(sv, 'o');
+               sv_magic(sv, 0, PERL_MAGIC_collxfrm, 0, 0);
+               mg = mg_find(sv, PERL_MAGIC_collxfrm);
                assert(mg);
            }
            mg->mg_ptr = xf;
@@ -4297,13 +4954,12 @@ appending to the currently-stored string.
 char *
 Perl_sv_gets(pTHX_ register SV *sv, register PerlIO *fp, I32 append)
 {
-    dTHR;
     char *rsptr;
     STRLEN rslen;
     register STDCHAR rslast;
     register STDCHAR *bp;
     register I32 cnt;
-    I32 i;
+    I32 i = 0;
 
     SV_CHECK_THINKFIRST(sv);
     (void)SvUPGRADE(sv, SVt_PV);
@@ -4333,14 +4989,31 @@ Perl_sv_gets(pTHX_ register SV *sv, register PerlIO *fp, I32 append)
 #endif
       SvCUR_set(sv, bytesread);
       buffer[bytesread] = '\0';
+      if (PerlIO_isutf8(fp))
+       SvUTF8_on(sv);
+      else
+       SvUTF8_off(sv);
       return(SvCUR(sv) ? SvPVX(sv) : Nullch);
     }
     else if (RsPARA(PL_rs)) {
        rsptr = "\n\n";
        rslen = 2;
     }
-    else
-       rsptr = SvPV(PL_rs, rslen);
+    else {
+       /* Get $/ i.e. PL_rs into same encoding as stream wants */
+       if (PerlIO_isutf8(fp)) {
+           rsptr = SvPVutf8(PL_rs, rslen);
+       }
+       else {
+           if (SvUTF8(PL_rs)) {
+               if (!sv_utf8_downgrade(PL_rs, TRUE)) {
+                   Perl_croak(aTHX_ "Wide character in $/");
+               }
+           }
+           rsptr = SvPV(PL_rs, rslen);
+       }
+    }
+
     rslast = rslen ? rsptr[rslen - 1] : '\0';
 
     if (RsPARA(PL_rs)) {               /* have to do this both before and after */
@@ -4559,6 +5232,11 @@ screamer2:
        }
     }
 
+    if (PerlIO_isutf8(fp))
+       SvUTF8_on(sv);
+    else
+       SvUTF8_off(sv);
+
     return (SvCUR(sv) - append) ? SvPVX(sv) : Nullch;
 }
 
@@ -4583,7 +5261,6 @@ Perl_sv_inc(pTHX_ register SV *sv)
        mg_get(sv);
     if (SvTHINKFIRST(sv)) {
        if (SvREADONLY(sv)) {
-           dTHR;
            if (PL_curcop != &PL_compiling)
                Perl_croak(aTHX_ PL_no_modify);
        }
@@ -4597,12 +5274,15 @@ Perl_sv_inc(pTHX_ register SV *sv)
        }
     }
     flags = SvFLAGS(sv);
-    if (flags & SVp_NOK) {
-       (void)SvNOK_only(sv);
-       SvNVX(sv) += 1.0;
-       return;
-    }
-    if (flags & SVp_IOK) {
+    if ((flags & (SVp_NOK|SVp_IOK)) == SVp_NOK) {
+       /* It's (privately or publicly) a float, but not tested as an
+          integer, so test it to see. */
+       (void) SvIV(sv);
+       flags = SvFLAGS(sv);
+    }
+    if ((flags & SVf_IOK) || ((flags & (SVp_IOK | SVp_NOK)) == SVp_IOK)) {
+       /* It's publicly an integer, or privately an integer-not-float */
+      oops_its_int:
        if (SvIsUV(sv)) {
            if (SvUVX(sv) == UV_MAX)
                sv_setnv(sv, (NV)UV_MAX + 1.0);
@@ -4611,7 +5291,7 @@ Perl_sv_inc(pTHX_ register SV *sv)
                ++SvUVX(sv);
        } else {
            if (SvIVX(sv) == IV_MAX)
-               sv_setnv(sv, (NV)IV_MAX + 1.0);
+               sv_setuv(sv, (UV)IV_MAX + 1);
            else {
                (void)SvIOK_only(sv);
                ++SvIVX(sv);
@@ -4619,18 +5299,59 @@ Perl_sv_inc(pTHX_ register SV *sv)
        }
        return;
     }
-    if (!(flags & SVp_POK) || !*SvPVX(sv)) {
-       if ((flags & SVTYPEMASK) < SVt_PVNV)
-           sv_upgrade(sv, SVt_NV);
-       SvNVX(sv) = 1.0;
+    if (flags & SVp_NOK) {
        (void)SvNOK_only(sv);
+       SvNVX(sv) += 1.0;
+       return;
+    }
+
+    if (!(flags & SVp_POK) || !*SvPVX(sv)) {
+       if ((flags & SVTYPEMASK) < SVt_PVIV)
+           sv_upgrade(sv, SVt_IV);
+       (void)SvIOK_only(sv);
+       SvIVX(sv) = 1;
        return;
     }
     d = SvPVX(sv);
     while (isALPHA(*d)) d++;
     while (isDIGIT(*d)) d++;
     if (*d) {
-       sv_setnv(sv,Atof(SvPVX(sv)) + 1.0);  /* punt */
+#ifdef PERL_PRESERVE_IVUV
+       /* Got to punt this an an integer if needs be, but we don't issue
+          warnings. Probably ought to make the sv_iv_please() that does
+          the conversion if possible, and silently.  */
+       int numtype = grok_number(SvPVX(sv), SvCUR(sv), NULL);
+       if (numtype && !(numtype & IS_NUMBER_INFINITY)) {
+           /* Need to try really hard to see if it's an integer.
+              9.22337203685478e+18 is an integer.
+              but "9.22337203685478e+18" + 0 is UV=9223372036854779904
+              so $a="9.22337203685478e+18"; $a+0; $a++
+              needs to be the same as $a="9.22337203685478e+18"; $a++
+              or we go insane. */
+       
+           (void) sv_2iv(sv);
+           if (SvIOK(sv))
+               goto oops_its_int;
+
+           /* sv_2iv *should* have made this an NV */
+           if (flags & SVp_NOK) {
+               (void)SvNOK_only(sv);
+               SvNVX(sv) += 1.0;
+               return;
+           }
+           /* I don't think we can get here. Maybe I should assert this
+              And if we do get here I suspect that sv_setnv will croak. NWC
+              Fall through. */
+#if defined(USE_LONG_DOUBLE)
+           DEBUG_c(PerlIO_printf(Perl_debug_log,"sv_inc punt failed to convert '%s' to IOK or NOKp, UV=0x%"UVxf" NV=%"PERL_PRIgldbl"\n",
+                                 SvPVX(sv), SvIVX(sv), SvNVX(sv)));
+#else
+           DEBUG_c(PerlIO_printf(Perl_debug_log,"sv_inc punt failed to convert '%s' to IOK or NOKp, UV=0x%"UVxf" NV=%g\n",
+                                 SvPVX(sv), SvIVX(sv), SvNVX(sv)));
+#endif
+       }
+#endif /* PERL_PRESERVE_IVUV */
+       sv_setnv(sv,Atof(SvPVX(sv)) + 1.0);
        return;
     }
     d--;
@@ -4691,7 +5412,6 @@ Perl_sv_dec(pTHX_ register SV *sv)
        mg_get(sv);
     if (SvTHINKFIRST(sv)) {
        if (SvREADONLY(sv)) {
-           dTHR;
            if (PL_curcop != &PL_compiling)
                Perl_croak(aTHX_ PL_no_modify);
        }
@@ -4704,13 +5424,12 @@ Perl_sv_dec(pTHX_ register SV *sv)
            sv_setiv(sv, i);
        }
     }
+    /* Unlike sv_inc we don't have to worry about string-never-numbers
+       and keeping them magic. But we mustn't warn on punting */
     flags = SvFLAGS(sv);
-    if (flags & SVp_NOK) {
-       SvNVX(sv) -= 1.0;
-       (void)SvNOK_only(sv);
-       return;
-    }
-    if (flags & SVp_IOK) {
+    if ((flags & SVf_IOK) || ((flags & (SVp_IOK | SVp_NOK)) == SVp_IOK)) {
+       /* It's publicly an integer, or privately an integer-not-float */
+      oops_its_int:
        if (SvIsUV(sv)) {
            if (SvUVX(sv) == 0) {
                (void)SvIOK_only(sv);
@@ -4730,6 +5449,11 @@ Perl_sv_dec(pTHX_ register SV *sv)
        }
        return;
     }
+    if (flags & SVp_NOK) {
+       SvNVX(sv) -= 1.0;
+       (void)SvNOK_only(sv);
+       return;
+    }
     if (!(flags & SVp_POK)) {
        if ((flags & SVTYPEMASK) < SVt_PVNV)
            sv_upgrade(sv, SVt_NV);
@@ -4737,6 +5461,40 @@ Perl_sv_dec(pTHX_ register SV *sv)
        (void)SvNOK_only(sv);
        return;
     }
+#ifdef PERL_PRESERVE_IVUV
+    {
+       int numtype = grok_number(SvPVX(sv), SvCUR(sv), NULL);
+       if (numtype && !(numtype & IS_NUMBER_INFINITY)) {
+           /* Need to try really hard to see if it's an integer.
+              9.22337203685478e+18 is an integer.
+              but "9.22337203685478e+18" + 0 is UV=9223372036854779904
+              so $a="9.22337203685478e+18"; $a+0; $a--
+              needs to be the same as $a="9.22337203685478e+18"; $a--
+              or we go insane. */
+       
+           (void) sv_2iv(sv);
+           if (SvIOK(sv))
+               goto oops_its_int;
+
+           /* sv_2iv *should* have made this an NV */
+           if (flags & SVp_NOK) {
+               (void)SvNOK_only(sv);
+               SvNVX(sv) -= 1.0;
+               return;
+           }
+           /* I don't think we can get here. Maybe I should assert this
+              And if we do get here I suspect that sv_setnv will croak. NWC
+              Fall through. */
+#if defined(USE_LONG_DOUBLE)
+           DEBUG_c(PerlIO_printf(Perl_debug_log,"sv_dec punt failed to convert '%s' to IOK or NOKp, UV=0x%"UVxf" NV=%"PERL_PRIgldbl"\n",
+                                 SvPVX(sv), SvIVX(sv), SvNVX(sv)));
+#else
+           DEBUG_c(PerlIO_printf(Perl_debug_log,"sv_dec punt failed to convert '%s' to IOK or NOKp, UV=0x%"UVxf" NV=%g\n",
+                                 SvPVX(sv), SvIVX(sv), SvNVX(sv)));
+#endif
+       }
+    }
+#endif /* PERL_PRESERVE_IVUV */
     sv_setnv(sv,Atof(SvPVX(sv)) - 1.0);        /* punt */
 }
 
@@ -4757,7 +5515,6 @@ as mortal.
 SV *
 Perl_sv_mortalcopy(pTHX_ SV *oldstr)
 {
-    dTHR;
     register SV *sv;
 
     new_SV(sv);
@@ -4779,7 +5536,6 @@ Creates a new SV which is mortal.  The reference count of the SV is set to 1.
 SV *
 Perl_sv_newmortal(pTHX)
 {
-    dTHR;
     register SV *sv;
 
     new_SV(sv);
@@ -4803,7 +5559,6 @@ ends.
 SV *
 Perl_sv_2mortal(pTHX_ register SV *sv)
 {
-    dTHR;
     if (!sv)
        return sv;
     if (SvREADONLY(sv) && SvIMMORTAL(sv))
@@ -4870,20 +5625,33 @@ will avoid string compare.
 */
 
 SV *
-Perl_newSVpvn_share(pTHX_ const char *src, STRLEN len, U32 hash)
+Perl_newSVpvn_share(pTHX_ const char *src, I32 len, U32 hash)
 {
     register SV *sv;
+    bool is_utf8 = FALSE;
+    if (len < 0) {
+        len = -len;
+        is_utf8 = TRUE;
+    }
+    if (is_utf8 && !(PL_hints & HINT_UTF8_DISTINCT)) {
+       STRLEN tmplen = len;
+       /* See the note in hv.c:hv_fetch() --jhi */
+       src = (char*)bytes_from_utf8((U8*)src, &tmplen, &is_utf8);
+       len = tmplen;
+    }
     if (!hash)
        PERL_HASH(hash, src, len);
     new_SV(sv);
     sv_upgrade(sv, SVt_PVIV);
-    SvPVX(sv) = sharepvn(src, len, hash);
+    SvPVX(sv) = sharepvn(src, is_utf8?-len:len, hash);
     SvCUR(sv) = len;
     SvUVX(sv) = hash;
     SvLEN(sv) = 0;
     SvREADONLY_on(sv);
     SvFAKE_on(sv);
     SvPOK_on(sv);
+    if (is_utf8)
+        SvUTF8_on(sv);
     return sv;
 }
 
@@ -4999,7 +5767,6 @@ SV is B<not> incremented.
 SV *
 Perl_newRV_noinc(pTHX_ SV *tmpRef)
 {
-    dTHR;
     register SV *sv;
 
     new_SV(sv);
@@ -5030,7 +5797,6 @@ Creates a new SV which is an exact duplicate of the original SV.
 SV *
 Perl_newSVsv(pTHX_ register SV *old)
 {
-    dTHR;
     register SV *sv;
 
     if (!old)
@@ -5113,7 +5879,7 @@ Perl_sv_reset(pTHX_ register char *s, HV *stash)
                }
                if (GvHV(gv) && !HvNAME(GvHV(gv))) {
                    hv_clear(GvHV(gv));
-#ifndef VMS  /* VMS has no environ array */
+#ifdef USE_ENVIRON_ARRAY
                    if (gv == PL_envgv)
                        environ[0] = Nullch;
 #endif
@@ -5185,7 +5951,6 @@ Perl_sv_2cv(pTHX_ SV *sv, HV **st, GV **gvp, I32 lref)
        if (SvGMAGICAL(sv))
            mg_get(sv);
        if (SvROK(sv)) {
-           dTHR;
            SV **sp = &sv;              /* Used in tryAMAGICunDEREF macro. */
            tryAMAGICunDEREF(to_cv);
 
@@ -5241,7 +6006,6 @@ Returns true if the SV has a true value by Perl's rules.
 I32
 Perl_sv_true(pTHX_ register SV *sv)
 {
-    dTHR;
     if (!sv)
        return 0;
     if (SvPOK(sv)) {
@@ -5327,6 +6091,23 @@ Get a sensible string out of the SV somehow.
 char *
 Perl_sv_pvn_force(pTHX_ SV *sv, STRLEN *lp)
 {
+    return sv_pvn_force_flags(sv, lp, SV_GMAGIC);
+}
+
+/*
+=for apidoc sv_pvn_force_flags
+
+Get a sensible string out of the SV somehow.
+If C<flags> has C<SV_GMAGIC> bit set, will C<mg_get> on C<sv> if
+appropriate, else not. C<sv_pvn_force> and C<sv_pvn_force_nomg> are
+implemented in terms of this function.
+
+=cut
+*/
+
+char *
+Perl_sv_pvn_force_flags(pTHX_ SV *sv, STRLEN *lp, I32 flags)
+{
     char *s;
 
     if (SvTHINKFIRST(sv) && !SvROK(sv))
@@ -5337,12 +6118,11 @@ Perl_sv_pvn_force(pTHX_ SV *sv, STRLEN *lp)
     }
     else {
        if (SvTYPE(sv) > SVt_PVLV && SvTYPE(sv) != SVt_PVFM) {
-           dTHR;
            Perl_croak(aTHX_ "Can't coerce %s to string in %s", sv_reftype(sv,0),
                PL_op_name[PL_op->op_type]);
        }
        else
-           s = sv_2pv(sv, lp);
+           s = sv_2pv_flags(sv, lp, flags);
        if (s != SvPVX(sv)) {   /* Almost, but not quite, sv_setpvn() */
            STRLEN len = *lp;
        
@@ -5367,18 +6147,21 @@ Perl_sv_pvn_force(pTHX_ SV *sv, STRLEN *lp)
 char *
 Perl_sv_pvbyte(pTHX_ SV *sv)
 {
+    sv_utf8_downgrade(sv,0);
     return sv_pv(sv);
 }
 
 char *
 Perl_sv_pvbyten(pTHX_ SV *sv, STRLEN *lp)
 {
+    sv_utf8_downgrade(sv,0);
     return sv_pvn(sv,lp);
 }
 
 char *
 Perl_sv_pvbyten_force(pTHX_ SV *sv, STRLEN *lp)
 {
+    sv_utf8_downgrade(sv,0);
     return sv_pvn_force(sv,lp);
 }
 
@@ -5517,7 +6300,6 @@ reference count is 1.
 SV*
 Perl_newSVrv(pTHX_ SV *rv, const char *classname)
 {
-    dTHR;
     SV *sv;
 
     new_SV(sv);
@@ -5604,6 +6386,25 @@ Perl_sv_setref_iv(pTHX_ SV *rv, const char *classname, IV iv)
 }
 
 /*
+=for apidoc sv_setref_uv
+
+Copies an unsigned integer into a new SV, optionally blessing the SV.  The C<rv>
+argument will be upgraded to an RV.  That RV will be modified to point to
+the new SV.  The C<classname> argument indicates the package for the
+blessing.  Set C<classname> to C<Nullch> to avoid the blessing.  The new SV
+will be returned and will have a reference count of 1.
+
+=cut
+*/
+
+SV*
+Perl_sv_setref_uv(pTHX_ SV *rv, const char *classname, UV uv)
+{
+    sv_setuv(newSVrv(rv,classname), uv);
+    return rv;
+}
+
+/*
 =for apidoc sv_setref_nv
 
 Copies a double into a new SV, optionally blessing the SV.  The C<rv>
@@ -5657,7 +6458,6 @@ of the SV is unaffected.
 SV*
 Perl_sv_bless(pTHX_ SV *sv, HV *stash)
 {
-    dTHR;
     SV *tmpRef;
     if (!SvROK(sv))
         Perl_croak(aTHX_ "Can't bless non-reference value");
@@ -5698,7 +6498,7 @@ S_sv_unglob(pTHX_ SV *sv)
        SvREFCNT_dec(GvSTASH(sv));
        GvSTASH(sv) = Nullhv;
     }
-    sv_unmagic(sv, '*');
+    sv_unmagic(sv, PERL_MAGIC_glob);
     Safefree(GvNAME(sv));
     GvMULTI_off(sv);
 
@@ -5713,17 +6513,21 @@ S_sv_unglob(pTHX_ SV *sv)
 }
 
 /*
-=for apidoc sv_unref
+=for apidoc sv_unref_flags
 
 Unsets the RV status of the SV, and decrements the reference count of
 whatever was being referenced by the RV.  This can almost be thought of
-as a reversal of C<newSVrv>.  See C<SvROK_off>.
+as a reversal of C<newSVrv>.  The C<cflags> argument can contain
+C<SV_IMMEDIATE_UNREF> to force the reference count to be decremented
+(otherwise the decrementing is conditional on the reference count being
+different from one or the reference being a readonly SV).
+See C<SvROK_off>.
 
 =cut
 */
 
 void
-Perl_sv_unref(pTHX_ SV *sv)
+Perl_sv_unref_flags(pTHX_ SV *sv, U32 flags)
 {
     SV* rv = SvRV(sv);
 
@@ -5735,23 +6539,40 @@ Perl_sv_unref(pTHX_ SV *sv)
     }
     SvRV(sv) = 0;
     SvROK_off(sv);
-    if (SvREFCNT(rv) != 1 || SvREADONLY(rv))
+    if (SvREFCNT(rv) != 1 || SvREADONLY(rv) || flags) /* SV_IMMEDIATE_UNREF */
        SvREFCNT_dec(rv);
-    else
+    else /* XXX Hack, but hard to make $a=$a->[1] work otherwise */
        sv_2mortal(rv);         /* Schedule for freeing later */
 }
 
+/*
+=for apidoc sv_unref
+
+Unsets the RV status of the SV, and decrements the reference count of
+whatever was being referenced by the RV.  This can almost be thought of
+as a reversal of C<newSVrv>.  This is C<sv_unref_flags> with the C<flag>
+being zero.  See C<SvROK_off>.
+
+=cut
+*/
+
+void
+Perl_sv_unref(pTHX_ SV *sv)
+{
+    sv_unref_flags(sv, 0);
+}
+
 void
 Perl_sv_taint(pTHX_ SV *sv)
 {
-    sv_magic((sv), Nullsv, 't', Nullch, 0);
+    sv_magic((sv), Nullsv, PERL_MAGIC_taint, Nullch, 0);
 }
 
 void
 Perl_sv_untaint(pTHX_ SV *sv)
 {
     if (SvTYPE(sv) >= SVt_PVMG && SvMAGIC(sv)) {
-       MAGIC *mg = mg_find(sv, 't');
+       MAGIC *mg = mg_find(sv, PERL_MAGIC_taint);
        if (mg)
            mg->mg_len &= ~1;
     }
@@ -5761,7 +6582,7 @@ bool
 Perl_sv_tainted(pTHX_ SV *sv)
 {
     if (SvTYPE(sv) >= SVt_PVMG && SvMAGIC(sv)) {
-       MAGIC *mg = mg_find(sv, 't');
+       MAGIC *mg = mg_find(sv, PERL_MAGIC_taint);
        if (mg && ((mg->mg_len & 1) || ((mg->mg_len & 2) && mg->mg_obj == sv)))
            return TRUE;
     }
@@ -5903,12 +6724,15 @@ Perl_sv_catpvf_mg_nocontext(SV *sv, const char* pat, ...)
 /*
 =for apidoc sv_catpvf
 
-Processes its arguments like C<sprintf> and appends the formatted output
-to an SV.  Handles 'get' magic, but not 'set' magic.  C<SvSETMAGIC()> must
-typically be called after calling this function to handle 'set' magic.
+Processes its arguments like C<sprintf> and appends the formatted
+output to an SV.  If the appended data contains "wide" characters
+(including, but not limited to, SVs with a UTF-8 PV formatted with %s,
+and characters >255 formatted with %c), the original SV might get
+upgraded to UTF-8.  Handles 'get' magic, but not 'set' magic.
+C<SvSETMAGIC()> must typically be called after calling this function
+to handle 'set' magic.
 
-=cut
-*/
+=cut */
 
 void
 Perl_sv_catpvf(pTHX_ SV *sv, const char* pat, ...)
@@ -5965,6 +6789,21 @@ Perl_sv_vsetpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
     sv_vcatpvfn(sv, pat, patlen, args, svargs, svmax, maybe_tainted);
 }
 
+STATIC I32
+S_expect_number(pTHX_ char** pattern)
+{
+    I32 var = 0;
+    switch (**pattern) {
+    case '1': case '2': case '3':
+    case '4': case '5': case '6':
+    case '7': case '8': case '9':
+       while (isDIGIT(**pattern))
+           var = var * 10 + (*(*pattern)++ - '0');
+    }
+    return var;
+}
+#define EXPECT_NUMBER(pattern, var) (var = S_expect_number(aTHX_ &pattern))
+
 /*
 =for apidoc sv_vcatpvfn
 
@@ -5980,14 +6819,13 @@ locales).
 void
 Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV **svargs, I32 svmax, bool *maybe_tainted)
 {
-    dTHR;
     char *p;
     char *q;
     char *patend;
     STRLEN origlen;
     I32 svix = 0;
     static char nullstr[] = "(null)";
-    SV *argsv;
+    SV *argsv = Nullsv;
 
     /* no matter what, this is a string now */
     (void)SvPV_force(sv, origlen);
@@ -6026,7 +6864,8 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
        bool alt = FALSE;
        bool left = FALSE;
        bool vectorize = FALSE;
-       bool utf = FALSE;
+       bool vectorarg = FALSE;
+       bool vec_utf = FALSE;
        char fill = ' ';
        char plus = 0;
        char intsize = 0;
@@ -6035,9 +6874,9 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
        bool has_precis = FALSE;
        STRLEN precis = 0;
        bool is_utf = FALSE;
-
+       
        char esignbuf[4];
-       U8 utf8buf[UTF8_MAXLEN];
+       U8 utf8buf[UTF8_MAXLEN+1];
        STRLEN esignlen = 0;
 
        char *eptr = Nullch;
@@ -6054,7 +6893,7 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
        STRLEN veclen = 0;
        char c;
        int i;
-       unsigned base;
+       unsigned base = 0;
        IV iv;
        UV uv;
        NV nv;
@@ -6063,7 +6902,13 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
        STRLEN gap;
        char *dotstr = ".";
        STRLEN dotstrlen = 1;
+       I32 efix = 0; /* explicit format parameter index */
+       I32 ewix = 0; /* explicit width index */
+       I32 epix = 0; /* explicit precision index */
+       I32 evix = 0; /* explicit vector index */
+       bool asterisk = FALSE;
 
+       /* echo everything up to the next format specification */
        for (q = p; q < patend && *q != '%'; ++q) ;
        if (q > p) {
            sv_catpvn(sv, p, q - p);
@@ -6072,6 +6917,25 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
        if (q++ >= patend)
            break;
 
+/*
+    We allow format specification elements in this order:
+       \d+\$              explicit format parameter index
+       [-+ 0#]+           flags
+       \*?(\d+\$)?v       vector with optional (optionally specified) arg
+       \d+|\*(\d+\$)?     width using optional (optionally specified) arg
+       \.(\d*|\*(\d+\$)?) precision using optional (optionally specified) arg
+       [hlqLV]            size
+    [%bcdefginopsux_DFOUX] format (mandatory)
+*/
+       if (EXPECT_NUMBER(q, width)) {
+           if (*q == '$') {
+               ++q;
+               efix = width;
+           } else {
+               goto gotwidth;
+           }
+       }
+
        /* FLAGS */
 
        while (*q) {
@@ -6095,65 +6959,88 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
                q++;
                continue;
 
-           case '*':                   /* printf("%*vX",":",$ipv6addr) */
-               if (q[1] != 'v')
-                   break;
-               q++;
-               if (args)
-                   vecsv = va_arg(*args, SV*);
-               else if (svix < svmax)
-                   vecsv = svargs[svix++];
-               else
-                   continue;
-               dotstr = SvPVx(vecsv,dotstrlen);
-               if (DO_UTF8(vecsv))
-                   is_utf = TRUE;
-               /* FALL THROUGH */
-
-           case 'v':
-               vectorize = TRUE;
-               q++;
-               continue;
-
            default:
                break;
            }
            break;
        }
 
-       /* WIDTH */
+      tryasterisk:
+       if (*q == '*') {
+           q++;
+           if (EXPECT_NUMBER(q, ewix))
+               if (*q++ != '$')
+                   goto unknown;
+           asterisk = TRUE;
+       }
+       if (*q == 'v') {
+           q++;
+           if (vectorize)
+               goto unknown;
+           if ((vectorarg = asterisk)) {
+               evix = ewix;
+               ewix = 0;
+               asterisk = FALSE;
+           }
+           vectorize = TRUE;
+           goto tryasterisk;
+       }
+
+       if (!asterisk)
+           EXPECT_NUMBER(q, width);
 
-       switch (*q) {
-       case '1': case '2': case '3':
-       case '4': case '5': case '6':
-       case '7': case '8': case '9':
-           width = 0;
-           while (isDIGIT(*q))
-               width = width * 10 + (*q++ - '0');
-           break;
+       if (vectorize) {
+           if (vectorarg) {
+               if (args)
+                   vecsv = va_arg(*args, SV*);
+               else
+                   vecsv = (evix ? evix <= svmax : svix < svmax) ?
+                       svargs[ewix ? ewix-1 : svix++] : &PL_sv_undef;
+               dotstr = SvPVx(vecsv, dotstrlen);
+               if (DO_UTF8(vecsv))
+                   is_utf = TRUE;
+           }
+           if (args) {
+               vecsv = va_arg(*args, SV*);
+               vecstr = (U8*)SvPVx(vecsv,veclen);
+               vec_utf = DO_UTF8(vecsv);
+           }
+           else if (efix ? efix <= svmax : svix < svmax) {
+               vecsv = svargs[efix ? efix-1 : svix++];
+               vecstr = (U8*)SvPVx(vecsv,veclen);
+               vec_utf = DO_UTF8(vecsv);
+           }
+           else {
+               vecstr = (U8*)"";
+               veclen = 0;
+           }
+       }
 
-       case '*':
+       if (asterisk) {
            if (args)
                i = va_arg(*args, int);
            else
-               i = (svix < svmax) ? SvIVx(svargs[svix++]) : 0;
+               i = (ewix ? ewix <= svmax : svix < svmax) ?
+                   SvIVx(svargs[ewix ? ewix-1 : svix++]) : 0;
            left |= (i < 0);
            width = (i < 0) ? -i : i;
-           q++;
-           break;
        }
+      gotwidth:
 
        /* PRECISION */
 
        if (*q == '.') {
            q++;
            if (*q == '*') {
+               q++;
+               if (EXPECT_NUMBER(q, epix) && *q++ != '$')
+                   goto unknown;
                if (args)
                    i = va_arg(*args, int);
                else
-                   i = (svix < svmax) ? SvIVx(svargs[svix++]) : 0;
+                   i = (ewix ? ewix <= svmax : svix < svmax)
+                       ? SvIVx(svargs[ewix ? ewix-1 : svix++]) : 0;
                precis = (i < 0) ? 0 : i;
-               q++;
            }
            else {
                precis = 0;
@@ -6163,23 +7050,6 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
            has_precis = TRUE;
        }
 
-       if (vectorize) {
-           if (args) {
-               vecsv = va_arg(*args, SV*);
-               vecstr = (U8*)SvPVx(vecsv,veclen);
-               utf = DO_UTF8(vecsv);
-           }
-           else if (svix < svmax) {
-               vecsv = svargs[svix++];
-               vecstr = (U8*)SvPVx(vecsv,veclen);
-               utf = DO_UTF8(vecsv);
-           }
-           else {
-               vecstr = (U8*)"";
-               veclen = 0;
-           }
-       }
-
        /* SIZE */
 
        switch (*q) {
@@ -6211,23 +7081,27 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
 
        /* CONVERSION */
 
+       if (*q == '%') {
+           eptr = q++;
+           elen = 1;
+           goto string;
+       }
+
+       if (!args)
+           argsv = (efix ? efix <= svmax : svix < svmax) ?
+                   svargs[efix ? efix-1 : svix++] : &PL_sv_undef;
+
        switch (c = *q++) {
 
            /* STRINGS */
 
-       case '%':
-           eptr = q - 1;
-           elen = 1;
-           goto string;
-
        case 'c':
-           if (args)
-               uv = va_arg(*args, int);
-           else
-               uv = (svix < svmax) ? SvIVx(svargs[svix++]) : 0;
-           if ((uv > 255 || (uv > 127 && SvUTF8(sv))) && !IN_BYTE) {
+           uv = args ? va_arg(*args, int) : SvIVx(argsv);
+           if ((uv > 255 ||
+                (!UNI_IS_INVARIANT(uv) && SvUTF8(sv)))
+               && !IN_BYTES) {
                eptr = (char*)utf8buf;
-               elen = uv_to_utf8((U8*)eptr, uv) - utf8buf;
+               elen = uvchr_to_utf8((U8*)eptr, uv) - utf8buf;
                is_utf = TRUE;
            }
            else {
@@ -6253,8 +7127,7 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
                    elen = sizeof nullstr - 1;
                }
            }
-           else if (svix < svmax) {
-               argsv = svargs[svix++];
+           else {
                eptr = SvPVx(argsv, elen);
                if (DO_UTF8(argsv)) {
                    if (has_precis && precis < elen) {
@@ -6278,7 +7151,7 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
             */
            if (!args)
                goto unknown;
-           argsv = va_arg(*args,SV*);
+           argsv = va_arg(*args, SV*);
            eptr = SvPVx(argsv, elen);
            if (DO_UTF8(argsv))
                is_utf = TRUE;
@@ -6294,10 +7167,7 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
        case 'p':
            if (alt)
                goto unknown;
-           if (args)
-               uv = PTR2UV(va_arg(*args, void*));
-           else
-               uv = (svix < svmax) ? PTR2UV(svargs[svix++]) : 0;
+           uv = PTR2UV(args ? va_arg(*args, void*) : argsv);
            base = 16;
            goto integer;
 
@@ -6311,13 +7181,11 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
        case 'd':
        case 'i':
            if (vectorize) {
-               I32 ulen;
-               if (!veclen) {
-                   vectorize = FALSE;
-                   break;
-               }
-               if (utf)
-                   iv = (IV)utf8_to_uv(vecstr, &ulen, 0);
+               STRLEN ulen;
+               if (!veclen)
+                   continue;
+               if (vec_utf)
+                   iv = (IV)utf8n_to_uvchr(vecstr, veclen, &ulen, 0);
                else {
                    iv = *vecstr;
                    ulen = 1;
@@ -6337,7 +7205,7 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
                }
            }
            else {
-               iv = (svix < svmax) ? SvIVx(svargs[svix++]) : 0;
+               iv = SvIVx(argsv);
                switch (intsize) {
                case 'h':       iv = (short)iv; break;
                default:        break;
@@ -6392,14 +7260,12 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
 
        uns_integer:
            if (vectorize) {
-               I32 ulen;
+               STRLEN ulen;
        vector:
-               if (!veclen) {
-                   vectorize = FALSE;
-                   break;
-               }
-               if (utf)
-                   uv = utf8_to_uv(vecstr, &ulen, 0);
+               if (!veclen)
+                   continue;
+               if (vec_utf)
+                   uv = utf8n_to_uvchr(vecstr, veclen, &ulen, 0);
                else {
                    uv = *vecstr;
                    ulen = 1;
@@ -6419,7 +7285,7 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
                }
            }
            else {
-               uv = (svix < svmax) ? SvUVx(svargs[svix++]) : 0;
+               uv = SvUVx(argsv);
                switch (intsize) {
                case 'h':       uv = (unsigned short)uv; break;
                default:        break;
@@ -6508,10 +7374,7 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
            /* This is evil, but floating point is even more evil */
 
            vectorize = FALSE;
-           if (args)
-               nv = va_arg(*args, NV);
-           else
-               nv = (svix < svmax) ? SvNVx(svargs[svix++]) : 0.0;
+           nv = args ? va_arg(*args, NV) : SvNVx(argsv);
 
            need = 0;
            if (c != 'e' && c != 'E') {
@@ -6566,15 +7429,10 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
                *--eptr = '#';
            *--eptr = '%';
 
-           {
-               STORE_NUMERIC_STANDARD_SET_LOCAL();
-#ifdef USE_LOCALE_NUMERIC
-               if (!was_standard && maybe_tainted)
-                   *maybe_tainted = TRUE;
-#endif
-               (void)sprintf(PL_efloatbuf, eptr, nv);
-               RESTORE_NUMERIC_STANDARD();
-           }
+           /* No taint.  Otherwise we are in the strange situation
+            * where printf() taints but print($float) doesn't.
+            * --jhi */
+           (void)sprintf(PL_efloatbuf, eptr, nv);
 
            eptr = PL_efloatbuf;
            elen = strlen(PL_efloatbuf);
@@ -6596,8 +7454,8 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
 #endif
                }
            }
-           else if (svix < svmax)
-               sv_setuv_mg(svargs[svix++], (UV)i);
+           else
+               sv_setuv_mg(argsv, (UV)i);
            continue;   /* not "break" */
 
            /* UNKNOWN */
@@ -6632,7 +7490,7 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
            /* ... right here, because formatting flags should not apply */
            SvGROW(sv, SvCUR(sv) + elen + 1);
            p = SvEND(sv);
-           memcpy(p, eptr, elen);
+           Copy(eptr, p, elen, char);
            p += elen;
            *p = '\0';
            SvCUR(sv) = p - SvPVX(sv);
@@ -6662,7 +7520,7 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
                *p++ = '0';
        }
        if (elen) {
-           memcpy(p, eptr, elen);
+           Copy(eptr, p, elen, char);
            p += elen;
        }
        if (gap && left) {
@@ -6671,7 +7529,7 @@ Perl_sv_vcatpvfn(pTHX_ SV *sv, const char *pat, STRLEN patlen, va_list *args, SV
        }
        if (vectorize) {
            if (veclen) {
-               memcpy(p, dotstr, dotstrlen);
+               Copy(dotstr, p, dotstrlen, char);
                p += dotstrlen;
            }
            else
@@ -6733,7 +7591,7 @@ Perl_fp_dup(pTHX_ PerlIO *fp, char type)
        return ret;
 
     /* create anew and remember what it is */
-    ret = PerlIO_fdupopen(fp);
+    ret = PerlIO_fdupopen(aTHX_ fp);
     ptr_table_store(PL_ptr_table, fp, ret);
     return ret;
 }
@@ -6781,8 +7639,8 @@ Perl_gp_dup(pTHX_ GP *gp)
 MAGIC *
 Perl_mg_dup(pTHX_ MAGIC *mg)
 {
-    MAGIC *mgret = (MAGIC*)NULL;
-    MAGIC *mgprev;
+    MAGIC *mgprev = (MAGIC*)NULL;
+    MAGIC *mgret;
     if (!mg)
        return (MAGIC*)NULL;
     /* look for it in the table first */
@@ -6793,15 +7651,15 @@ Perl_mg_dup(pTHX_ MAGIC *mg)
     for (; mg; mg = mg->mg_moremagic) {
        MAGIC *nmg;
        Newz(0, nmg, 1, MAGIC);
-       if (!mgret)
-           mgret = nmg;
-       else
+       if (mgprev)
            mgprev->mg_moremagic = nmg;
+       else
+           mgret = nmg;
        nmg->mg_virtual = mg->mg_virtual;       /* XXX copy dynamic vtable? */
        nmg->mg_private = mg->mg_private;
        nmg->mg_type    = mg->mg_type;
        nmg->mg_flags   = mg->mg_flags;
-       if (mg->mg_type == 'r') {
+       if (mg->mg_type == PERL_MAGIC_qr) {
            nmg->mg_obj = (SV*)re_dup((REGEXP*)mg->mg_obj);
        }
        else {
@@ -6811,10 +7669,12 @@ Perl_mg_dup(pTHX_ MAGIC *mg)
        }
        nmg->mg_len     = mg->mg_len;
        nmg->mg_ptr     = mg->mg_ptr;   /* XXX random ptr? */
-       if (mg->mg_ptr && mg->mg_type != 'g') {
+       if (mg->mg_ptr && mg->mg_type != PERL_MAGIC_regex_global) {
            if (mg->mg_len >= 0) {
                nmg->mg_ptr     = SAVEPVN(mg->mg_ptr, mg->mg_len);
-               if (mg->mg_type == 'c' && AMT_AMAGIC((AMT*)mg->mg_ptr)) {
+               if (mg->mg_type == PERL_MAGIC_overload_table &&
+                       AMT_AMAGIC((AMT*)mg->mg_ptr))
+               {
                    AMT *amtp = (AMT*)mg->mg_ptr;
                    AMT *namtp = (AMT*)nmg->mg_ptr;
                    I32 i;
@@ -6915,10 +7775,110 @@ Perl_ptr_table_split(pTHX_ PTR_TBL_t *tbl)
     }
 }
 
+void
+Perl_ptr_table_clear(pTHX_ PTR_TBL_t *tbl)
+{
+    register PTR_TBL_ENT_t **array;
+    register PTR_TBL_ENT_t *entry;
+    register PTR_TBL_ENT_t *oentry = Null(PTR_TBL_ENT_t*);
+    UV riter = 0;
+    UV max;
+
+    if (!tbl || !tbl->tbl_items) {
+        return;
+    }
+
+    array = tbl->tbl_ary;
+    entry = array[0];
+    max = tbl->tbl_max;
+
+    for (;;) {
+        if (entry) {
+            oentry = entry;
+            entry = entry->next;
+            Safefree(oentry);
+        }
+        if (!entry) {
+            if (++riter > max) {
+                break;
+            }
+            entry = array[riter];
+        }
+    }
+
+    tbl->tbl_items = 0;
+}
+
+void
+Perl_ptr_table_free(pTHX_ PTR_TBL_t *tbl)
+{
+    if (!tbl) {
+        return;
+    }
+    ptr_table_clear(tbl);
+    Safefree(tbl->tbl_ary);
+    Safefree(tbl);
+}
+
 #ifdef DEBUGGING
 char *PL_watch_pvx;
 #endif
 
+STATIC SV *
+S_gv_share(pTHX_ SV *sstr)
+{
+    GV *gv = (GV*)sstr;
+    SV *sv = &PL_sv_no; /* just need SvREADONLY-ness */
+
+    if (GvIO(gv) || GvFORM(gv)) {
+        GvSHARED_off(gv); /* GvIOs cannot be shared. nor can GvFORMs */
+    }
+    else if (!GvCV(gv)) {
+        GvCV(gv) = (CV*)sv;
+    }
+    else {
+        /* CvPADLISTs cannot be shared */
+        if (!CvXSUB(GvCV(gv))) {
+            GvSHARED_off(gv);
+        }
+    }
+
+    if (!GvSHARED(gv)) {
+#if 0
+        PerlIO_printf(Perl_debug_log, "gv_share: unable to share %s::%s\n",
+                      HvNAME(GvSTASH(gv)), GvNAME(gv));
+#endif
+        return Nullsv;
+    }
+
+    /*
+     * write attempts will die with
+     * "Modification of a read-only value attempted"
+     */
+    if (!GvSV(gv)) {
+        GvSV(gv) = sv;
+    }
+    else {
+        SvREADONLY_on(GvSV(gv));
+    }
+
+    if (!GvAV(gv)) {
+        GvAV(gv) = (AV*)sv;
+    }
+    else {
+        SvREADONLY_on(GvAV(gv));
+    }
+
+    if (!GvHV(gv)) {
+        GvHV(gv) = (HV*)sv;
+    }
+    else {
+        SvREADONLY_on(GvAV(gv));
+    }
+
+    return sstr; /* he_dup() will SvREFCNT_inc() */
+}
+
 SV *
 Perl_sv_dup(pTHX_ SV *sstr)
 {
@@ -6960,14 +7920,18 @@ Perl_sv_dup(pTHX_ SV *sstr)
        break;
     case SVt_RV:
        SvANY(dstr)     = new_XRV();
-       SvRV(dstr)      = sv_dup_inc(SvRV(sstr));
+       SvRV(dstr)      = SvRV(sstr) && SvWEAKREF(SvRV(sstr))
+                       ? sv_dup(SvRV(sstr))
+                       : sv_dup_inc(SvRV(sstr));
        break;
     case SVt_PV:
        SvANY(dstr)     = new_XPV();
        SvCUR(dstr)     = SvCUR(sstr);
        SvLEN(dstr)     = SvLEN(sstr);
        if (SvROK(sstr))
-           SvRV(dstr)  = sv_dup_inc(SvRV(sstr));
+           SvRV(dstr)  = SvWEAKREF(SvRV(sstr))
+                       ? sv_dup(SvRV(sstr))
+                       : sv_dup_inc(SvRV(sstr));
        else if (SvPVX(sstr) && SvLEN(sstr))
            SvPVX(dstr) = SAVEPVN(SvPVX(sstr), SvLEN(sstr)-1);
        else
@@ -6979,7 +7943,9 @@ Perl_sv_dup(pTHX_ SV *sstr)
        SvLEN(dstr)     = SvLEN(sstr);
        SvIVX(dstr)     = SvIVX(sstr);
        if (SvROK(sstr))
-           SvRV(dstr)  = sv_dup_inc(SvRV(sstr));
+           SvRV(dstr)  = SvWEAKREF(SvRV(sstr))
+                       ? sv_dup(SvRV(sstr))
+                       : sv_dup_inc(SvRV(sstr));
        else if (SvPVX(sstr) && SvLEN(sstr))
            SvPVX(dstr) = SAVEPVN(SvPVX(sstr), SvLEN(sstr)-1);
        else
@@ -6992,7 +7958,9 @@ Perl_sv_dup(pTHX_ SV *sstr)
        SvIVX(dstr)     = SvIVX(sstr);
        SvNVX(dstr)     = SvNVX(sstr);
        if (SvROK(sstr))
-           SvRV(dstr)  = sv_dup_inc(SvRV(sstr));
+           SvRV(dstr)  = SvWEAKREF(SvRV(sstr))
+                       ? sv_dup(SvRV(sstr))
+                       : sv_dup_inc(SvRV(sstr));
        else if (SvPVX(sstr) && SvLEN(sstr))
            SvPVX(dstr) = SAVEPVN(SvPVX(sstr), SvLEN(sstr)-1);
        else
@@ -7007,7 +7975,9 @@ Perl_sv_dup(pTHX_ SV *sstr)
        SvMAGIC(dstr)   = mg_dup(SvMAGIC(sstr));
        SvSTASH(dstr)   = hv_dup_inc(SvSTASH(sstr));
        if (SvROK(sstr))
-           SvRV(dstr)  = sv_dup_inc(SvRV(sstr));
+           SvRV(dstr)  = SvWEAKREF(SvRV(sstr))
+                       ? sv_dup(SvRV(sstr))
+                       : sv_dup_inc(SvRV(sstr));
        else if (SvPVX(sstr) && SvLEN(sstr))
            SvPVX(dstr) = SAVEPVN(SvPVX(sstr), SvLEN(sstr)-1);
        else
@@ -7022,7 +7992,9 @@ Perl_sv_dup(pTHX_ SV *sstr)
        SvMAGIC(dstr)   = mg_dup(SvMAGIC(sstr));
        SvSTASH(dstr)   = hv_dup_inc(SvSTASH(sstr));
        if (SvROK(sstr))
-           SvRV(dstr)  = sv_dup_inc(SvRV(sstr));
+           SvRV(dstr)  = SvWEAKREF(SvRV(sstr))
+                       ? sv_dup(SvRV(sstr))
+                       : sv_dup_inc(SvRV(sstr));
        else if (SvPVX(sstr) && SvLEN(sstr))
            SvPVX(dstr) = SAVEPVN(SvPVX(sstr), SvLEN(sstr)-1);
        else
@@ -7040,7 +8012,9 @@ Perl_sv_dup(pTHX_ SV *sstr)
        SvMAGIC(dstr)   = mg_dup(SvMAGIC(sstr));
        SvSTASH(dstr)   = hv_dup_inc(SvSTASH(sstr));
        if (SvROK(sstr))
-           SvRV(dstr)  = sv_dup_inc(SvRV(sstr));
+           SvRV(dstr)  = SvWEAKREF(SvRV(sstr))
+                       ? sv_dup(SvRV(sstr))
+                       : sv_dup_inc(SvRV(sstr));
        else if (SvPVX(sstr) && SvLEN(sstr))
            SvPVX(dstr) = SAVEPVN(SvPVX(sstr), SvLEN(sstr)-1);
        else
@@ -7051,6 +8025,18 @@ Perl_sv_dup(pTHX_ SV *sstr)
        LvTYPE(dstr)    = LvTYPE(sstr);
        break;
     case SVt_PVGV:
+       if (GvSHARED((GV*)sstr)) {
+            SV *share;
+            if ((share = gv_share(sstr))) {
+                del_SV(dstr);
+                dstr = share;
+#if 0
+                PerlIO_printf(Perl_debug_log, "sv_dup: sharing %s::%s\n",
+                              HvNAME(GvSTASH(share)), GvNAME(share));
+#endif
+                break;
+            }
+       }
        SvANY(dstr)     = new_XPVGV();
        SvCUR(dstr)     = SvCUR(sstr);
        SvLEN(dstr)     = SvLEN(sstr);
@@ -7059,7 +8045,9 @@ Perl_sv_dup(pTHX_ SV *sstr)
        SvMAGIC(dstr)   = mg_dup(SvMAGIC(sstr));
        SvSTASH(dstr)   = hv_dup_inc(SvSTASH(sstr));
        if (SvROK(sstr))
-           SvRV(dstr)  = sv_dup_inc(SvRV(sstr));
+           SvRV(dstr)  = SvWEAKREF(SvRV(sstr))
+                       ? sv_dup(SvRV(sstr))
+                       : sv_dup_inc(SvRV(sstr));
        else if (SvPVX(sstr) && SvLEN(sstr))
            SvPVX(dstr) = SAVEPVN(SvPVX(sstr), SvLEN(sstr)-1);
        else
@@ -7080,7 +8068,9 @@ Perl_sv_dup(pTHX_ SV *sstr)
        SvMAGIC(dstr)   = mg_dup(SvMAGIC(sstr));
        SvSTASH(dstr)   = hv_dup_inc(SvSTASH(sstr));
        if (SvROK(sstr))
-           SvRV(dstr)  = sv_dup_inc(SvRV(sstr));
+           SvRV(dstr)  = SvWEAKREF(SvRV(sstr))
+                       ? sv_dup(SvRV(sstr))
+                       : sv_dup_inc(SvRV(sstr));
        else if (SvPVX(sstr) && SvLEN(sstr))
            SvPVX(dstr) = SAVEPVN(SvPVX(sstr), SvLEN(sstr)-1);
        else
@@ -7174,6 +8164,8 @@ Perl_sv_dup(pTHX_ SV *sstr)
        }
        HvPMROOT((HV*)dstr)     = HvPMROOT((HV*)sstr);          /* XXX */
        HvNAME((HV*)dstr)       = SAVEPV(HvNAME((HV*)sstr));
+       if(HvNAME((HV*)dstr))
+           av_push(PL_clone_callbacks, dstr);
        break;
     case SVt_PVFM:
        SvANY(dstr)     = new_XPVFM();
@@ -7198,7 +8190,7 @@ dup_pvcv:
        CvROOT(dstr)    = OpREFCNT_inc(CvROOT(sstr));
        CvXSUB(dstr)    = CvXSUB(sstr);
        CvXSUBANY(dstr) = CvXSUBANY(sstr);
-       CvGV(dstr)      = gv_dup_inc(CvGV(sstr));
+       CvGV(dstr)      = gv_dup(CvGV(sstr));
        CvDEPTH(dstr)   = CvDEPTH(sstr);
        if (CvPADLIST(sstr) && !AvREAL(CvPADLIST(sstr))) {
            /* XXX padlists are real, but pretend to be not */
@@ -7209,7 +8201,10 @@ dup_pvcv:
        }
        else
            CvPADLIST(dstr)     = av_dup_inc(CvPADLIST(sstr));
-       CvOUTSIDE(dstr) = cv_dup_inc(CvOUTSIDE(sstr));
+       if (!CvANON(sstr) || CvCLONED(sstr))
+           CvOUTSIDE(dstr)     = cv_dup_inc(CvOUTSIDE(sstr));
+       else
+           CvOUTSIDE(dstr)     = cv_dup(CvOUTSIDE(sstr));
        CvFLAGS(dstr)   = CvFLAGS(sstr);
        break;
     default:
@@ -7263,7 +8258,7 @@ Perl_cx_dup(pTHX_ PERL_CONTEXT *cxs, I32 ix, I32 max)
                ncx->blk_sub.argarray   = (cx->blk_sub.hasargs
                                           ? av_dup_inc(cx->blk_sub.argarray)
                                           : Nullav);
-               ncx->blk_sub.savearray  = av_dup(cx->blk_sub.savearray);
+               ncx->blk_sub.savearray  = av_dup_inc(cx->blk_sub.savearray);
                ncx->blk_sub.olddepth   = cx->blk_sub.olddepth;
                ncx->blk_sub.hasargs    = cx->blk_sub.hasargs;
                ncx->blk_sub.lval       = cx->blk_sub.lval;
@@ -7517,6 +8512,7 @@ Perl_ss_dup(pTHX_ PerlInterpreter *proto_perl)
            TOPIV(nss,ix) = iv;
             break;
        case SAVEt_FREESV:
+       case SAVEt_MORTALIZESV:
            sv = (SV*)POPPTR(ss,ix);
            TOPPTR(nss,ix) = sv_dup_inc(sv);
            break;
@@ -7609,6 +8605,14 @@ Perl_ss_dup(pTHX_ PerlInterpreter *proto_perl)
            av = (AV*)POPPTR(ss,ix);
            TOPPTR(nss,ix) = av_dup(av);
            break;
+       case SAVEt_PADSV:
+           longval = (long)POPLONG(ss,ix);
+           TOPLONG(nss,ix) = longval;
+           ptr = POPPTR(ss,ix);
+           TOPPTR(nss,ix) = any_dup(ptr, proto_perl);
+           sv = (SV*)POPPTR(ss,ix);
+           TOPPTR(nss,ix) = sv_dup(sv);
+           break;
        default:
            Perl_croak(aTHX_ "panic: ss_dup inconsistency");
        }
@@ -7668,6 +8672,7 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
     PL_scopestack = 0;
     PL_savestack = 0;
     PL_retstack = 0;
+    PL_sig_pending = 0;
 #    else      /* !DEBUGGING */
     Zero(my_perl, 1, PerlInterpreter);
 #    endif     /* DEBUGGING */
@@ -7694,6 +8699,7 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
     PL_scopestack = 0;
     PL_savestack = 0;
     PL_retstack = 0;
+    PL_sig_pending = 0;
 #    else      /* !DEBUGGING */
     Zero(my_perl, 1, PerlInterpreter);
 #    endif     /* DEBUGGING */
@@ -7782,6 +8788,8 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
     ptr_table_store(PL_ptr_table, &proto_perl->Icompiling, &PL_compiling);
     if (!specialWARN(PL_compiling.cop_warnings))
        PL_compiling.cop_warnings = sv_dup_inc(PL_compiling.cop_warnings);
+    if (!specialCopIO(PL_compiling.cop_io))
+       PL_compiling.cop_io = sv_dup_inc(PL_compiling.cop_io);
     PL_curcop          = (COP*)any_dup(proto_perl->Tcurcop, proto_perl);
 
     /* pseudo environmental stuff */
@@ -7792,6 +8800,7 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
     while (i-- > 0) {
        PL_origargv[i]  = SAVEPV(proto_perl->Iorigargv[i]);
     }
+    PL_clone_callbacks = newAV();   /* Setup array of objects to callbackon */
     PL_envgv           = gv_dup(proto_perl->Ienvgv);
     PL_incgv           = gv_dup(proto_perl->Iincgv);
     PL_hintgv          = gv_dup(proto_perl->Ihintgv);
@@ -7838,7 +8847,7 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
     PL_defgv           = gv_dup(proto_perl->Idefgv);
     PL_argvgv          = gv_dup(proto_perl->Iargvgv);
     PL_argvoutgv       = gv_dup(proto_perl->Iargvoutgv);
-    PL_argvout_stack   = av_dup(proto_perl->Iargvout_stack);
+    PL_argvout_stack   = av_dup_inc(proto_perl->Iargvout_stack);
 
     /* shortcuts to regexp stuff */
     PL_replgv          = gv_dup(proto_perl->Ireplgv);
@@ -7907,8 +8916,7 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
     PL_laststype       = proto_perl->Ilaststype;
     PL_mess_sv         = Nullsv;
 
-    PL_orslen          = proto_perl->Iorslen;
-    PL_ors             = SAVEPVN(proto_perl->Iors, PL_orslen);
+    PL_ors_sv          = sv_dup_inc(proto_perl->Iors_sv);
     PL_ofmt            = SAVEPV(proto_perl->Iofmt);
 
     /* interpreter atexit processing */
@@ -8050,7 +9058,7 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
     PL_numeric_name    = SAVEPV(proto_perl->Inumeric_name);
     PL_numeric_standard        = proto_perl->Inumeric_standard;
     PL_numeric_local   = proto_perl->Inumeric_local;
-    PL_numeric_radix   = proto_perl->Inumeric_radix;
+    PL_numeric_radix_sv        = sv_dup_inc(proto_perl->Inumeric_radix_sv);
 #endif /* !USE_LOCALE_NUMERIC */
 
     /* utf8 character classes */
@@ -8092,12 +9100,18 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
     PL_uudmap['M']     = 0;            /* reinits on demand */
     PL_bitcount                = Nullch;       /* reinits on demand */
 
+    if (proto_perl->Ipsig_pend) {
+       Newz(0, PL_psig_pend, SIG_SIZE, int);
+    }
+    else {
+       PL_psig_pend    = (int*)NULL;
+    }
+
     if (proto_perl->Ipsig_ptr) {
-       int sig_num[] = { SIG_NUM };
-       Newz(0, PL_psig_ptr, sizeof(sig_num)/sizeof(*sig_num), SV*);
-       Newz(0, PL_psig_name, sizeof(sig_num)/sizeof(*sig_num), SV*);
-       for (i = 1; PL_sig_name[i]; i++) {
-           PL_psig_ptr[i] = sv_dup_inc(proto_perl->Ipsig_ptr[i]);
+       Newz(0, PL_psig_ptr,  SIG_SIZE, SV*);
+       Newz(0, PL_psig_name, SIG_SIZE, SV*);
+       for (i = 1; i < SIG_SIZE; i++) {
+           PL_psig_ptr[i]  = sv_dup_inc(proto_perl->Ipsig_ptr[i]);
            PL_psig_name[i] = sv_dup_inc(proto_perl->Ipsig_name[i]);
        }
     }
@@ -8108,7 +9122,7 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
 
     /* thrdvar.h stuff */
 
-    if (flags & 1) {
+    if (flags & CLONEf_COPY_STACKS) {
        /* next allocation will be PL_tmps_stack[PL_tmps_ix+1] */
        PL_tmps_ix              = proto_perl->Ttmps_ix;
        PL_tmps_max             = proto_perl->Ttmps_max;
@@ -8191,8 +9205,7 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
     PL_nrs             = sv_dup_inc(proto_perl->Tnrs);
     PL_rs              = sv_dup_inc(proto_perl->Trs);
     PL_last_in_gv      = gv_dup(proto_perl->Tlast_in_gv);
-    PL_ofslen          = proto_perl->Tofslen;
-    PL_ofs             = SAVEPVN(proto_perl->Tofs, PL_ofslen);
+    PL_ofs_sv          = sv_dup_inc(proto_perl->Tofs_sv);
     PL_defoutgv                = gv_dup_inc(proto_perl->Tdefoutgv);
     PL_chopset         = proto_perl->Tchopset; /* XXX never deallocated */
     PL_toptarget       = sv_dup_inc(proto_perl->Ttoptarget);
@@ -8259,7 +9272,6 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
     PL_regendp         = (I32*)NULL;
     PL_reglastparen    = (U32*)NULL;
     PL_regtill         = Nullch;
-    PL_regprev         = '\n';
     PL_reg_start_tmp   = (char**)NULL;
     PL_reg_start_tmpl  = 0;
     PL_regdata         = (struct reg_data*)NULL;
@@ -8295,6 +9307,29 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags,
     PL_reginterp_cnt   = 0;
     PL_reg_starttry    = 0;
 
+    if (!(flags & CLONEf_KEEP_PTR_TABLE)) {
+        ptr_table_free(PL_ptr_table);
+        PL_ptr_table = NULL;
+    }
+    
+    while(av_len(PL_clone_callbacks) != -1) {
+        HV* stash = (HV*) av_shift(PL_clone_callbacks);
+        CV* cloner = (CV*) gv_fetchmethod_autoload(stash,"CLONE",0);
+        if(cloner) {
+            dSP;
+            cloner = GvCV(cloner);
+            ENTER;
+            SAVETMPS;
+            PUSHMARK(SP);
+            XPUSHs(newSVpv(HvNAME(stash),0));
+            PUTBACK;
+            call_sv((SV*)cloner, G_DISCARD);
+            FREETMPS;
+            LEAVE;
+            
+        }
+    }
+
 #ifdef PERL_OBJECT
     return (PerlInterpreter*)pPerl;
 #else
@@ -8325,7 +9360,7 @@ do_clean_objs(pTHXo_ SV *sv)
     SV* rv;
 
     if (SvROK(sv) && SvOBJECT(rv = SvRV(sv))) {
-       DEBUG_D((PerlIO_printf(Perl_debug_log, "Cleaning object ref:\n "), sv_dump(sv));)
+       DEBUG_D((PerlIO_printf(Perl_debug_log, "Cleaning object ref:\n "), sv_dump(sv)));
        if (SvWEAKREF(sv)) {
            sv_del_backref(sv);
            SvWEAKREF_off(sv);
@@ -8351,7 +9386,7 @@ do_clean_named_objs(pTHXo_ SV *sv)
             (GvIO(sv) && SvOBJECT(GvIO(sv))) ||
             (GvCV(sv) && SvOBJECT(GvCV(sv))) )
        {
-           DEBUG_D((PerlIO_printf(Perl_debug_log, "Cleaning named glob object:\n "), sv_dump(sv));)
+           DEBUG_D((PerlIO_printf(Perl_debug_log, "Cleaning named glob object:\n "), sv_dump(sv)));
            SvREFCNT_dec(sv);
        }
     }
@@ -8361,7 +9396,7 @@ do_clean_named_objs(pTHXo_ SV *sv)
 static void
 do_clean_all(pTHXo_ SV *sv)
 {
-    DEBUG_D((PerlIO_printf(Perl_debug_log, "Cleaning loops: SV at 0x%"UVxf"\n", PTR2UV(sv)) );)
+    DEBUG_D((PerlIO_printf(Perl_debug_log, "Cleaning loops: SV at 0x%"UVxf"\n", PTR2UV(sv)) ));
     SvFLAGS(sv) |= SVf_BREAK;
     SvREFCNT_dec(sv);
 }