summaryrefslogtreecommitdiff
path: root/pp_hot.c
diff options
context:
space:
mode:
authorNicholas Clark <nick@ccl4.org>2021-04-25 20:03:37 +0000
committerRicardo Signes <rjbs@semiotic.systems>2021-10-15 09:28:26 -0400
commitc52d5e029a9e8dc660331b57193ea652aa0f2beb (patch)
tree16d1954b942847f9251a1749fdb07037214bb0ba /pp_hot.c
parent525dc1e45d4c3a81deef4481ad38a8e49380b753 (diff)
downloadperl-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.c92
1 files changed, 77 insertions, 15 deletions
diff --git a/pp_hot.c b/pp_hot.c
index 42de654303..316a26a665 100644
--- a/pp_hot.c
+++ b/pp_hot.c
@@ -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