X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=pp.c;h=ae8cb6fdb88c2147ef2b95a40d6b1b14b9f11fab;hb=1cc8b4c566f7901a54e4b576f09608beb4c81f86;hp=e470d1c411e791b0a5241172549685291e6a4375;hpb=d9f424b23bb434af43f899daf2cb6cfe42fe6e1a;p=p5sagit%2Fp5-mst-13.2.git diff --git a/pp.c b/pp.c index e470d1c..ae8cb6f 100644 --- a/pp.c +++ b/pp.c @@ -999,29 +999,111 @@ PP(pp_multiply) PP(pp_divide) { dSP; dATARGET; tryAMAGICbin(div,opASSIGN); - { - dPOPPOPnnrl; - NV value; - if (right == 0.0) - DIE(aTHX_ "Illegal division by zero"); + /* Only try to do UV divide first + if ((SLOPPYDIVIDE is true) or + (PERL_PRESERVE_IVUV is true and one or both SV is a UV too large + to preserve)) + The assumption is that it is better to use floating point divide + whenever possible, only doing integer divide first if we can't be sure. + If NV_PRESERVES_UV is true then we know at compile time that no UV + can be too large to preserve, so don't need to compile the code to + test the size of UVs. */ + #ifdef SLOPPYDIVIDE - /* insure that 20./5. == 4. */ - { - IV k; - if ((NV)I_V(left) == left && - (NV)I_V(right) == right && - (k = I_V(left)/I_V(right))*I_V(right) == I_V(left)) { - value = k; - } - else { - value = left / right; - } - } +# define PERL_TRY_UV_DIVIDE + /* ensure that 20./5. == 4. */ #else - value = left / right; +# ifdef PERL_PRESERVE_IVUV +# ifndef NV_PRESERVES_UV +# define PERL_TRY_UV_DIVIDE +# endif +# endif #endif - PUSHn( value ); - RETURN; + +#ifdef PERL_TRY_UV_DIVIDE + SvIV_please(TOPs); + if (SvIOK(TOPs)) { + SvIV_please(TOPm1s); + if (SvIOK(TOPm1s)) { + bool left_non_neg = SvUOK(TOPm1s); + bool right_non_neg = SvUOK(TOPs); + UV left; + UV right; + + if (right_non_neg) { + right = SvUVX(TOPs); + } + else { + IV biv = SvIVX(TOPs); + if (biv >= 0) { + right = biv; + right_non_neg = TRUE; /* effectively it's a UV now */ + } + else { + right = -biv; + } + } + /* historically undef()/0 gives a "Use of uninitialized value" + warning before dieing, hence this test goes here. + If it were immediately before the second SvIV_please, then + DIE() would be invoked before left was even inspected, so + no inpsection would give no warning. */ + if (right == 0) + DIE(aTHX_ "Illegal division by zero"); + + if (left_non_neg) { + left = SvUVX(TOPm1s); + } + else { + IV aiv = SvIVX(TOPm1s); + if (aiv >= 0) { + left = aiv; + left_non_neg = TRUE; /* effectively it's a UV now */ + } + else { + left = -aiv; + } + } + + if (left >= right +#ifdef SLOPPYDIVIDE + /* For sloppy divide we always attempt integer division. */ +#else + /* Otherwise we only attempt it if either or both operands + would not be preserved by an NV. If both fit in NVs + we fall through to the NV divide code below. */ + && ((left > ((UV)1 << NV_PRESERVES_UV_BITS)) + || (right > ((UV)1 << NV_PRESERVES_UV_BITS))) +#endif + ) { + /* Integer division can't overflow, but it can be imprecise. */ + UV result = left / right; + if (result * right == left) { + SP--; /* result is valid */ + if (left_non_neg == right_non_neg) { + /* signs identical, result is positive. */ + SETu( result ); + RETURN; + } + /* 2s complement assumption */ + if (result <= (UV)IV_MIN) + SETi( -result ); + else { + /* It's exact but too negative for IV. */ + SETn( -(NV)result ); + } + RETURN; + } /* tried integer divide but it was not an integer result */ + } /* else (abs(result) < 1.0) or (both UVs in range for NV) */ + } /* left wasn't SvIOK */ + } /* right wasn't SvIOK */ +#endif /* PERL_TRY_UV_DIVIDE */ + { + dPOPPOPnnrl; + if (right == 0.0) + DIE(aTHX_ "Illegal division by zero"); + PUSHn( left / right ); + RETURN; } } @@ -4065,11 +4147,11 @@ PP(pp_split) #endif } else if (gimme != G_ARRAY) -#ifdef USE_THREADS +#ifdef USE_5005THREADS ary = (AV*)PL_curpad[0]; #else ary = GvAVn(PL_defgv); -#endif /* USE_THREADS */ +#endif /* USE_5005THREADS */ else ary = Nullav; if (ary && (gimme != G_ARRAY || (pm->op_pmflags & PMf_ONCE))) { @@ -4325,7 +4407,7 @@ PP(pp_split) RETPUSHUNDEF; } -#ifdef USE_THREADS +#ifdef USE_5005THREADS void Perl_unlock_condpair(pTHX_ void *svv) { @@ -4342,16 +4424,16 @@ Perl_unlock_condpair(pTHX_ void *svv) PTR2UV(thr), PTR2UV(svv))); MUTEX_UNLOCK(MgMUTEXP(mg)); } -#endif /* USE_THREADS */ +#endif /* USE_5005THREADS */ PP(pp_lock) { dSP; dTOPss; SV *retsv = sv; -#ifdef USE_THREADS +#ifdef USE_5005THREADS sv_lock(sv); -#endif /* USE_THREADS */ +#endif /* USE_5005THREADS */ #ifdef USE_ITHREADS shared_sv *ssv = Perl_sharedsv_find(aTHX_ sv); if(ssv) @@ -4367,7 +4449,7 @@ PP(pp_lock) PP(pp_threadsv) { -#ifdef USE_THREADS +#ifdef USE_5005THREADS dSP; EXTEND(SP, 1); if (PL_op->op_private & OPpLVAL_INTRO) @@ -4377,5 +4459,5 @@ PP(pp_threadsv) RETURN; #else DIE(aTHX_ "tried to access per-thread data in non-threaded perl"); -#endif /* USE_THREADS */ +#endif /* USE_5005THREADS */ }