diff options
author | David Mitchell <davem@iabyn.com> | 2015-10-17 00:18:44 +0100 |
---|---|---|
committer | David Mitchell <davem@iabyn.com> | 2016-02-03 09:18:29 +0000 |
commit | 93661e56c6b52b1bf34963a3a62b2a766381cccd (patch) | |
tree | 8948b55e3e1b49cbe32d79b407abedf985c121b3 | |
parent | 0724084c5ac1086f135da52e9e7cf29cc2280640 (diff) | |
download | perl-93661e56c6b52b1bf34963a3a62b2a766381cccd.tar.gz |
split CXt_LOOP_FOR into CXt_LOOP_LIST,CXt_LOOP_ARY
Create a new context type so that "for (1,2,3)" and "for (@ary)"
are now two separate types.
For the list type, we store the index of the base stack element in the
state union rather than having an array pointer. Currently this is just
the same as blk_resetsp, but this will shortly allow us to eliminate the
resetsp field from the struct block_loop - which is currently the largest
sub-struct within the block union.
Having two separate types also allows the two cases to be handled directly
in the main switch in the hot pp_iter code, rather than having extra
conditionals.
-rw-r--r-- | cop.h | 48 | ||||
-rw-r--r-- | lib/B/Op_private.pm | 4 | ||||
-rw-r--r-- | opcode.h | 6 | ||||
-rw-r--r-- | perl.h | 5 | ||||
-rw-r--r-- | pp_ctl.c | 79 | ||||
-rw-r--r-- | pp_hot.c | 57 | ||||
-rw-r--r-- | regen/op_private | 5 | ||||
-rw-r--r-- | scope.c | 5 | ||||
-rw-r--r-- | sv.c | 3 |
9 files changed, 110 insertions, 102 deletions
@@ -770,15 +770,19 @@ struct block_loop { } itervar_u; SV *itersave; /* the original iteration var */ union { - struct { /* valid if type is LOOP_FOR or LOOP_PLAIN (but {NULL,0})*/ - AV * ary; /* use the stack if this is NULL */ - IV ix; + struct { /* CXt_LOOP_ARY, C<for (@ary)> */ + AV *ary; /* array being iterated over */ + IV ix; /* index relative to base of array */ } ary; - struct { /* valid if type is LOOP_LAZYIV */ + struct { /* CXt_LOOP_LIST, C<for (list)> */ + I32 basesp; /* first element of list on stack */ + IV ix; /* index relative to basesp */ + } stack; + struct { /* CXt_LOOP_LAZYIV, C<for (1..9)> */ IV cur; IV end; } lazyiv; - struct { /* valid if type if LOOP_LAZYSV */ + struct { /* CXt_LOOP_LAZYSV C<for ('a'..'z')> */ SV * cur; SV * end; /* maxiumum value (or minimum in reverse) */ } lazysv; @@ -822,8 +826,6 @@ struct block_loop { #define PUSHLOOP_FOR(cx, ivar, isave, s) \ cx->blk_loop.resetsp = s - PL_stack_base; \ cx->blk_loop.my_op = cLOOP; \ - cx->blk_loop.state_u.ary.ary = NULL; \ - cx->blk_loop.state_u.ary.ix = 0; \ cx->blk_loop.itervar_u.svp = (SV**)(ivar); \ cx->cx_old_savestack_ix = PL_savestack_ix; \ cx->blk_loop.itersave = isave; \ @@ -834,7 +836,7 @@ struct block_loop { SvREFCNT_dec_NN(cx->blk_loop.state_u.lazysv.cur); \ SvREFCNT_dec_NN(cx->blk_loop.state_u.lazysv.end); \ } \ - else if (CxTYPE(cx) == CXt_LOOP_FOR) \ + else if (CxTYPE(cx) == CXt_LOOP_ARY) \ SvREFCNT_dec(cx->blk_loop.state_u.ary.ary); \ if (cx->cx_type & (CXp_FOR_PAD|CXp_FOR_GV)) { \ SV *cursv; \ @@ -1035,15 +1037,19 @@ struct context { The first 4 don't have a 'case' in at least one switch statement in pp_ctl.c */ #define CXt_GIVEN 3 -/* This is first so that CXt_LOOP_FOR|CXt_LOOP_LAZYIV is CXt_LOOP_LAZYIV */ -#define CXt_LOOP_FOR 4 -#define CXt_LOOP_PLAIN 5 -#define CXt_LOOP_LAZYSV 6 -#define CXt_LOOP_LAZYIV 7 -#define CXt_SUB 8 -#define CXt_FORMAT 9 -#define CXt_EVAL 10 -#define CXt_SUBST 11 + +/* be careful of the ordering of these five. Macros like CxTYPE_is_LOOP, + * CxFOREACH compare ranges */ +#define CXt_LOOP_PLAIN 4 /* {} */ +#define CXt_LOOP_LAZYIV 5 /* for (1..9) {} */ +#define CXt_LOOP_LAZYSV 6 /* for ('a'..'z') {} */ +#define CXt_LOOP_LIST 7 /* for (1,2,3) {} */ +#define CXt_LOOP_ARY 8 /* for (@ary) {} */ + +#define CXt_SUB 9 +#define CXt_FORMAT 10 +#define CXt_EVAL 11 +#define CXt_SUBST 12 /* SUBST doesn't feature in all switch statements. */ /* private flags for CXt_SUB and CXt_FORMAT */ @@ -1068,15 +1074,15 @@ struct context { #define CXp_ONCE 0x10 /* What was sbu_once in struct subst */ #define CxTYPE(c) ((c)->cx_type & CXTYPEMASK) -#define CxTYPE_is_LOOP(c) (((c)->cx_type & 0xC) == 0x4) +#define CxTYPE_is_LOOP(c) ( CxTYPE(cx) >= CXt_LOOP_PLAIN \ + && CxTYPE(cx) <= CXt_LOOP_ARY) #define CxMULTICALL(c) ((c)->cx_type & CXp_MULTICALL) #define CxREALEVAL(c) (((c)->cx_type & (CXTYPEMASK|CXp_REAL)) \ == (CXt_EVAL|CXp_REAL)) #define CxTRYBLOCK(c) (((c)->cx_type & (CXTYPEMASK|CXp_TRYBLOCK)) \ == (CXt_EVAL|CXp_TRYBLOCK)) -#define CxFOREACH(c) (CxTYPE_is_LOOP(c) && CxTYPE(c) != CXt_LOOP_PLAIN) -#define CxFOREACHDEF(c) ((CxTYPE_is_LOOP(c) && CxTYPE(c) != CXt_LOOP_PLAIN) \ - && ((c)->cx_type & CXp_FOR_DEF)) +#define CxFOREACH(c) ( CxTYPE(cx) >= CXt_LOOP_LAZYSV \ + && CxTYPE(cx) <= CXt_LOOP_ARY) #define CXINC (cxstack_ix < cxstack_max ? ++cxstack_ix : (cxstack_ix = cxinc())) diff --git a/lib/B/Op_private.pm b/lib/B/Op_private.pm index 85076ee251..7825f79f3d 100644 --- a/lib/B/Op_private.pm +++ b/lib/B/Op_private.pm @@ -130,7 +130,7 @@ $bits{$_}{2} = 'OPpFT_STACKED' for qw(ftatime ftbinary ftblk ftchr ftctime ftdir $bits{$_}{3} = 'OPpFT_STACKING' for qw(ftatime ftbinary ftblk ftchr ftctime ftdir fteexec fteowned fteread ftewrite ftfile ftis ftlink ftmtime ftpipe ftrexec ftrowned ftrread ftrwrite ftsgid ftsize ftsock ftsuid ftsvtx fttext fttty ftzero); $bits{$_}{1} = 'OPpHINT_STRICT_REFS' for qw(entersub multideref rv2av rv2cv rv2gv rv2hv rv2sv); $bits{$_}{5} = 'OPpHUSH_VMSISH' for qw(dbstate nextstate); -$bits{$_}{2} = 'OPpITER_REVERSED' for qw(enteriter iter); +$bits{$_}{1} = 'OPpITER_REVERSED' for qw(enteriter iter); $bits{$_}{7} = 'OPpLVALUE' for qw(leave leaveloop); $bits{$_}{6} = 'OPpLVAL_DEFER' for qw(aelem helem multideref); $bits{$_}{7} = 'OPpLVAL_INTRO' for qw(aelem aslice cond_expr delete enteriter entersub gvsv helem hslice list lvavref lvref lvrefslice multideref padav padhv padrange padsv pushmark refassign rv2av rv2gv rv2hv rv2sv); @@ -604,7 +604,7 @@ our %defines = ( OPpHINT_STRICT_REFS => 2, OPpHUSH_VMSISH => 32, OPpITER_DEF => 8, - OPpITER_REVERSED => 4, + OPpITER_REVERSED => 2, OPpLIST_GUESSED => 64, OPpLVALUE => 128, OPpLVAL_DEFER => 64, @@ -2190,6 +2190,7 @@ END_EXTERN_C #define OPpEVAL_HAS_HH 0x02 #define OPpFT_ACCESS 0x02 #define OPpHINT_STRICT_REFS 0x02 +#define OPpITER_REVERSED 0x02 #define OPpSORT_INTEGER 0x02 #define OPpTRANS_TO_UTF 0x02 #define OPpARG2_MASK 0x03 @@ -2198,7 +2199,6 @@ END_EXTERN_C #define OPpENTERSUB_HASTARG 0x04 #define OPpEVAL_UNICODE 0x04 #define OPpFT_STACKED 0x04 -#define OPpITER_REVERSED 0x04 #define OPpLVREF_ELEM 0x04 #define OPpSLICEWARNING 0x04 #define OPpSORT_REVERSE 0x04 @@ -2873,8 +2873,8 @@ EXTCONST U16 PL_op_private_bitdefs[] = { 0x00bc, 0x012f, /* caller */ 0x21f5, /* nextstate, dbstate */ 0x29fc, 0x33d9, /* leave */ - 0x2b5c, 0x3078, 0x0e8c, 0x36e9, /* enteriter */ - 0x36e9, /* iter */ + 0x2b5c, 0x3078, 0x0e8c, 0x36e5, /* enteriter */ + 0x36e5, /* iter */ 0x29fc, 0x0067, /* leaveloop */ 0x41dc, 0x0003, /* last, next, redo, dump, goto */ 0x325c, 0x3178, 0x2634, 0x2570, 0x012f, /* open */ @@ -5110,10 +5110,11 @@ EXTCONST char* const PL_block_type[] = { "WHEN", "BLOCK", "GIVEN", - "LOOP_FOR", "LOOP_PLAIN", - "LOOP_LAZYSV", "LOOP_LAZYIV", + "LOOP_LAZYSV", + "LOOP_LIST", + "LOOP_ARY", "SUB", "FORMAT", "EVAL", @@ -1266,10 +1266,11 @@ static const char * const context_name[] = { NULL, /* CXt_WHEN never actually needs "block" */ NULL, /* CXt_BLOCK never actually needs "block" */ NULL, /* CXt_GIVEN never actually needs "block" */ - NULL, /* CXt_LOOP_FOR never actually needs "loop" */ NULL, /* CXt_LOOP_PLAIN never actually needs "loop" */ - NULL, /* CXt_LOOP_LAZYSV never actually needs "loop" */ NULL, /* CXt_LOOP_LAZYIV never actually needs "loop" */ + NULL, /* CXt_LOOP_LAZYSV never actually needs "loop" */ + NULL, /* CXt_LOOP_LIST never actually needs "loop" */ + NULL, /* CXt_LOOP_ARY never actually needs "loop" */ "subroutine", "format", "eval", @@ -1297,10 +1298,11 @@ S_dopoptolabel(pTHX_ const char *label, STRLEN len, U32 flags) if (CxTYPE(cx) == CXt_NULL) /* sort BLOCK */ return -1; break; + case CXt_LOOP_PLAIN: case CXt_LOOP_LAZYIV: case CXt_LOOP_LAZYSV: - case CXt_LOOP_FOR: - case CXt_LOOP_PLAIN: + case CXt_LOOP_LIST: + case CXt_LOOP_ARY: { STRLEN cx_label_len = 0; U32 cx_label_flags = 0; @@ -1444,10 +1446,11 @@ S_dopoptoloop(pTHX_ I32 startingblock) if ((CxTYPE(cx)) == CXt_NULL) /* sort BLOCK */ return -1; break; + case CXt_LOOP_PLAIN: case CXt_LOOP_LAZYIV: case CXt_LOOP_LAZYSV: - case CXt_LOOP_FOR: - case CXt_LOOP_PLAIN: + case CXt_LOOP_LIST: + case CXt_LOOP_ARY: DEBUG_l( Perl_deb(aTHX_ "(dopoptoloop(): found loop at cx=%ld)\n", (long)i)); return i; } @@ -1470,12 +1473,13 @@ S_dopoptogivenfor(pTHX_ I32 startingblock) DEBUG_l( Perl_deb(aTHX_ "(dopoptogivenfor(): found given at cx=%ld)\n", (long)i)); return i; case CXt_LOOP_PLAIN: - assert(!CxFOREACHDEF(cx)); + assert(!(cx->cx_type & CXp_FOR_DEF)); break; case CXt_LOOP_LAZYIV: case CXt_LOOP_LAZYSV: - case CXt_LOOP_FOR: - if (CxFOREACHDEF(cx)) { + case CXt_LOOP_LIST: + case CXt_LOOP_ARY: + if (cx->cx_type & CXp_FOR_DEF) { DEBUG_l( Perl_deb(aTHX_ "(dopoptogivenfor(): found foreach at cx=%ld)\n", (long)i)); return i; } @@ -1527,10 +1531,11 @@ Perl_dounwind(pTHX_ I32 cxix) case CXt_BLOCK: POPBASICBLK(cx); break; + case CXt_LOOP_PLAIN: case CXt_LOOP_LAZYIV: case CXt_LOOP_LAZYSV: - case CXt_LOOP_FOR: - case CXt_LOOP_PLAIN: + case CXt_LOOP_LIST: + case CXt_LOOP_ARY: POPLOOP(cx); break; case CXt_WHEN: @@ -2142,7 +2147,7 @@ PP(pp_enteriter) const I32 gimme = GIMME_V; void *itervarp; /* GV or pad slot of the iteration variable */ SV *itersave; /* the old var in the iterator var slot */ - U8 cxtype = CXt_LOOP_FOR; + U8 cxflags = 0; if (PL_op->op_targ) { /* "my" variable */ itervarp = &PAD_SVl(PL_op->op_targ); @@ -2155,7 +2160,7 @@ PP(pp_enteriter) SvPADSTALE_on(itersave); } SvREFCNT_inc_simple_void_NN(itersave); - cxtype |= CXp_FOR_PAD; + cxflags = CXp_FOR_PAD; } else { SV * const sv = POPs; @@ -2167,37 +2172,33 @@ PP(pp_enteriter) SvREFCNT_inc_simple_void_NN(itersave); else *svp = newSV(0); - cxtype |= CXp_FOR_GV; + cxflags = CXp_FOR_GV; } else { /* LV ref: for \$foo (...) */ assert(SvTYPE(sv) == SVt_PVMG); assert(SvMAGIC(sv)); assert(SvMAGIC(sv)->mg_type == PERL_MAGIC_lvref); itersave = NULL; - cxtype |= CXp_FOR_LVREF; + cxflags = CXp_FOR_LVREF; } } if (PL_op->op_private & OPpITER_DEF) - cxtype |= CXp_FOR_DEF; + cxflags |= CXp_FOR_DEF; - PUSHBLOCK(cx, cxtype, SP); + PUSHBLOCK(cx, cxflags, SP); PUSHLOOP_FOR(cx, itervarp, itersave, MARK); if (PL_op->op_flags & OPf_STACKED) { SV *maybe_ary = POPs; if (SvTYPE(maybe_ary) != SVt_PVAV) { dPOPss; SV * const right = maybe_ary; - if (UNLIKELY(cxtype & CXp_FOR_LVREF)) + if (UNLIKELY(cxflags & CXp_FOR_LVREF)) DIE(aTHX_ "Assigned value is not a reference"); SvGETMAGIC(sv); SvGETMAGIC(right); if (RANGE_IS_NUMERIC(sv,right)) { - cx->cx_type &= ~CXTYPEMASK; cx->cx_type |= CXt_LOOP_LAZYIV; - /* Make sure that no-one re-orders cop.h and breaks our - assumptions */ - assert(CxTYPE(cx) == CXt_LOOP_LAZYIV); if (S_outside_integer(aTHX_ sv) || S_outside_integer(aTHX_ right)) DIE(aTHX_ "Range iterator outside integer range"); @@ -2209,11 +2210,7 @@ PP(pp_enteriter) #endif } else { - cx->cx_type &= ~CXTYPEMASK; cx->cx_type |= CXt_LOOP_LAZYSV; - /* Make sure that no-one re-orders cop.h and breaks our - assumptions */ - assert(CxTYPE(cx) == CXt_LOOP_LAZYSV); cx->blk_loop.state_u.lazysv.cur = newSVsv(sv); cx->blk_loop.state_u.lazysv.end = right; SvREFCNT_inc(right); @@ -2230,6 +2227,7 @@ PP(pp_enteriter) } } else /* SvTYPE(maybe_ary) == SVt_PVAV */ { + cx->cx_type |= CXt_LOOP_ARY; cx->blk_loop.state_u.ary.ary = MUTABLE_AV(maybe_ary); SvREFCNT_inc(maybe_ary); cx->blk_loop.state_u.ary.ix = @@ -2239,13 +2237,12 @@ PP(pp_enteriter) } } else { /* iterating over items on the stack */ - cx->blk_loop.state_u.ary.ary = NULL; /* means to use the stack */ - if (PL_op->op_private & OPpITER_REVERSED) { - cx->blk_loop.state_u.ary.ix = cx->blk_oldsp + 1; - } - else { - cx->blk_loop.state_u.ary.ix = MARK - PL_stack_base; - } + cx->cx_type |= CXt_LOOP_LIST; + cx->blk_loop.state_u.stack.basesp = MARK - PL_stack_base; + cx->blk_loop.state_u.stack.ix = + (PL_op->op_private & OPpITER_REVERSED) + ? cx->blk_oldsp + 1 + : cx->blk_loop.state_u.stack.basesp; } RETURN; @@ -2577,12 +2574,7 @@ PP(pp_last) cx = &cxstack[cxstack_ix]; - assert( - CxTYPE(cx) == CXt_LOOP_LAZYIV - || CxTYPE(cx) == CXt_LOOP_LAZYSV - || CxTYPE(cx) == CXt_LOOP_FOR - || CxTYPE(cx) == CXt_LOOP_PLAIN - ); + assert(CxTYPE_is_LOOP(cx)); PL_stack_sp = PL_stack_base + cx->blk_loop.resetsp; TAINT_NOT; @@ -2969,10 +2961,11 @@ PP(pp_goto) break; } /* else fall through */ - case CXt_LOOP_LAZYIV: - case CXt_LOOP_LAZYSV: - case CXt_LOOP_FOR: - case CXt_LOOP_PLAIN: + case CXt_LOOP_PLAIN: + case CXt_LOOP_LAZYIV: + case CXt_LOOP_LAZYSV: + case CXt_LOOP_LIST: + case CXt_LOOP_ARY: case CXt_GIVEN: case CXt_WHEN: gotoprobe = OpSIBLING(cx->blk_oldcop); @@ -247,13 +247,7 @@ PP(pp_unstack) PL_stack_sp = PL_stack_base + cx->blk_oldsp; FREETMPS; if (!(PL_op->op_flags & OPf_SPECIAL)) { - assert( - CxTYPE(cx) == CXt_BLOCK - || CxTYPE(cx) == CXt_LOOP_FOR - || CxTYPE(cx) == CXt_LOOP_PLAIN - || CxTYPE(cx) == CXt_LOOP_LAZYSV - || CxTYPE(cx) == CXt_LOOP_LAZYIV - ); + assert(CxTYPE(cx) == CXt_BLOCK || CxTYPE_is_LOOP(cx)); CX_LEAVE_SCOPE(cx); } return NORMAL; @@ -2702,28 +2696,37 @@ PP(pp_iter) break; } - case CXt_LOOP_FOR: /* iterate array */ { - - AV *av = cx->blk_loop.state_u.ary.ary; SV *sv; - bool av_is_stack = FALSE; + AV *av; IV ix; + IV inc; - if (!av) { - av_is_stack = TRUE; - av = PL_curstack; - } - if (PL_op->op_private & OPpITER_REVERSED) { - ix = --cx->blk_loop.state_u.ary.ix; - if (UNLIKELY(ix <= (av_is_stack ? cx->blk_loop.resetsp : -1))) - RETPUSHNO; - } - else { - ix = ++cx->blk_loop.state_u.ary.ix; - if (UNLIKELY(ix > (av_is_stack ? cx->blk_oldsp : AvFILL(av)))) - RETPUSHNO; - } + case CXt_LOOP_LIST: /* for (1,2,3) */ + + assert(OPpITER_REVERSED == 2); /* so inc becomes -1 or 1 */ + inc = 1 - (PL_op->op_private & OPpITER_REVERSED); + ix = (cx->blk_loop.state_u.stack.ix += inc); + if (UNLIKELY(inc > 0 + ? ix > cx->blk_oldsp + : ix <= cx->blk_loop.state_u.stack.basesp) + ) + RETPUSHNO; + + sv = PL_stack_base[ix]; + av = NULL; + goto loop_ary_common; + + case CXt_LOOP_ARY: /* for (@ary) */ + + av = cx->blk_loop.state_u.ary.ary; + inc = 1 - (PL_op->op_private & OPpITER_REVERSED); + ix = (cx->blk_loop.state_u.ary.ix += inc); + if (UNLIKELY(inc > 0 + ? ix > AvFILL(av) + : ix < 0) + ) + RETPUSHNO; if (UNLIKELY(SvMAGICAL(av) || AvREIFY(av))) { SV * const * const svp = av_fetch(av, ix, FALSE); @@ -2733,6 +2736,8 @@ PP(pp_iter) sv = AvARRAY(av)[ix]; } + loop_ary_common: + if (UNLIKELY(cx->cx_type & CXp_FOR_LVREF)) { SvSetMagicSV(*itersvp, sv); break; @@ -2751,7 +2756,7 @@ PP(pp_iter) SvREFCNT_inc_simple_void_NN(sv); } } - else if (!av_is_stack) { + else if (av) { sv = newSVavdefelem(av, ix, 0); } else diff --git a/regen/op_private b/regen/op_private index ab63e11833..a7f569e38e 100644 --- a/regen/op_private +++ b/regen/op_private @@ -619,12 +619,13 @@ addbits('rv2gv', ); +# NB OPpITER_REVERSED must always be bit 1: see pp_iter() addbits('enteriter', - 2 => qw(OPpITER_REVERSED REVERSED),# for (reverse ...) + 1 => qw(OPpITER_REVERSED REVERSED),# for (reverse ...) 3 => qw(OPpITER_DEF DEF), # 'for $_' ); -addbits('iter', 2 => qw(OPpITER_REVERSED REVERSED)); +addbits('iter', 1 => qw(OPpITER_REVERSED REVERSED)); @@ -1366,10 +1366,11 @@ Perl_cx_dump(pTHX_ PERL_CONTEXT *cx) PTR2UV(cx->blk_eval.retop)); break; + case CXt_LOOP_PLAIN: case CXt_LOOP_LAZYIV: case CXt_LOOP_LAZYSV: - case CXt_LOOP_FOR: - case CXt_LOOP_PLAIN: + case CXt_LOOP_LIST: + case CXt_LOOP_ARY: PerlIO_printf(Perl_debug_log, "BLK_LOOP.LABEL = %s\n", CxLABEL(cx)); PerlIO_printf(Perl_debug_log, "BLK_LOOP.RESETSP = %ld\n", (long)cx->blk_loop.resetsp); @@ -13969,10 +13969,11 @@ Perl_cx_dup(pTHX_ PERL_CONTEXT *cxs, I32 ix, I32 max, CLONE_PARAMS* param) assert ((void *) &ncx->blk_loop.state_u.ary.ary == (void *) &ncx->blk_loop.state_u.lazysv.cur); /* FALLTHROUGH */ - case CXt_LOOP_FOR: + case CXt_LOOP_ARY: ncx->blk_loop.state_u.ary.ary = av_dup_inc(ncx->blk_loop.state_u.ary.ary, param); /* FALLTHROUGH */ + case CXt_LOOP_LIST: case CXt_LOOP_LAZYIV: case CXt_LOOP_PLAIN: /* code common to all CXt_LOOP_* types */ |