diff options
author | Nicholas Clark <nick@ccl4.org> | 2021-04-25 20:03:37 +0000 |
---|---|---|
committer | Ricardo Signes <rjbs@semiotic.systems> | 2021-10-15 09:28:26 -0400 |
commit | c52d5e029a9e8dc660331b57193ea652aa0f2beb (patch) | |
tree | 16d1954b942847f9251a1749fdb07037214bb0ba /pp_hot.c | |
parent | 525dc1e45d4c3a81deef4481ad38a8e49380b753 (diff) | |
download | perl-c52d5e029a9e8dc660331b57193ea652aa0f2beb.tar.gz |
Iterate for loops $n-at-a-time in PP_ITER.
This commit provides the runtime changes needed to iterate for loops over
two or more variables.
Diffstat (limited to 'pp_hot.c')
-rw-r--r-- | pp_hot.c | 92 |
1 files changed, 77 insertions, 15 deletions
@@ -3906,11 +3906,25 @@ PP(pp_iter) IV ix; IV inc; + /* Classic "for" syntax iterates one-at-a-time. + Many-at-a-time for loops are only for lexicals declared as part of the + for loop, and rely on all the lexicals being in adjacent pad slots. + + Curiously, even if the iterator variable is a lexical, the pad offset is + stored in the targ slot of the ENTERITER op, meaning that targ of this OP + has always been zero. Hence we can use this op's targ to hold "how many" + for many-at-a-time. We actually store C<how_many - 1>, so that for the + case of one-at-a-time we have zero (as before), as this makes all the + logic of the for loop below much simpler, with all the other + one-at-a-time cases just falling out of this "naturally". */ + PADOFFSET how_many = PL_op->op_targ; + PADOFFSET i = 0; + cx = CX_CUR(); itersvp = CxITERVAR(cx); assert(itersvp); - { + for (; i <= how_many; ++i ) { switch (CxTYPE(cx)) { case CXt_LOOP_LAZYSV: /* string increment */ @@ -3921,17 +3935,35 @@ PP(pp_iter) It has SvPVX of "" and SvCUR of 0, which is what we want. */ STRLEN maxlen = 0; const char *max = SvPV_const(end, maxlen); + bool pad_it = FALSE; if (DO_UTF8(end) && IN_UNI_8_BIT) maxlen = sv_len_utf8_nomg(end); - if (UNLIKELY(SvNIOK(cur) || SvCUR(cur) > maxlen)) - goto retno; + if (UNLIKELY(SvNIOK(cur) || SvCUR(cur) > maxlen)) { + if (LIKELY(!i)) { + goto retno; + } + /* We are looping n-at-a-time and the range isn't a multiple + of n, so we fill the rest of the lexicals with undef. + This only happens on the last iteration of the loop, and + we will have already set up the "terminate next time" + condition earlier in this for loop for this call of the + ITER op when we set up the lexical corresponding to the + last value in the range. Hence we don't goto retno (yet), + and just below we don't repeat the setup for "terminate + next time". */ + pad_it = TRUE; + } oldsv = *itersvp; /* NB: on the first iteration, oldsv will have a ref count of at * least 2 (one extra from blk_loop.itersave), so the GV or pad * slot will get localised; on subsequent iterations the RC==1 * optimisation may kick in and the SV will be reused. */ - if (oldsv && LIKELY(SvREFCNT(oldsv) == 1 && !SvMAGICAL(oldsv))) { + if (UNLIKELY(pad_it)) { + *itersvp = &PL_sv_undef; + SvREFCNT_dec(oldsv); + } + else if (oldsv && LIKELY(SvREFCNT(oldsv) == 1 && !SvMAGICAL(oldsv))) { /* safe to reuse old SV */ sv_setsv(oldsv, cur); } @@ -3942,7 +3974,10 @@ PP(pp_iter) *itersvp = newSVsv(cur); SvREFCNT_dec(oldsv); } - if (strEQ(SvPVX_const(cur), max)) + + if (UNLIKELY(pad_it)) { + } + else if (strEQ(SvPVX_const(cur), max)) sv_setiv(cur, 0); /* terminate next time */ else sv_inc(cur); @@ -3952,12 +3987,21 @@ PP(pp_iter) case CXt_LOOP_LAZYIV: /* integer increment */ { IV cur = cx->blk_loop.state_u.lazyiv.cur; - if (UNLIKELY(cur > cx->blk_loop.state_u.lazyiv.end)) - goto retno; + bool pad_it = FALSE; + if (UNLIKELY(cur > cx->blk_loop.state_u.lazyiv.end)) { + if (LIKELY(!i)) { + goto retno; + } + pad_it = TRUE; + } oldsv = *itersvp; /* see NB comment above */ - if (oldsv && LIKELY(SvREFCNT(oldsv) == 1 && !SvMAGICAL(oldsv))) { + if (UNLIKELY(pad_it)) { + *itersvp = &PL_sv_undef; + SvREFCNT_dec(oldsv); + } + else if (oldsv && LIKELY(SvREFCNT(oldsv) == 1 && !SvMAGICAL(oldsv))) { /* safe to reuse old SV */ if ( (SvFLAGS(oldsv) & (SVTYPEMASK|SVf_THINKFIRST|SVf_IVisUV)) @@ -3970,6 +4014,7 @@ PP(pp_iter) SvFLAGS(oldsv) |= (SVf_IOK|SVp_IOK); /* SvIV_set() where sv_any points to head */ oldsv->sv_u.svu_iv = cur; + } else sv_setiv(oldsv, cur); @@ -3982,7 +4027,11 @@ PP(pp_iter) SvREFCNT_dec(oldsv); } - if (UNLIKELY(cur == IV_MAX)) { + if (UNLIKELY(pad_it)) { + /* We're good (see "We are looping n-at-a-time" comment + above). */ + } + else if (UNLIKELY(cur == IV_MAX)) { /* Handle end of range at IV_MAX */ cx->blk_loop.state_u.lazyiv.end = IV_MIN; } else @@ -3998,10 +4047,17 @@ PP(pp_iter) if (UNLIKELY(inc > 0 ? ix > cx->blk_oldsp : ix <= cx->blk_loop.state_u.stack.basesp) - ) - goto retno; + ) { + if (LIKELY(!i)) { + goto retno; + } + + sv = &PL_sv_undef; + } + else { + sv = PL_stack_base[ix]; + } - sv = PL_stack_base[ix]; av = NULL; goto loop_ary_common; @@ -4013,10 +4069,13 @@ PP(pp_iter) if (UNLIKELY(inc > 0 ? ix > AvFILL(av) : ix < 0) - ) - goto retno; + ) { + if (LIKELY(!i)) { + goto retno; + } - if (UNLIKELY(SvRMAGICAL(av))) { + sv = &PL_sv_undef; + } else if (UNLIKELY(SvRMAGICAL(av))) { SV * const * const svp = av_fetch(av, ix, FALSE); sv = svp ? *svp : NULL; } @@ -4058,6 +4117,9 @@ PP(pp_iter) default: DIE(aTHX_ "panic: pp_iter, type=%u", CxTYPE(cx)); } + + /* Only relevant for a many-at-a-time loop: */ + ++itersvp; } /* Try to bypass pushing &PL_sv_yes and calling pp_and(); instead |