diff --git a/MANIFEST b/MANIFEST index 8d3a39b94c6d..edb85de307b0 100644 --- a/MANIFEST +++ b/MANIFEST @@ -4731,6 +4731,7 @@ ext/XS-APItest/t/whichsig.t XS::APItest: tests for whichsig() and variants ext/XS-APItest/t/win32.t Test Win32 specific APIs ext/XS-APItest/t/xs_special_subs.t Test that XS BEGIN/CHECK/INIT/END work ext/XS-APItest/t/xs_special_subs_require.t for require too +ext/XS-APItest/t/xsppwrap.t Tests for XSPP_wrapped() macro ext/XS-APItest/t/xsub_h.t Tests for XSUB.h ext/XS-APItest/typemap ext/XS-APItest/XSUB-redefined-macros.xs XS code needing redefined macros. diff --git a/autodoc.pl b/autodoc.pl index 9583901d18d2..91d2fa5edccf 100644 --- a/autodoc.pl +++ b/autodoc.pl @@ -138,6 +138,7 @@ my $MRO_scn = 'MRO'; my $multicall_scn = 'Multicall Functions'; my $numeric_scn = 'Numeric Functions'; +my $rpp_scn = 'Reference-counted stack manipulation'; # Now combined, as unclear which functions go where, but separate names kept # to avoid 1) other code changes; 2) in case it seems better to split again @@ -320,6 +321,14 @@ See L. EOT }, + $rpp_scn => { + header => <<~'EOT', + Functions for pushing and pulling items on the stack when the + stack is reference counted. They are intended as replacements + for the old PUSHs, POPi, EXTEND etc pp macros within pp + functions. + EOT + }, $signals_scn => {}, $site_scn => { header => <<~'EOT', @@ -1713,7 +1722,7 @@ sub output { # We allow empty sections in perlintern. if (! $section_info && $podname eq 'perlapi') { - warn "Empty section '$section_name'; skipped"; + warn "Empty section '$section_name' for $podname; skipped"; next; } diff --git a/av.c b/av.c index d9868f44078b..27dbe6115a6d 100644 --- a/av.c +++ b/av.c @@ -42,8 +42,8 @@ Perl_av_reify(pTHX_ AV *av) SvREFCNT_inc_simple_void(sv); } key = AvARRAY(av) - AvALLOC(av); - while (key) - AvALLOC(av)[--key] = NULL; + if (key) + Zero(AvALLOC(av), key, SV*); AvREIFY_off(av); AvREAL_on(av); } @@ -617,7 +617,6 @@ to it. void Perl_av_clear(pTHX_ AV *av) { - SSize_t extra; bool real; SSize_t orig_ix = 0; @@ -662,12 +661,9 @@ Perl_av_clear(pTHX_ AV *av) SvREFCNT_dec(sv); } } - extra = AvARRAY(av) - AvALLOC(av); - if (extra) { - AvMAX(av) += extra; - AvARRAY(av) = AvALLOC(av); - } AvFILLp(av) = -1; + av_remove_offset(av); + if (real) { /* disarm av's premature free guard */ if (LIKELY(PL_tmps_ix == orig_ix)) @@ -886,6 +882,9 @@ Perl_av_unshift(pTHX_ AV *av, SSize_t num) AvMAX(av) += i; AvFILLp(av) += i; AvARRAY(av) = AvARRAY(av) - i; +#ifdef PERL_RC_STACK + Zero(AvARRAY(av), i, SV*); +#endif } if (num) { SV **ary; @@ -939,8 +938,10 @@ Perl_av_shift(pTHX_ AV *av) if (AvFILL(av) < 0) return &PL_sv_undef; retval = *AvARRAY(av); +#ifndef PERL_RC_STACK if (AvREAL(av)) *AvARRAY(av) = NULL; +#endif AvARRAY(av) = AvARRAY(av) + 1; AvMAX(av)--; AvFILLp(av)--; diff --git a/cop.h b/cop.h index 969a17846b5f..1d61a4b58e77 100644 --- a/cop.h +++ b/cop.h @@ -860,12 +860,14 @@ struct block_format { } STMT_END /* junk in @_ spells trouble when cloning CVs and in pp_caller(), so don't - * leave any (a fast av_clear(ary), basically) */ + * leave any (a fast av_clear(ary), basically). + * New code should probably be using Perl_clear_defarray_simple() + * and/or Perl_clear_defarray() + */ #define CLEAR_ARGARRAY(ary) \ STMT_START { \ - AvMAX(ary) += AvARRAY(ary) - AvALLOC(ary); \ - AvARRAY(ary) = AvALLOC(ary); \ AvFILLp(ary) = -1; \ + av_remove_offset(ary); \ } STMT_END @@ -1230,6 +1232,15 @@ struct stackinfo { I32 si_markoff; /* offset where markstack begins for us. * currently used only with DEBUGGING, * but not #ifdef-ed for bincompat */ + +#ifdef PERL_RC_STACK + /* index of first entry in the argument + stack which is not ref-counted. If + set to 0 (default), all stack + elements are ref-counted */ + I32 si_stack_nonrc_base; +#endif + #if defined DEBUGGING && !defined DEBUGGING_RE_ONLY /* high water mark: for checking if the stack was correctly extended / * tested for extension by each pp function */ @@ -1263,54 +1274,31 @@ typedef struct stackinfo PERL_SI; # define PUSHSTACK_INIT_HWM(si) NOOP #endif +/* for backcompat; use push_stackinfo() instead */ + #define PUSHSTACKi(type) \ - STMT_START { \ - PERL_SI *next = PL_curstackinfo->si_next; \ - DEBUG_l({ \ - int i = 0; PERL_SI *p = PL_curstackinfo; \ - while (p) { i++; p = p->si_prev; } \ - Perl_deb(aTHX_ "push STACKINFO %d in %s at %s:%d\n", \ - i, SAFE_FUNCTION__, __FILE__, __LINE__);}) \ - if (!next) { \ - next = new_stackinfo(32, 2048/sizeof(PERL_CONTEXT) - 1); \ - next->si_prev = PL_curstackinfo; \ - PL_curstackinfo->si_next = next; \ - } \ - next->si_type = type; \ - next->si_cxix = -1; \ - next->si_cxsubix = -1; \ - PUSHSTACK_INIT_HWM(next); \ - AvFILLp(next->si_stack) = 0; \ - SWITCHSTACK(PL_curstack,next->si_stack); \ - PL_curstackinfo = next; \ - SET_MARK_OFFSET; \ + STMT_START { \ + PL_stack_sp = sp; \ + push_stackinfo(type, 0);\ + sp = PL_stack_sp ; \ } STMT_END #define PUSHSTACK PUSHSTACKi(PERLSI_UNKNOWN) -/* POPSTACK works with PL_stack_sp, so it may need to be bracketed by + +/* for backcompat; use pop_stackinfo() instead. + * + * POPSTACK works with PL_stack_sp, so it may need to be bracketed by * PUTBACK/SPAGAIN to flush/refresh any local SP that may be active */ -#define POPSTACK \ - STMT_START { \ - dSP; \ - PERL_SI * const prev = PL_curstackinfo->si_prev; \ - DEBUG_l({ \ - int i = -1; PERL_SI *p = PL_curstackinfo; \ - while (p) { i++; p = p->si_prev; } \ - Perl_deb(aTHX_ "pop STACKINFO %d in %s at %s:%d\n", \ - i, SAFE_FUNCTION__, __FILE__, __LINE__);}) \ - if (!prev) { \ - Perl_croak_popstack(); \ - } \ - SWITCHSTACK(PL_curstack,prev->si_stack); \ - /* don't free prev here, free them all at the END{} */ \ - PL_curstackinfo = prev; \ - } STMT_END + +#define POPSTACK pop_stackinfo() + #define POPSTACK_TO(s) \ STMT_START { \ while (PL_curstack != s) { \ dounwind(-1); \ + rpp_obliterate_stack_to(0); \ POPSTACK; \ } \ } STMT_END diff --git a/deb.c b/deb.c index 64ff5874cb6c..dc28a022e9d1 100644 --- a/deb.c +++ b/deb.c @@ -133,13 +133,16 @@ Perl_debstackptrs(pTHX) /* Currently unused in cpan and core */ * and display the marks whose offsets are contained in addresses * PL_markstack[mark_min+1 .. mark_max] and whose values are in the range * of the stack values being displayed + * On PERL_RC_STACK builds, nonrc_base indicates the lowest + * non-reference-counted stack element (or 0 if none or not such a build). + * Display a vertical bar at this position. * * Only displays top 30 max */ STATIC void S_deb_stack_n(pTHX_ SV** stack_base, I32 stack_min, I32 stack_max, - I32 mark_min, I32 mark_max) + I32 mark_min, I32 mark_max, I32 nonrc_base) { #ifdef DEBUGGING I32 i = stack_max - 30; @@ -171,7 +174,11 @@ S_deb_stack_n(pTHX_ SV** stack_base, I32 stack_min, I32 stack_max, } if (i > stack_max) break; + PerlIO_printf(Perl_debug_log, "%-4s ", SvPEEK(stack_base[i])); + + if (nonrc_base && nonrc_base == i + 1) + PerlIO_printf(Perl_debug_log, "| "); } while (1); PerlIO_printf(Perl_debug_log, "\n"); @@ -182,6 +189,7 @@ S_deb_stack_n(pTHX_ SV** stack_base, I32 stack_min, I32 stack_max, PERL_UNUSED_ARG(stack_max); PERL_UNUSED_ARG(mark_min); PERL_UNUSED_ARG(mark_max); + PERL_UNUSED_ARG(nonrc_base); #endif /* DEBUGGING */ } @@ -206,7 +214,13 @@ Perl_debstack(pTHX) 0, PL_stack_sp - PL_stack_base, PL_curstackinfo->si_markoff, - PL_markstack_ptr - PL_markstack); + PL_markstack_ptr - PL_markstack, +#ifdef PERL_RC_STACK + PL_curstackinfo->si_stack_nonrc_base +#else + 0 +#endif + ); #endif /* SKIP_DEBUGGING */ @@ -254,8 +268,16 @@ Perl_deb_stack_all(pTHX) si_name_ix < C_ARRAY_LENGTH(si_names) ? si_names[si_name_ix] : "????"; I32 ix; - PerlIO_printf(Perl_debug_log, "STACK %" IVdf ": %s\n", - (IV)si_ix, si_name); + PerlIO_printf(Perl_debug_log, "STACK %" IVdf ": %s%s\n", + (IV)si_ix, si_name, +#ifdef PERL_RC_STACK + AvREAL(si->si_stack) + ? (si->si_stack_nonrc_base ? " (partial real)" : " (real)") + : "" +#else + "" +#endif + ); for (ix=0; ix<=si->si_cxix; ix++) { @@ -320,7 +342,10 @@ Perl_deb_stack_all(pTHX) } if (CxTYPE(&(si_n->si_cxstack[i])) == CXt_SUBST) continue; - cx_n = &(si_n->si_cxstack[i]); + if (si_n->si_cxix >= 0) + cx_n = &(si_n->si_cxstack[i]); + else + cx_n = NULL; break; } @@ -333,7 +358,13 @@ Perl_deb_stack_all(pTHX) } deb_stack_n(AvARRAY(si->si_stack), - stack_min, stack_max, mark_min, mark_max); + stack_min, stack_max, mark_min, mark_max, +#ifdef PERL_RC_STACK + si->si_stack_nonrc_base +#else + 0 +#endif + ); if (CxTYPE(cx) == CXt_EVAL || CxTYPE(cx) == CXt_SUB || CxTYPE(cx) == CXt_FORMAT) diff --git a/dist/threads/lib/threads.pm b/dist/threads/lib/threads.pm index 26e00570a028..b9722a029cbb 100644 --- a/dist/threads/lib/threads.pm +++ b/dist/threads/lib/threads.pm @@ -5,7 +5,7 @@ use 5.008; use strict; use warnings; -our $VERSION = '2.34'; # remember to update version in POD! +our $VERSION = '2.35'; # remember to update version in POD below! my $XS_VERSION = $VERSION; $VERSION = eval $VERSION; @@ -134,7 +134,7 @@ threads - Perl interpreter-based threads =head1 VERSION -This document describes threads version 2.34 +This document describes threads version 2.35 =head1 WARNING diff --git a/dist/threads/threads.xs b/dist/threads/threads.xs index 093380e7514b..7489c1b176f6 100644 --- a/dist/threads/threads.xs +++ b/dist/threads/threads.xs @@ -611,16 +611,20 @@ S_ithread_run(void * arg) int ii; int jmp_rc; - dSP; +#ifdef PERL_RC_STACK + assert(rpp_stack_is_rc()); +#endif + ENTER; SAVETMPS; /* Put args on the stack */ - PUSHMARK(SP); + PUSHMARK(PL_stack_sp); + rpp_extend(len); for (ii=0; ii < len; ii++) { - XPUSHs(av_shift(params)); + SV *sv = av_shift(params); + rpp_push_1(sv); } - PUTBACK; jmp_rc = S_jmpenv_run(aTHX_ 0, thread, &len, &exit_app, &exit_code); @@ -633,12 +637,12 @@ S_ithread_run(void * arg) #endif /* Remove args from stack and put back in params array */ - SPAGAIN; for (ii=len-1; ii >= 0; ii--) { - SV *sv = POPs; + SV *sv = *PL_stack_sp; if (jmp_rc == 0 && (thread->gimme & G_WANT) != G_VOID) { av_store(params, ii, SvREFCNT_inc(sv)); } + rpp_popfree_1(); } FREETMPS; diff --git a/doio.c b/doio.c index 48deb13d6a8c..c1fb3491c61a 100644 --- a/doio.c +++ b/doio.c @@ -2225,7 +2225,6 @@ Perl_do_print(pTHX_ SV *sv, PerlIO *fp) I32 Perl_my_stat_flags(pTHX_ const U32 flags) { - dSP; IO *io; GV* gv; @@ -2265,7 +2264,7 @@ Perl_my_stat_flags(pTHX_ const U32 flags) == OPpFT_STACKED) return PL_laststatval; else { - SV* const sv = TOPs; + SV* const sv = *PL_stack_sp; const char *s, *d; STRLEN len; if ((gv = MAYBE_DEREF_GV_flags(sv,flags))) { @@ -2302,10 +2301,9 @@ I32 Perl_my_lstat_flags(pTHX_ const U32 flags) { static const char* const no_prev_lstat = "The stat preceding -l _ wasn't an lstat"; - dSP; const char *file; STRLEN len; - SV* const sv = TOPs; + SV* const sv = *PL_stack_sp; bool isio = FALSE; if (PL_op->op_flags & OPf_REF) { if (cGVOP_gv == PL_defgv) { diff --git a/doop.c b/doop.c index 37d7ea4d17a9..0670e5c62166 100644 --- a/doop.c +++ b/doop.c @@ -1183,8 +1183,7 @@ Perl_do_vop(pTHX_ I32 optype, SV *sv, SV *left, SV *right) * values, or key-value pairs, depending on PL_op. */ -OP * -Perl_do_kv(pTHX) +PP_wrapped(do_kv, 1, 0) { dSP; HV * const keys = MUTABLE_HV(POPs); diff --git a/dump.c b/dump.c index 62f41b439211..ac9454bbfe1c 100644 --- a/dump.c +++ b/dump.c @@ -536,10 +536,12 @@ Perl_sv_peek(pTHX_ SV *sv) break; } } - if (is_tmp || SvREFCNT(sv) > 1) { + if (is_tmp || SvREFCNT(sv) > 1 || SvPADTMP(sv)) { Perl_sv_catpvf(aTHX_ t, "<"); if (SvREFCNT(sv) > 1) Perl_sv_catpvf(aTHX_ t, "%" UVuf, (UV)SvREFCNT(sv)); + if (SvPADTMP(sv)) + Perl_sv_catpvf(aTHX_ t, "%s", "P"); if (is_tmp) Perl_sv_catpvf(aTHX_ t, "%s", SvTEMP(t) ? "T" : "t"); Perl_sv_catpvf(aTHX_ t, ">"); @@ -2808,6 +2810,12 @@ Perl_runops_debug(pTHX) PL_curstackinfo->si_stack_hwm = PL_stack_sp - PL_stack_base; #endif +#ifdef PERL_RC_STACK + assert(rpp_stack_is_rc()); + assert(PL_stack_base + PL_curstackinfo->si_stack_nonrc_base + <= PL_stack_sp); +#endif + if (!PL_op) { Perl_ck_warner_d(aTHX_ packWARN(WARN_DEBUGGING), "NULL OP IN RUN"); return 0; diff --git a/embed.fnc b/embed.fnc index 8af314950c5f..638a0132a64a 100644 --- a/embed.fnc +++ b/embed.fnc @@ -691,6 +691,8 @@ Adip |void |av_push_simple |NN AV *av \ |NN SV *val : Used in scope.c, and by Data::Alias EXp |void |av_reify |NN AV *av +ipx |void |av_remove_offset \ + |NN AV *av ARdp |SV * |av_shift |NN AV *av Adp |SV ** |av_store |NN AV *av \ |SSize_t key \ @@ -805,6 +807,8 @@ Adfp |void |ck_warner_d |U32 err \ Cp |void |clear_defarray |NN AV *av \ |bool abandon +Cipx |void |clear_defarray_simple \ + |NN AV *av p |const COP *|closest_cop|NN const COP *cop \ |NULLOK const OP *o \ |NULLOK const OP *curop \ @@ -839,6 +843,7 @@ p |OP * |coresub_op |NN SV * const coreargssv \ : Used in op.c and perl.c px |void |create_eval_scope \ |NULLOK OP *retop \ + |NN SV **sp \ |U32 flags : croak()'s first parm can be NULL. Otherwise, mod_perl breaks. Adfpr |void |croak |NULLOK const char *pat \ @@ -2210,6 +2215,10 @@ ARdp |OP * |newSLICEOP |I32 flags \ |NULLOK OP *listop CRp |PERL_SI *|new_stackinfo|I32 stitems \ |I32 cxitems +CRp |PERL_SI *|new_stackinfo_flags \ + |I32 stitems \ + |I32 cxitems \ + |UV flags ARdp |OP * |newSTATEOP |I32 flags \ |NULLOK char *label \ |NULLOK OP *o @@ -2500,6 +2509,7 @@ p |OP * |pmruntime |NN OP *o \ |I32 floor Xiop |I32 |POPMARK Cdp |void |pop_scope +Cipx |void |pop_stackinfo : Used in perl.c and toke.c Fop |void |populate_isa |NN const char *name \ @@ -2533,6 +2543,8 @@ Adp |void |ptr_table_store|NN PTR_TBL_t * const tbl \ |NULLOK const void * const oldsv \ |NN void * const newsv Cdp |void |push_scope +Cipx |void |push_stackinfo |I32 type \ + |UV flags Adp |char * |pv_display |NN SV *dsv \ |NN const char *pv \ |STRLEN cur \ @@ -2727,6 +2739,30 @@ APTdp |char * |rninstr |NN const char *big \ |NN const char *little \ |NN const char *lend p |void |rpeep |NULLOK OP *o +Adipx |void |rpp_extend |SSize_t n +Adipx |bool |rpp_is_lone |NN SV *sv +Cpx |void |rpp_obliterate_stack_to \ + |I32 ix +Adipx |void |rpp_popfree_1 +Adipx |void |rpp_popfree_2 +Adipx |void |rpp_popfree_to |NN SV **sp +Adipx |SV * |rpp_pop_1_norc +Adipx |void |rpp_push_1 |NN SV *sv +Adipx |void |rpp_push_2 |NN SV *sv1 \ + |NN SV *sv2 +Adipx |void |rpp_push_1_norc|NN SV *sv +Adipx |void |rpp_replace_1_1|NN SV *sv +Adipx |void |rpp_replace_2_1|NN SV *sv +Adipx |bool |rpp_stack_is_rc +Adipx |bool |rpp_try_AMAGIC_1 \ + |int method \ + |int flags +Adipx |bool |rpp_try_AMAGIC_2 \ + |int method \ + |int flags +Adipx |void |rpp_xpush_1 |NN SV *sv +Adipx |void |rpp_xpush_2 |NN SV *sv1 \ + |NN SV *sv2 Adp |Sighandler_t|rsignal |int i \ |Sighandler_t t : Used in pp_sys.c @@ -2740,6 +2776,7 @@ Adp |Sighandler_t|rsignal_state \ |int i Cdhp |int |runops_debug Cdhp |int |runops_standard +Cpx |int |runops_wrap Adp |CV * |rv2cv_op_cv |NN OP *cvop \ |U32 flags : Used in pp_hot.c @@ -3397,6 +3434,7 @@ Adp |void |sv_vsetpvfn |NN SV * const sv \ |NULLOK SV ** const svargs \ |const Size_t sv_count \ |NULLOK bool * const maybe_tainted +Cipx |void |switch_argstack|NN AV *to Adp |void |switch_to_global_locale Adp |bool |sync_locale CTop |void |sys_init |NN int *argc \ @@ -3999,7 +4037,8 @@ S |void |deb_stack_n |NN SV **stack_base \ |I32 stack_min \ |I32 stack_max \ |I32 mark_min \ - |I32 mark_max + |I32 mark_max \ + |I32 nonrc_base #endif /* defined(PERL_IN_DEB_C) */ #if defined(PERL_IN_DOIO_C) S |bool |argvout_final |NN MAGIC *mg \ @@ -5951,6 +5990,13 @@ Cipx |void |cx_pushwhen |NN PERL_CONTEXT *cx Cipx |void |cx_topblock |NN PERL_CONTEXT *cx Cipx |U8 |gimme_V #endif /* !defined(PERL_NO_INLINE_FUNCTIONS) */ +#if defined(PERL_RC_STACK) +EXopx |OP * |pp_wrap |NN Perl_ppaddr_t real_pp_fn \ + |I32 nargs \ + |int nlists +EXopx |void |xs_wrap |NN XSUBADDR_t xsub \ + |NN CV *cv +#endif /* defined(PERL_RC_STACK) */ #if defined(PERL_USE_3ARG_SIGHANDLER) CTp |Signal_t|csighandler |int sig \ |NULLOK Siginfo_t *info \ diff --git a/embed.h b/embed.h index b0d23f0a4738..c7f44e32890c 100644 --- a/embed.h +++ b/embed.h @@ -174,6 +174,7 @@ # define ck_entersub_args_proto(a,b,c) Perl_ck_entersub_args_proto(aTHX_ a,b,c) # define ck_entersub_args_proto_or_list(a,b,c) Perl_ck_entersub_args_proto_or_list(aTHX_ a,b,c) # define clear_defarray(a,b) Perl_clear_defarray(aTHX_ a,b) +# define clear_defarray_simple(a) Perl_clear_defarray_simple(aTHX_ a) # define cop_fetch_label(a,b,c) Perl_cop_fetch_label(aTHX_ a,b,c) # define cop_store_label(a,b,c,d) Perl_cop_store_label(aTHX_ a,b,c,d) # define croak_memory_wrap Perl_croak_memory_wrap @@ -449,6 +450,7 @@ # define newXS(a,b,c) Perl_newXS(aTHX_ a,b,c) # define newXS_flags(a,b,c,d,e) Perl_newXS_flags(aTHX_ a,b,c,d,e) # define new_stackinfo(a,b) Perl_new_stackinfo(aTHX_ a,b) +# define new_stackinfo_flags(a,b,c) Perl_new_stackinfo_flags(aTHX_ a,b,c) # define new_version(a) Perl_new_version(aTHX_ a) # define nothreadhook() Perl_nothreadhook(aTHX) # define op_append_elem(a,b,c) Perl_op_append_elem(aTHX_ a,b,c) @@ -494,6 +496,7 @@ # define perly_sighandler Perl_perly_sighandler # define pmop_dump(a) Perl_pmop_dump(aTHX_ a) # define pop_scope() Perl_pop_scope(aTHX) +# define pop_stackinfo() Perl_pop_stackinfo(aTHX) # define pregcomp(a,b) Perl_pregcomp(aTHX_ a,b) # define pregexec(a,b,c,d,e,f,g) Perl_pregexec(aTHX_ a,b,c,d,e,f,g) # define pregfree(a) Perl_pregfree(aTHX_ a) @@ -505,6 +508,7 @@ # define ptr_table_split(a) Perl_ptr_table_split(aTHX_ a) # define ptr_table_store(a,b,c) Perl_ptr_table_store(aTHX_ a,b,c) # define push_scope() Perl_push_scope(aTHX) +# define push_stackinfo(a,b) Perl_push_stackinfo(aTHX_ a,b) # define pv_display(a,b,c,d,e) Perl_pv_display(aTHX_ a,b,c,d,e) # define pv_escape(a,b,c,d,e,f) Perl_pv_escape(aTHX_ a,b,c,d,e,f) # define pv_pretty(a,b,c,d,e,f,g) Perl_pv_pretty(aTHX_ a,b,c,d,e,f,g) @@ -532,10 +536,28 @@ # define repeatcpy Perl_repeatcpy # define require_pv(a) Perl_require_pv(aTHX_ a) # define rninstr Perl_rninstr +# define rpp_extend(a) Perl_rpp_extend(aTHX_ a) +# define rpp_is_lone(a) Perl_rpp_is_lone(aTHX_ a) +# define rpp_obliterate_stack_to(a) Perl_rpp_obliterate_stack_to(aTHX_ a) +# define rpp_pop_1_norc() Perl_rpp_pop_1_norc(aTHX) +# define rpp_popfree_1() Perl_rpp_popfree_1(aTHX) +# define rpp_popfree_2() Perl_rpp_popfree_2(aTHX) +# define rpp_popfree_to(a) Perl_rpp_popfree_to(aTHX_ a) +# define rpp_push_1(a) Perl_rpp_push_1(aTHX_ a) +# define rpp_push_1_norc(a) Perl_rpp_push_1_norc(aTHX_ a) +# define rpp_push_2(a,b) Perl_rpp_push_2(aTHX_ a,b) +# define rpp_replace_1_1(a) Perl_rpp_replace_1_1(aTHX_ a) +# define rpp_replace_2_1(a) Perl_rpp_replace_2_1(aTHX_ a) +# define rpp_stack_is_rc() Perl_rpp_stack_is_rc(aTHX) +# define rpp_try_AMAGIC_1(a,b) Perl_rpp_try_AMAGIC_1(aTHX_ a,b) +# define rpp_try_AMAGIC_2(a,b) Perl_rpp_try_AMAGIC_2(aTHX_ a,b) +# define rpp_xpush_1(a) Perl_rpp_xpush_1(aTHX_ a) +# define rpp_xpush_2(a,b) Perl_rpp_xpush_2(aTHX_ a,b) # define rsignal(a,b) Perl_rsignal(aTHX_ a,b) # define rsignal_state(a) Perl_rsignal_state(aTHX_ a) # define runops_debug() Perl_runops_debug(aTHX) # define runops_standard() Perl_runops_standard(aTHX) +# define runops_wrap() Perl_runops_wrap(aTHX) # define rv2cv_op_cv(a,b) Perl_rv2cv_op_cv(aTHX_ a,b) # define safesyscalloc Perl_safesyscalloc # define safesysfree Perl_safesysfree @@ -729,6 +751,7 @@ # define sv_vsetpvf(a,b,c) Perl_sv_vsetpvf(aTHX_ a,b,c) # define sv_vsetpvf_mg(a,b,c) Perl_sv_vsetpvf_mg(aTHX_ a,b,c) # define sv_vsetpvfn(a,b,c,d,e,f,g) Perl_sv_vsetpvfn(aTHX_ a,b,c,d,e,f,g) +# define switch_argstack(a) Perl_switch_argstack(aTHX_ a) # define switch_to_global_locale() Perl_switch_to_global_locale(aTHX) # define sync_locale() Perl_sync_locale(aTHX) # define taint_env() Perl_taint_env(aTHX) @@ -872,6 +895,7 @@ # define apply(a,b,c) Perl_apply(aTHX_ a,b,c) # define av_extend_guts(a,b,c,d,e) Perl_av_extend_guts(aTHX_ a,b,c,d,e) # define av_nonelem(a,b) Perl_av_nonelem(aTHX_ a,b) +# define av_remove_offset(a) Perl_av_remove_offset(aTHX_ a) # define bind_match(a,b,c) Perl_bind_match(aTHX_ a,b,c) # define boot_core_PerlIO() Perl_boot_core_PerlIO(aTHX) # define boot_core_UNIVERSAL() Perl_boot_core_UNIVERSAL(aTHX) @@ -886,7 +910,7 @@ # define cmpchain_start(a,b,c) Perl_cmpchain_start(aTHX_ a,b,c) # define core_prototype(a,b,c,d) Perl_core_prototype(aTHX_ a,b,c,d) # define coresub_op(a,b,c) Perl_coresub_op(aTHX_ a,b,c) -# define create_eval_scope(a,b) Perl_create_eval_scope(aTHX_ a,b) +# define create_eval_scope(a,b,c) Perl_create_eval_scope(aTHX_ a,b,c) # define croak_caller Perl_croak_caller # define croak_no_mem Perl_croak_no_mem # define croak_popstack Perl_croak_popstack @@ -1150,7 +1174,7 @@ # define get_aux_mg(a) S_get_aux_mg(aTHX_ a) # endif /* defined(PERL_IN_AV_C) */ # if defined(PERL_IN_DEB_C) -# define deb_stack_n(a,b,c,d,e) S_deb_stack_n(aTHX_ a,b,c,d,e) +# define deb_stack_n(a,b,c,d,e,f) S_deb_stack_n(aTHX_ a,b,c,d,e,f) # endif /* defined(PERL_IN_DEB_C) */ # if defined(PERL_IN_DOIO_C) # define argvout_final(a,b,c) S_argvout_final(aTHX_ a,b,c) diff --git a/ext/Devel-Peek/Peek.xs b/ext/Devel-Peek/Peek.xs index 0befd9ee4216..38a7be1f95e3 100644 --- a/ext/Devel-Peek/Peek.xs +++ b/ext/Devel-Peek/Peek.xs @@ -339,11 +339,14 @@ S_do_dump(pTHX_ SV *const sv, I32 lim) static OP * S_pp_dump(pTHX) { - dSP; - const I32 lim = PL_op->op_private == 2 ? (I32)POPi : 4; - dPOPss; - S_do_dump(aTHX_ sv, lim); - RETPUSHUNDEF; + I32 lim = 4; + if (PL_op->op_private == 2) { + lim = (I32)SvIVx(*PL_stack_sp); + rpp_popfree_1(); + } + S_do_dump(aTHX_ *PL_stack_sp, lim); + rpp_replace_1_1(&PL_sv_undef); + return NORMAL; } static OP * diff --git a/ext/Devel-Peek/t/Peek.t b/ext/Devel-Peek/t/Peek.t index d6267e158b29..d0a77e8c0221 100644 --- a/ext/Devel-Peek/t/Peek.t +++ b/ext/Devel-Peek/t/Peek.t @@ -143,7 +143,7 @@ END { do_test('assignment of immediate constant (string)', $a = "foo", 'SV = PV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(POK,(?:IsCOW,)?pPOK\\) PV = $ADDR "foo"\\\0 CUR = 3 @@ -154,7 +154,7 @@ do_test('assignment of immediate constant (string)', do_test('immediate constant (string)', "bar", 'SV = PV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(.*POK,READONLY,(?:IsCOW,)?pPOK\\) # $] < 5.021005 FLAGS = \\(.*POK,(?:IsCOW,)?READONLY,PROTECT,pPOK\\) # $] >=5.021005 PV = $ADDR "bar"\\\0 @@ -166,14 +166,14 @@ do_test('immediate constant (string)', do_test('assignment of immediate constant (integer)', $b = 123, 'SV = IV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(IOK,pIOK\\) IV = 123'); do_test('immediate constant (integer)', 456, 'SV = IV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(.*IOK,READONLY,pIOK\\) # $] < 5.021005 FLAGS = \\(.*IOK,READONLY,PROTECT,pIOK\\) # $] >=5.021005 IV = 456'); @@ -181,7 +181,7 @@ do_test('immediate constant (integer)', do_test('assignment of immediate constant (integer)', $c = 456, 'SV = IV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\($PADMY,IOK,pIOK\\) IV = 456'); @@ -193,7 +193,7 @@ do_test('assignment of immediate constant (integer)', my $type = do_test('result of addition', $c + $d, 'SV = ([NI])V\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(PADTMP,\1OK,p\1OK\\) # $] < 5.019003 FLAGS = \\(\1OK,p\1OK\\) # $] >=5.019003 \1V = 456'); @@ -206,7 +206,7 @@ do_test('floating point value', || $Config{ccflags} =~ /-DPERL_(?:NO_COW|OLD_COPY_ON_WRITE)\b/ ? 'SV = PVNV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(NOK,pNOK\\) IV = \d+ NV = 789\\.(?:1(?:000+\d+)?|0999+\d+) @@ -215,7 +215,7 @@ do_test('floating point value', LEN = \\d+' : 'SV = PVNV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(NOK,pNOK\\) IV = \d+ NV = 789\\.(?:1(?:000+\d+)?|0999+\d+) @@ -224,7 +224,7 @@ do_test('floating point value', do_test('integer constant', 0xabcd, 'SV = IV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(.*IOK,READONLY,pIOK\\) # $] < 5.021005 FLAGS = \\(.*IOK,READONLY,PROTECT,pIOK\\) # $] >=5.021005 IV = 43981'); @@ -240,7 +240,7 @@ do_test('undef', do_test('reference to scalar', \$a, 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PV\\($ADDR\\) at $ADDR @@ -295,7 +295,7 @@ if ($type eq 'N') { do_test('reference to array', [$b,$c], 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PVAV\\($ADDR\\) at $ADDR @@ -315,7 +315,7 @@ do_test('reference to array', do_test('reference to hash', {$b=>$c}, 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PVHV\\($ADDR\\) at $ADDR @@ -333,7 +333,7 @@ do_test('reference to hash', do_test('reference to anon sub with empty prototype', sub(){@_}, 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PVCV\\($ADDR\\) at $ADDR @@ -359,7 +359,7 @@ do_test('reference to anon sub with empty prototype', do_test('reference to named subroutine without prototype', \&do_test, 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PVCV\\($ADDR\\) at $ADDR @@ -396,7 +396,7 @@ do_test('reference to named subroutine without prototype', do_test('reference to regexp', qr(tic), 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = REGEXP\\($ADDR\\) at $ADDR @@ -471,7 +471,7 @@ do_test('reference to regexp', do_test('reference to blessed hash', (bless {}, "Tac"), 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PVHV\\($ADDR\\) at $ADDR @@ -489,7 +489,7 @@ do_test('reference to blessed hash', do_test('typeglob', *a, 'SV = PVGV\\($ADDR\\) at $ADDR - REFCNT = 5 + REFCNT = \d+ FLAGS = \\(MULTI(?:,IN_PAD)?\\) NAME = "a" NAMELEN = 1 @@ -525,7 +525,7 @@ foreach my $ref (\$cp100_bytes, \$cp0_bytes, \$cp200_bytes) { do_test('string with Unicode', chr(256).chr(0).chr(512), 'SV = PV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\((?:PADTMP,)?POK,READONLY,pPOK,UTF8\\) # $] < 5.019003 FLAGS = \\((?:PADTMP,)?POK,(?:IsCOW,)?pPOK,UTF8\\) # $] >=5.019003 PV = $ADDR "' . $cp100_bytes @@ -540,7 +540,7 @@ do_test('string with Unicode', do_test('reference to hash containing Unicode', {chr(256)=>chr(512)}, 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PVHV\\($ADDR\\) at $ADDR @@ -569,7 +569,7 @@ $x=~/.??/g; do_test('scalar with pos magic', $x, 'SV = PVMG\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\($PADMY,SMG,POK,(?:IsCOW,)?pPOK\\) IV = \d+ NV = 0 @@ -601,7 +601,7 @@ if (${^TAINT}) { do_test('tainted value in %ENV', $ENV{PATH}=@ARGV, # scalar(@ARGV) is a handy known tainted value 'SV = PVMG\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(GMG,SMG,RMG(?:,POK)?(?:,pIOK)?,pPOK\\) IV = 0 NV = 0 @@ -631,7 +631,7 @@ if (${^TAINT}) { do_test('blessed reference', bless(\\undef, 'Foobar'), 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PVMG\\($ADDR\\) at $ADDR @@ -656,7 +656,7 @@ sub const () { do_test('constant subroutine', \&const, 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PVCV\\($ADDR\\) at $ADDR @@ -692,7 +692,7 @@ do_test('constant subroutine', do_test('isUV should show on PVMG', do { my $v = $1; $v = ~0; $v }, 'SV = PVMG\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(IOK,pIOK,IsUV\\) UV = \d+ NV = 0 @@ -701,7 +701,7 @@ do_test('isUV should show on PVMG', do_test('IO', *STDOUT{IO}, 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PVIO\\($ADDR\\) at $ADDR @@ -726,7 +726,7 @@ do_test('IO', do_test('FORMAT', *PIE{FORMAT}, 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PVFM\\($ADDR\\) at $ADDR @@ -753,7 +753,7 @@ do_test('FORMAT', do_test('blessing to a class with embedded NUL characters', (bless {}, "\0::foo::\n::baz::\t::\0"), 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PVHV\\($ADDR\\) at $ADDR @@ -771,7 +771,7 @@ do_test('blessing to a class with embedded NUL characters', do_test('ENAME on a stash', \%RWOM::, 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PVHV\\($ADDR\\) at $ADDR @@ -794,7 +794,7 @@ do_test('ENAME on a stash', do_test('ENAMEs on a stash', \%RWOM::, 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PVHV\\($ADDR\\) at $ADDR @@ -818,7 +818,7 @@ undef %RWOM::; do_test('ENAMEs on a stash with no NAME', \%RWOM::, 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PVHV\\($ADDR\\) at $ADDR @@ -843,7 +843,7 @@ my $b = %small; do_test('small hash', \%small, 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PVHV\\($ADDR\\) at $ADDR @@ -869,7 +869,7 @@ $b = keys %small; do_test('small hash after keys', \%small, 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PVHV\\($ADDR\\) at $ADDR @@ -899,7 +899,7 @@ $b = %small; do_test('small hash after keys and scalar', \%small, 'SV = $RV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = PVHV\\($ADDR\\) at $ADDR @@ -928,7 +928,7 @@ do_test('small hash after keys and scalar', @array = 1..3; do_test('Dump @array', '@array', <<'ARRAY', '', undef, 1); SV = PVAV\($ADDR\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \(\) ARRAY = $ADDR FILL = 2 @@ -953,7 +953,7 @@ ARRAY do_test('Dump @array,1', '@array,1', <<'ARRAY', '', undef, 1); SV = PVAV\($ADDR\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \(\) ARRAY = $ADDR FILL = 2 @@ -969,7 +969,7 @@ ARRAY %hash = 1..2; do_test('Dump %hash', '%hash', <<'HASH', '', undef, 1); SV = PVHV\($ADDR\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \(SHAREKEYS\) ARRAY = $ADDR \(0:7, 1:1\) hash quality = 100.0% @@ -986,7 +986,7 @@ HASH tie %tied, "Tie::StdHash"; do_test('Dump %tied', '%tied', <<'HASH', "", undef, 1); SV = PVHV\($ADDR\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \(RMG,SHAREKEYS\) MAGIC = $ADDR MG_VIRTUAL = &PL_vtbl_pack @@ -1015,7 +1015,7 @@ HASH $_ = "hello"; do_test('rvalue substr', 'substr $_, 1, 2', <<'SUBSTR', '', undef, 1); SV = PV\($ADDR\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \(PADTMP,POK,pPOK\) PV = $ADDR "el"\\0 CUR = 2 @@ -1061,7 +1061,7 @@ unless ($Config{useithreads}) { do_test('regular string constant', perl, 'SV = PV\\($ADDR\\) at $ADDR - REFCNT = 5 + REFCNT = \d+ FLAGS = \\(PADMY,POK,READONLY,(?:IsCOW,)?pPOK\\) # $] < 5.021005 FLAGS = \\(POK,(?:IsCOW,)?READONLY,pPOK\\) # $] >=5.021005 PV = $ADDR "rule"\\\0 @@ -1074,7 +1074,7 @@ unless ($Config{useithreads}) { do_test('string constant now an FBM', perl, 'SV = PVMG\\($ADDR\\) at $ADDR - REFCNT = 5 + REFCNT = \d+ FLAGS = \\($PADMY,SMG,POK,(?:IsCOW,)?READONLY,(?:IsCOW,)?pPOK\\) PV = $ADDR "rule"\\\0 CUR = 4 @@ -1094,7 +1094,7 @@ unless ($Config{useithreads}) { do_test('string constant still an FBM', perl, 'SV = PVMG\\($ADDR\\) at $ADDR - REFCNT = 5 + REFCNT = \d+ FLAGS = \\($PADMY,SMG,POK,(?:IsCOW,)?READONLY,(?:IsCOW,)?pPOK\\) PV = $ADDR "rule"\\\0 CUR = 4 @@ -1112,7 +1112,7 @@ unless ($Config{useithreads}) { do_test('regular string constant', beer, 'SV = PV\\($ADDR\\) at $ADDR - REFCNT = 6 + REFCNT = \d+ FLAGS = \\(PADMY,POK,READONLY,(?:IsCOW,)?pPOK\\) # $] < 5.021005 FLAGS = \\(POK,(?:IsCOW,)?READONLY,pPOK\\) # $] >=5.021005 PV = $ADDR "foam"\\\0 @@ -1124,7 +1124,7 @@ unless ($Config{useithreads}) { is(study beer, 1, "Our studies were successful"); do_test('string constant quite unaffected', beer, 'SV = PV\\($ADDR\\) at $ADDR - REFCNT = 6 + REFCNT = \d+ FLAGS = \\(PADMY,POK,READONLY,(?:IsCOW,)?pPOK\\) # $] < 5.021005 FLAGS = \\(POK,(?:IsCOW,)?READONLY,pPOK\\) # $] >=5.021005 PV = $ADDR "foam"\\\0 @@ -1134,7 +1134,7 @@ unless ($Config{useithreads}) { '); my $want = 'SV = PVMG\\($ADDR\\) at $ADDR - REFCNT = 6 + REFCNT = \d+ FLAGS = \\($PADMY,SMG,POK,(?:IsCOW,)?READONLY,(?:IsCOW,)?pPOK\\) PV = $ADDR "foam"\\\0 CUR = 4 @@ -1161,7 +1161,7 @@ unless ($Config{useithreads}) { do_test('string constant still FBMed', beer, $want); do_test('second string also unaffected', $pie, 'SV = PV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\($PADMY,POK,(?:IsCOW,)?pPOK\\) PV = $ADDR "good"\\\0 CUR = 4 @@ -1184,7 +1184,7 @@ unless ($Config{useithreads}) { do_test('UTF-8 in a regular expression', qr/\x{100}/, 'SV = IV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = REGEXP\\($ADDR\\) at $ADDR @@ -1259,7 +1259,7 @@ do_test('UTF-8 in a regular expression', do_test('Branch Reset regexp', qr/(?|(foo)|(bar))(?|(baz)|(bop))/, 'SV = IV\\($ADDR\\) at $ADDR - REFCNT = 1 + REFCNT = \d+ FLAGS = \\(ROK\\) RV = $ADDR SV = REGEXP\\($ADDR\\) at $ADDR diff --git a/ext/XS-APItest/APItest.pm b/ext/XS-APItest/APItest.pm index fb14f78dd5cf..5e8f716a94f9 100644 --- a/ext/XS-APItest/APItest.pm +++ b/ext/XS-APItest/APItest.pm @@ -4,7 +4,7 @@ use strict; use warnings; use Carp; -our $VERSION = '1.30'; +our $VERSION = '1.31'; require XSLoader; diff --git a/ext/XS-APItest/APItest.xs b/ext/XS-APItest/APItest.xs index a7f1d5f0110b..b8e0a7f6ebd0 100644 --- a/ext/XS-APItest/APItest.xs +++ b/ext/XS-APItest/APItest.xs @@ -690,16 +690,20 @@ THX_run_cleanup(pTHX_ void *cleanup_code_ref) POPSTACK; } +/* Note that this is a pp function attached to an OP */ + STATIC OP * THX_pp_establish_cleanup(pTHX) { - dSP; SV *cleanup_code_ref; - cleanup_code_ref = newSVsv(POPs); + cleanup_code_ref = newSVsv(*PL_stack_sp); + rpp_popfree_1(); SAVEFREESV(cleanup_code_ref); SAVEDESTRUCTOR_X(THX_run_cleanup, cleanup_code_ref); - if(GIMME_V != G_VOID) PUSHs(&PL_sv_undef); - RETURN; + if(GIMME_V != G_VOID) + rpp_push_1(&PL_sv_undef); + return NORMAL; + ; } STATIC OP * @@ -1513,6 +1517,43 @@ test_bool_internals_func(SV *true_sv, SV *false_sv, const char *msg) { SvREFCNT_dec(false_sv); return failed; } + + +/* A simplified/fake replacement for pp_add, which tests the pp + * function wrapping API, XSPP_wrapped() for a fixed number of args*/ + +XSPP_wrapped(my_pp_add, 2, 0) +{ + SV *ret; + dSP; + SV *r = POPs; + SV *l = TOPs; + if (SvROK(l)) + l = SvRV(l); + if (SvROK(r)) + r = SvRV(r); + ret = newSViv( SvIV(l) + SvIV(r)); + sv_2mortal(ret); + SETs(ret); + RETURN; +} + + +/* A copy of pp_anonlist, which tests the pp + * function wrapping API, XSPP_wrapped() for a list*/ + +XSPP_wrapped(my_pp_anonlist, 0, 1) +{ + dSP; dMARK; + const I32 items = SP - MARK; + SV * const av = MUTABLE_SV(av_make(items, MARK+1)); + SP = MARK; + mXPUSHs((PL_op->op_flags & OPf_SPECIAL) + ? newRV_noinc(av) : av); + RETURN; +} + + #include "const-c.inc" MODULE = XS::APItest PACKAGE = XS::APItest @@ -2566,19 +2607,27 @@ test_EXTEND(max_offset, nsv, use_ss) SV *nsv; bool use_ss; PREINIT: - SV **sp = PL_stack_max + max_offset; + SV **new_sp = PL_stack_max + max_offset; + SSize_t new_offset = new_sp - PL_stack_base; PPCODE: if (use_ss) { SSize_t n = (SSize_t)SvIV(nsv); - EXTEND(sp, n); - *(sp + n) = NULL; + EXTEND(new_sp, n); + new_sp = PL_stack_base + new_offset; + assert(new_sp + n <= PL_stack_max); + if ((new_sp + n) > PL_stack_sp) + *(new_sp + n) = NULL; } else { IV n = SvIV(nsv); - EXTEND(sp, n); - *(sp + n) = NULL; + EXTEND(new_sp, n); + new_sp = PL_stack_base + new_offset; + assert(new_sp + n <= PL_stack_max); + if ((new_sp + n) > PL_stack_sp) + *(new_sp + n) = NULL; } - *PL_stack_max = NULL; + if (PL_stack_max > PL_stack_sp) + *PL_stack_max = NULL; void @@ -4092,6 +4141,10 @@ CODE: PerlInterpreter *interp_dup; /* The duplicate interpreter */ int oldscope = 1; /* We are responsible for all scopes */ + /* push a ref-counted and non-RC stackinfo to see how they get cloned */ + push_stackinfo(PERLSI_UNKNOWN, 1); + push_stackinfo(PERLSI_UNKNOWN, 0); + interp_dup = perl_clone(interp, CLONEf_COPY_STACKS | CLONEf_CLONE_HOST ); /* destroy old perl */ @@ -4112,12 +4165,22 @@ CODE: /* switch to new perl */ PERL_SET_CONTEXT(interp_dup); + /* check and pop the stackinfo's pushed above */ +#ifdef PERL_RC_STACK + assert(!AvREAL(PL_curstack)); +#endif + pop_stackinfo(); +#ifdef PERL_RC_STACK + assert(AvREAL(PL_curstack)); +#endif + pop_stackinfo(); + /* continue after 'clone_with_stack' */ if (interp_dup->Iop) interp_dup->Iop = interp_dup->Iop->op_next; /* run with new perl */ - Perl_runops_standard(interp_dup); + CALLRUNOPS(interp_dup); /* We may have additional unclosed scopes if fork() was called * from within a BEGIN block. See perlfork.pod for more details. @@ -4736,6 +4799,24 @@ sv_streq_flags(SV *sv1, SV *sv2, U32 flags) OUTPUT: RETVAL +void +set_custom_pp_func(sv) + SV *sv; + PPCODE: + /* replace the pp func of the next op */ + OP* o = PL_op->op_next; + if (o->op_type == OP_ADD) + o->op_ppaddr = my_pp_add; + else if (o->op_type == OP_ANONLIST) + o->op_ppaddr = my_pp_anonlist; + else + croak("set_custom_pp_func: op_next is not an OP_ADD\n"); + + /* the single SV arg is passed through */ + PERL_UNUSED_ARG(sv); + XSRETURN(1); + + MODULE = XS::APItest PACKAGE = XS::APItest::AUTOLOADtest int diff --git a/ext/XS-APItest/t/magic.t b/ext/XS-APItest/t/magic.t index 8189523dc1a9..a192ce51612b 100644 --- a/ext/XS-APItest/t/magic.t +++ b/ext/XS-APItest/t/magic.t @@ -101,12 +101,21 @@ is $@, "", 'PERL_MAGIC_ext is permitted on read-only things'; $j = "blorp"; my_av_store(\@a,0,$j); }; - my $base_refcount = 2; # not sure where these come from. - if (\$a[0] == \$j) { - # in this case we expect to have an extra 2 refcounts, + + # what extra refcount is added to the SV by virtue of being on the + # stack? + my $extra = (Internals::stack_refcounted() & 1) ? 1 : 0; + + # Evaluate this boolean as a separate statement, so the two + # temporary \ refs are freed before we start comparing reference + # counts + my $is_same_SV = \$a[0] == \$j; + + if ($is_same_SV) { + # in this case we expect to have 2 refcounts, # one from $a[0] and one from $j itself. - is( sv_refcnt($j), $base_refcount + 2, - "\$a[0] is \$j, so refcount(\$j) should be 4"); + is( sv_refcnt($j), 2 + $extra, + "\$a[0] is \$j, so refcount(\$j) should be 2"); } else { # Note this branch isn't exercised. Whether by design # or not. I leave it here because it is a possible valid @@ -115,10 +124,10 @@ is $@, "", 'PERL_MAGIC_ext is permitted on read-only things'; diag "av_store has changed behavior - please review this test"; TODO:{ local $TODO = "av_store bug stores even if it dies during magic"; - # in this case we expect to have only 1 extra refcount, + # in this case we expect to have only 1 refcount, # from $j itself. - is( sv_refcnt($j), $base_refcount + 1, - "\$a[0] is not \$j, so refcount(\$j) should be 3"); + is( sv_refcnt($j), 1 + $extra, + "\$a[0] is not \$j, so refcount(\$j) should be 1"); } } } diff --git a/ext/XS-APItest/t/xsppwrap.t b/ext/XS-APItest/t/xsppwrap.t new file mode 100644 index 000000000000..a7f723cf7a51 --- /dev/null +++ b/ext/XS-APItest/t/xsppwrap.t @@ -0,0 +1,63 @@ +#!perl +# +# Test the XSPP_wrapped() macro. +# +# The XS function set_custom_pp_func() modifies the pp_addr value of the +# op following it to point to a pp function written in the traditional +# non-refcounted style (POPs etc), but which is declared using +# XSPP_wrapped(), so on PERL_RC_STACK builds, the ref count handling of +# the args and return value should still be correct. + +use warnings; +use strict; +use Test::More; +use Config; +use XS::APItest qw(set_custom_pp_func); + +my ($count, $ret, $c); +sub DESTROY { $count++ } + + +$count = 0; +{ + my $nine = 9; + # set_custom_pp_func() overrides the pp func for the next op, + # which is the pp_add + ($ret, $c) = (bless(\$nine)+set_custom_pp_func(15), $count); +} + +is($ret, 24, "custom add returns correct value"); +is($c, 0, "custom add: arg not yet freed"); +is($count, 1, "custom add: arg now freed"); + + +$count = 0; +{ + my $nine = 9; + my $ten = 10; + my $eleven = 11; + # set_custom_pp_func() overrides the pp func for the next op, + # which is the anonlist + ($ret, $c) = ( + [ + bless(\$nine), + bless(\$ten), + set_custom_pp_func(bless(\$eleven)), + ], + $count + ); +} + +ok(defined $ret, "custom anonlist returns defined value"); +is(${$ret->[0]}, 9, "custom anonlist arg [0]"); +is(${$ret->[1]}, 10, "custom anonlist arg [1]"); +is(${$ret->[2]}, 11, "custom anonlist arg [2]"); +is($c, 0, "custom anonlist args not yet freed"); +is($count, 0, "custom anonlist args not yet freed 2"); + +undef $ret; + +is($count, 3, "custom anonlist args now freed"); + + +done_testing(); diff --git a/gv.c b/gv.c index 0ce2a2ac271c..31798210c4a9 100644 --- a/gv.c +++ b/gv.c @@ -3354,10 +3354,11 @@ Perl_gv_handler(pTHX_ HV *stash, I32 id) */ bool -Perl_try_amagic_un(pTHX_ int method, int flags) { - dSP; +Perl_try_amagic_un(pTHX_ int method, int flags) +{ SV* tmpsv; - SV* const arg = TOPs; + SV* const arg = PL_stack_sp[0]; + bool is_rc = rpp_stack_is_rc(); SvGETMAGIC(arg); @@ -3370,22 +3371,33 @@ Perl_try_amagic_un(pTHX_ int method, int flags) { * then assign the returned value to targ and return that; * otherwise return the value directly */ + SV *targ = tmpsv; if ( (PL_opargs[PL_op->op_type] & OA_TARGLEX) && (PL_op->op_private & OPpTARGET_MY)) { - dTARGET; - sv_setsv(TARG, tmpsv); - SETTARG; + targ = PAD_SV(PL_op->op_targ); + sv_setsv(targ, tmpsv); + SvSETMAGIC(targ); + } + if (targ != arg) { + *PL_stack_sp = targ; + if (is_rc) { + SvREFCNT_inc_NN(targ); + SvREFCNT_dec_NN(arg); + } } - else - SETs(tmpsv); - PUTBACK; return TRUE; } - if ((flags & AMGf_numeric) && SvROK(arg)) - *sp = sv_2num(arg); + if ((flags & AMGf_numeric) && SvROK(arg)) { + PL_stack_sp[0] = tmpsv = sv_2num(arg); + if (is_rc) { + SvREFCNT_inc_NN(tmpsv); + SvREFCNT_dec_NN(arg); + } + } + return FALSE; } @@ -3546,10 +3558,11 @@ Perl_amagic_applies(pTHX_ SV *sv, int method, int flags) */ bool -Perl_try_amagic_bin(pTHX_ int method, int flags) { - dSP; - SV* const left = TOPm1s; - SV* const right = TOPs; +Perl_try_amagic_bin(pTHX_ int method, int flags) +{ + SV* left = PL_stack_sp[-1]; + SV* right = PL_stack_sp[0]; + bool is_rc = rpp_stack_is_rc(); SvGETMAGIC(left); if (left != right) @@ -3564,51 +3577,78 @@ Perl_try_amagic_bin(pTHX_ int method, int flags) { (mutator ? AMGf_assign: 0) | (flags & AMGf_numarg)); if (tmpsv) { - (void)POPs; + PL_stack_sp--; + if (is_rc) + SvREFCNT_dec_NN(right); /* where the op is one of the two forms: * $x op= $y * $lex = $x op $y (where the assign is optimised away) * then assign the returned value to targ and return that; * otherwise return the value directly */ + SV *targ = tmpsv;; if ( mutator || ( (PL_opargs[PL_op->op_type] & OA_TARGLEX) && (PL_op->op_private & OPpTARGET_MY))) { - dTARG; - TARG = mutator ? *SP : PAD_SV(PL_op->op_targ); - sv_setsv(TARG, tmpsv); - SETTARG; + targ = mutator ? left : PAD_SV(PL_op->op_targ); + sv_setsv(targ, tmpsv); + SvSETMAGIC(targ); + } + if (targ != left) { + *PL_stack_sp = targ; + if (is_rc) { + SvREFCNT_inc_NN(targ); + SvREFCNT_dec_NN(left); + } } - else - SETs(tmpsv); - PUTBACK; return TRUE; } } - if(left==right && SvGMAGICAL(left)) { - SV * const left = sv_newmortal(); - *(sp-1) = left; + /* if the same magic value appears on both sides, replace the LH one + * with a copy and call get magic on the RH one, so that magic gets + * called twice with possibly two different returned values */ + if (left == right && SvGMAGICAL(left)) { + SV * const tmpsv = is_rc ? newSV_type(SVt_NULL) : sv_newmortal(); /* Print the uninitialized warning now, so it includes the vari- able name. */ if (!SvOK(right)) { - if (ckWARN(WARN_UNINITIALIZED)) report_uninit(right); - sv_setbool(left, FALSE); + if (ckWARN(WARN_UNINITIALIZED)) + report_uninit(right); + sv_setbool(tmpsv, FALSE); } - else sv_setsv_flags(left, right, 0); + else + sv_setsv_flags(tmpsv, right, 0); + if (is_rc) + SvREFCNT_dec_NN(left); + left = PL_stack_sp[-1] = tmpsv; SvGETMAGIC(right); } + if (flags & AMGf_numeric) { - if (SvROK(TOPm1s)) - *(sp-1) = sv_2num(TOPm1s); - if (SvROK(right)) - *sp = sv_2num(right); + SV *tmpsv; + if (SvROK(left)) { + PL_stack_sp[-1] = tmpsv = sv_2num(left); + if (is_rc) { + SvREFCNT_inc_NN(tmpsv); + SvREFCNT_dec_NN(left); + } + } + if (SvROK(right)) { + PL_stack_sp[0] = tmpsv = sv_2num(right); + if (is_rc) { + SvREFCNT_inc_NN(tmpsv); + SvREFCNT_dec_NN(right); + } + } } + return FALSE; } + /* =for apidoc amagic_deref_call @@ -4079,7 +4119,7 @@ Perl_amagic_call(pTHX_ SV *left, SV *right, int method, int flags) { dSP; - BINOP myop; + UNOP myop; SV* res; const bool oldcatch = CATCH_GET; I32 oldmark, nret; @@ -4091,10 +4131,11 @@ Perl_amagic_call(pTHX_ SV *left, SV *right, int method, int flags) ? G_SCALAR : GIMME_V; CATCH_SET(TRUE); - Zero(&myop, 1, BINOP); - myop.op_last = (OP *) &myop; - myop.op_next = NULL; + Zero(&myop, 1, UNOP); myop.op_flags = OPf_STACKED; + myop.op_ppaddr = PL_ppaddr[OP_ENTERSUB]; + myop.op_type = OP_ENTERSUB; + switch (gimme) { case G_VOID: @@ -4134,9 +4175,7 @@ Perl_amagic_call(pTHX_ SV *left, SV *right, int method, int flags) PUSHs(MUTABLE_SV(cv)); PUTBACK; oldmark = TOPMARK; - - if ((PL_op = PL_ppaddr[OP_ENTERSUB](aTHX))) - CALLRUNOPS(aTHX); + CALLRUNOPS(aTHX); LEAVE; SPAGAIN; nret = SP - (PL_stack_base + oldmark); diff --git a/inline.h b/inline.h index b09da6c38382..2b6bc7c373e8 100644 --- a/inline.h +++ b/inline.h @@ -216,6 +216,24 @@ Perl_av_new_alloc(pTHX_ SSize_t size, bool zeroflag) } +/* remove (AvARRAY(av) - AvALLOC(av)) offset from empty array */ + +PERL_STATIC_INLINE void +Perl_av_remove_offset(pTHX_ AV *av) +{ + PERL_ARGS_ASSERT_AV_REMOVE_OFFSET; + assert(AvFILLp(av) == -1); + SSize_t i = AvARRAY(av) - AvALLOC(av); + if (i) { + AvARRAY(av) = AvALLOC(av); + AvMAX(av) += i; +#ifdef PERL_RC_STACK + Zero(AvALLOC(av), i, SV*); +#endif + } +} + + /* ------------------------------- cv.h ------------------------------- */ /* @@ -380,6 +398,392 @@ Perl_POPMARK(pTHX) return *PL_markstack_ptr--; } +/* +=for apidoc_section $rpp + +=for apidoc rpp_extend +Ensures that there is space on the stack to push C items, extending it +if necessary. + +=cut +*/ + +PERL_STATIC_INLINE void +Perl_rpp_extend(pTHX_ SSize_t n) +{ + PERL_ARGS_ASSERT_RPP_EXTEND; + + EXTEND_HWM_SET(PL_stack_sp, n); +#ifndef STRESS_REALLOC + if (UNLIKELY(_EXTEND_NEEDS_GROW(PL_stack_sp, n))) +#endif + { + (void)stack_grow(PL_stack_sp, PL_stack_sp, n); + } +} + + +/* +=for apidoc rpp_popfree_to + +Pop and free all items on the argument stack above C. On return, +C will be equal to C. + +=cut +*/ + +PERL_STATIC_INLINE void +Perl_rpp_popfree_to(pTHX_ SV **sp) +{ + PERL_ARGS_ASSERT_RPP_POPFREE_TO; + + assert(sp <= PL_stack_sp); +#ifdef PERL_RC_STACK +# ifdef DEBUG_LEAKING_SCALARS + assert(rpp_stack_is_rc()); +# endif + while (PL_stack_sp > sp) { + SV *sv = *PL_stack_sp--; + SvREFCNT_dec(sv); + } +#else + PL_stack_sp = sp; +#endif +} + + +/* +=for apidoc rpp_popfree_1 + +Pop and free the top item on the argument stack and update C. + +=cut +*/ + +PERL_STATIC_INLINE void +Perl_rpp_popfree_1(pTHX) +{ + PERL_ARGS_ASSERT_RPP_POPFREE_1; + +#ifdef PERL_RC_STACK +# ifdef DEBUG_LEAKING_SCALARS + assert(rpp_stack_is_rc()); +# endif + SV *sv = *PL_stack_sp--; + SvREFCNT_dec(sv); +#else + PL_stack_sp--; +#endif +} + + +/* +=for apidoc rpp_popfree_2 + +Pop and free the top two items on the argument stack and update +C. + +=cut +*/ + + +PERL_STATIC_INLINE void +Perl_rpp_popfree_2(pTHX) +{ + PERL_ARGS_ASSERT_RPP_POPFREE_2; + +#ifdef PERL_RC_STACK +# ifdef DEBUG_LEAKING_SCALARS + assert(rpp_stack_is_rc()); +# endif + for (int i = 0; i < 2; i++) { + SV *sv = *PL_stack_sp--; + SvREFCNT_dec(sv); + } +#else + PL_stack_sp -= 2; +#endif +} + +/* +=for apidoc rpp_pop_1_norc + +Pop and return the top item off the argument stack and update +C. It's similar to rpp_popfree_1(), except that it actually +returns a value, and it I decrement the SV's reference count. +On non-C builds it actually increments the SV's reference +count. + +This is useful in cases where the popped value is immediately embedded +somewhere e.g. via av_store(), allowing you skip decrementing and then +immediately incrementing the reference count again (and risk prematurely +freeing the SV if it had a RC of 1). On non-RC builds, the reference count +bookkeeping still works too, which is why it should be used rather than +a simple C<*PL_stack_sp-->. + +=cut +*/ + +PERL_STATIC_INLINE SV* +Perl_rpp_pop_1_norc(pTHX) +{ + PERL_ARGS_ASSERT_RPP_POP_1_NORC + + SV *sv = *PL_stack_sp--; + +#ifndef PERL_RC_STACK + SvREFCNT_inc(sv); +#else +# ifdef DEBUG_LEAKING_SCALARS + assert(rpp_stack_is_rc()); +# endif +#endif + return sv; +} + + + +/* +=for apidoc rpp_push_1 +=for apidoc_item rpp_push_2 +=for apidoc_item rpp_xpush_1 +=for apidoc_item rpp_xpush_2 + +Push one or two SVs onto the stack, incrementing their reference counts +and updating C. With the C variants, it extends the stack +first. + +=cut +*/ + +PERL_STATIC_INLINE void +Perl_rpp_push_1(pTHX_ SV *sv) +{ + PERL_ARGS_ASSERT_RPP_PUSH_1; + + *++PL_stack_sp = sv; +#ifdef PERL_RC_STACK +# ifdef DEBUG_LEAKING_SCALARS + assert(rpp_stack_is_rc()); +# endif + SvREFCNT_inc_simple_void_NN(sv); +#endif +} + +PERL_STATIC_INLINE void +Perl_rpp_push_2(pTHX_ SV *sv1, SV *sv2) +{ + PERL_ARGS_ASSERT_RPP_PUSH_2; + + *++PL_stack_sp = sv1; + *++PL_stack_sp = sv2; +#ifdef PERL_RC_STACK +# ifdef DEBUG_LEAKING_SCALARS + assert(rpp_stack_is_rc()); +# endif + SvREFCNT_inc_simple_void_NN(sv1); + SvREFCNT_inc_simple_void_NN(sv2); +#endif +} + +PERL_STATIC_INLINE void +Perl_rpp_xpush_1(pTHX_ SV *sv) +{ + PERL_ARGS_ASSERT_RPP_XPUSH_1; + + rpp_extend(1); + rpp_push_1(sv); +} + +PERL_STATIC_INLINE void +Perl_rpp_xpush_2(pTHX_ SV *sv1, SV *sv2) +{ + PERL_ARGS_ASSERT_RPP_XPUSH_2; + + rpp_extend(2); + rpp_push_2(sv1, sv2); +} + + +/* +=for apidoc rpp_push_1_norc + +Push C onto the stack without incrementing its reference count, and +update C. On non-PERL_RC_STACK builds, mortalise too. + +This is most useful where an SV has just been created and already has a +reference count of 1, but has not yet been anchored anywhere. + +=cut +*/ + +PERL_STATIC_INLINE void +Perl_rpp_push_1_norc(pTHX_ SV *sv) +{ + PERL_ARGS_ASSERT_RPP_PUSH_1; + + *++PL_stack_sp = sv; +#ifdef PERL_RC_STACK +# ifdef DEBUG_LEAKING_SCALARS + assert(rpp_stack_is_rc()); +# endif +#else + sv_2mortal(sv); +#endif +} + + +/* +=for apidoc rpp_replace_1_1 + +Replace the current top stack item with C, while suitably adjusting +reference counts. Equivalent to rpp_popfree_1(); rpp_push_1(sv), but +is more efficient and handles both SVs being the same. + +=cut +*/ + +PERL_STATIC_INLINE void +Perl_rpp_replace_1_1(pTHX_ SV *sv) +{ + PERL_ARGS_ASSERT_RPP_REPLACE_1_1; + +#ifdef PERL_RC_STACK +# ifdef DEBUG_LEAKING_SCALARS + assert(rpp_stack_is_rc()); +# endif + SV *oldsv = *PL_stack_sp; + *PL_stack_sp = sv; + SvREFCNT_inc_simple_void_NN(sv); + SvREFCNT_dec(oldsv); +#else + *PL_stack_sp = sv; +#endif +} + + +/* +=for apidoc rpp_replace_2_1 + +Replace the current top two stack items with C, while suitably +adjusting reference counts. Equivalent to rpp_popfree_2(); +rpp_push_1(sv), but is more efficient and handles SVs being the same. + +=cut +*/ + +PERL_STATIC_INLINE void +Perl_rpp_replace_2_1(pTHX_ SV *sv) +{ + PERL_ARGS_ASSERT_RPP_REPLACE_2_1; + +#ifdef PERL_RC_STACK +# ifdef DEBUG_LEAKING_SCALARS + assert(rpp_stack_is_rc()); +# endif + /* replace PL_stack_sp[-1] first; leave PL_stack_sp[0] in place while + * we free [-1], so if an exception occurs, [0] will still be freed. + */ + SV *oldsv = PL_stack_sp[-1]; + PL_stack_sp[-1] = sv; + SvREFCNT_inc_simple_void_NN(sv); + SvREFCNT_dec(oldsv); + oldsv = *PL_stack_sp--; + SvREFCNT_dec(oldsv); +#else + *--PL_stack_sp = sv; +#endif +} + + +/* +=for apidoc rpp_try_AMAGIC_1 +=for apidoc_item rpp_try_AMAGIC_2 + +Check whether either of the one or two SVs at the top of the stack is +magical or a ref, and in either case handle it specially: invoke get +magic, call an overload method, or replace a ref with a temporary numeric +value, as appropriate. If this function returns true, it indicates that +the correct return value is already on the stack. Intended to be used at +the beginning of the PP function for unary or binary ops. + +=cut +*/ + +PERL_STATIC_INLINE bool +Perl_rpp_try_AMAGIC_1(pTHX_ int method, int flags) +{ + return UNLIKELY((SvFLAGS(*PL_stack_sp) & (SVf_ROK|SVs_GMG))) + && Perl_try_amagic_un(aTHX_ method, flags); +} + +PERL_STATIC_INLINE bool +Perl_rpp_try_AMAGIC_2(pTHX_ int method, int flags) +{ + return UNLIKELY(((SvFLAGS(PL_stack_sp[-1])|SvFLAGS(PL_stack_sp[0])) + & (SVf_ROK|SVs_GMG))) + && Perl_try_amagic_bin(aTHX_ method, flags); +} + + +/* +=for apidoc rpp_stack_is_rc + +Returns a boolean value indicating whether the stack is currently +reference-counted. Note that if the stack is split (bottom half RC, top +half non-RC), this function returns false, even if the top half currently +contains zero items. + +=cut +*/ + +PERL_STATIC_INLINE bool +Perl_rpp_stack_is_rc(pTHX) +{ +#ifdef PERL_RC_STACK + return AvREAL(PL_curstack) && !PL_curstackinfo->si_stack_nonrc_base; +#else + return 0; +#endif + +} + + +/* +=for apidoc rpp_is_lone + +Indicates whether the stacked SV C (assumed to be not yet popped off +the stack) is only kept alive due to a single reference from the argument +stack and/or and the temps stack. + +This can used for example to decide whether the copying of return values in rvalue +context can be skipped, or whether it shouldn't be assigned to in lvalue +context. + +=cut +*/ + + +PERL_STATIC_INLINE bool +Perl_rpp_is_lone(pTHX_ SV *sv) +{ +#if defined(PERL_RC_STACK) && defined(DEBUG_LEAKING_SCALARS) + /* note that rpp_is_lone() can be used in wrapped pp functions, + * where technically the stack is no longer ref-counted; but because + * the args are non-RC copies of RC args further down the stack, we + * can't be in a *completely* non-ref stack. + */ + assert(AvREAL(PL_curstack)); +# endif + + return SvREFCNT(sv) <= cBOOL(SvTEMP(sv)) +#ifdef PERL_RC_STACK + + 1 +#endif + ; +} + + /* ----------------------------- regexp.h ----------------------------- */ /* PVLVs need to act as a superset of all scalar types - they are basically @@ -2749,6 +3153,9 @@ Perl_cx_pushblock(pTHX_ U8 type, U8 gimme, SV** sp, I32 saveix) cx->blk_gimme = gimme; cx->blk_oldsaveix = saveix; cx->blk_oldsp = (I32)(sp - PL_stack_base); + assert(cxstack_ix <= 0 + || CxTYPE(cx-1) == CXt_SUBST + || cx->blk_oldsp >= (cx-1)->blk_oldsp); cx->blk_oldcop = PL_curcop; cx->blk_oldmarksp = (I32)(PL_markstack_ptr - PL_markstack); cx->blk_oldscopesp = PL_scopestack_ix; @@ -2798,8 +3205,7 @@ Perl_cx_topblock(pTHX_ PERL_CONTEXT *cx) PL_markstack_ptr = PL_markstack + cx->blk_oldmarksp; PL_scopestack_ix = cx->blk_oldscopesp; PL_curpm = cx->blk_oldpm; - - PL_stack_sp = PL_stack_base + cx->blk_oldsp; + Perl_rpp_popfree_to(aTHX_ PL_stack_base + cx->blk_oldsp); } @@ -2858,12 +3264,15 @@ Perl_cx_popsub_args(pTHX_ PERL_CONTEXT *cx) CX_POP_SAVEARRAY(cx); av = MUTABLE_AV(PAD_SVl(0)); - if (UNLIKELY(AvREAL(av))) + if (!SvMAGICAL(av) && SvREFCNT(av) == 1 +#ifndef PERL_RC_STACK + && !AvREAL(av) +#endif + ) + clear_defarray_simple(av); + else /* abandon @_ if it got reified */ clear_defarray(av, 0); - else { - CLEAR_ARGARRAY(av); - } } @@ -3057,6 +3466,8 @@ Perl_cx_poploop(pTHX_ PERL_CONTEXT *cx) cx->blk_loop.itersave = NULL; SvREFCNT_dec(cursv); } + if (cx->cx_type & (CXp_FOR_GV|CXp_FOR_LVREF)) + SvREFCNT_dec(cx->blk_loop.itervar_u.svp); } @@ -3105,6 +3516,129 @@ Perl_cx_popgiven(pTHX_ PERL_CONTEXT *cx) SvREFCNT_dec(sv); } + +/* Make @_ empty in-place in simple cases: a cheap av_clear(). + * See Perl_clear_defarray() for non-simple cases */ + + +PERL_STATIC_INLINE void +Perl_clear_defarray_simple(pTHX_ AV *av) +{ + PERL_ARGS_ASSERT_CLEAR_DEFARRAY_SIMPLE; + + assert(SvTYPE(av) == SVt_PVAV); + assert(!SvREADONLY(av)); + assert(!SvMAGICAL(av)); + assert(SvREFCNT(av) == 1); + +#ifdef PERL_RC_STACK + assert(AvREAL(av)); + /* this code assumes that destructors called here can't free av + * itself, because pad[0] and/or CX pointers will keep it alive */ + SSize_t i = AvFILLp(av); + while (i >= 0) { + SV *sv = AvARRAY(av)[i]; + AvARRAY(av)[i--] = NULL; + SvREFCNT_dec(sv); + } +#else + assert(!AvREAL(av)); +#endif + AvFILLp(av) = -1; + Perl_av_remove_offset(aTHX_ av); +} + +/* Switch to a different argument stack. + * + * Note that it doesn't update PL_curstackinfo->si_stack_nonrc_base, + * so this should only be used as part of a general switching between + * stackinfos. + */ + +PERL_STATIC_INLINE void +Perl_switch_argstack(pTHX_ AV *to) +{ + PERL_ARGS_ASSERT_SWITCH_ARGSTACK; + + AvFILLp(PL_curstack) = PL_stack_sp - PL_stack_base; + PL_stack_base = AvARRAY(to); + PL_stack_max = PL_stack_base + AvMAX(to); + PL_stack_sp = PL_stack_base + AvFILLp(to); + PL_curstack = to; +} + + +/* Push, and switch to a new stackinfo, allocating one if none are spare, + * to get a fresh set of stacks. + * Update all the interpreter variables like PL_curstackinfo, + * PL_stack_sp, etc. + * current flag meanings: + * 1 make the new arg stack AvREAL + */ + + +PERL_STATIC_INLINE void +Perl_push_stackinfo(pTHX_ I32 type, UV flags) +{ + PERL_ARGS_ASSERT_PUSH_STACKINFO; + + PERL_SI *next = PL_curstackinfo->si_next; + DEBUG_l({ + int i = 0; PERL_SI *p = PL_curstackinfo; + while (p) { i++; p = p->si_prev; } + Perl_deb(aTHX_ "push STACKINFO %d in %s at %s:%d\n", + i, SAFE_FUNCTION__, __FILE__, __LINE__); + }) + + if (!next) { + next = new_stackinfo_flags(32, 2048/sizeof(PERL_CONTEXT) - 1, flags); + next->si_prev = PL_curstackinfo; + PL_curstackinfo->si_next = next; + } + next->si_type = type; + next->si_cxix = -1; + next->si_cxsubix = -1; + PUSHSTACK_INIT_HWM(next); +#ifdef PERL_RC_STACK + next->si_stack_nonrc_base = 0; +#endif + if (flags & 1) + AvREAL_on(next->si_stack); + else + AvREAL_off(next->si_stack); + AvFILLp(next->si_stack) = 0; + switch_argstack(next->si_stack); + PL_curstackinfo = next; + SET_MARK_OFFSET; +} + + +/* Pop, then switch to the previous stackinfo and set of stacks. + * Update all the interpreter variables like PL_curstackinfo, + * PL_stack_sp, etc. */ + +PERL_STATIC_INLINE void +Perl_pop_stackinfo(pTHX) +{ + PERL_ARGS_ASSERT_POP_STACKINFO; + + PERL_SI * const prev = PL_curstackinfo->si_prev; + DEBUG_l({ + int i = -1; PERL_SI *p = PL_curstackinfo; + while (p) { i++; p = p->si_prev; } + Perl_deb(aTHX_ "pop STACKINFO %d in %s at %s:%d\n", + i, SAFE_FUNCTION__, __FILE__, __LINE__);}) + if (!prev) { + Perl_croak_popstack(); + } + + switch_argstack(prev->si_stack); + /* don't free prev here, free them all at the END{} */ + PL_curstackinfo = prev; +} + + + /* =for apidoc newPADxVOP diff --git a/lib/B/Op_private.pm b/lib/B/Op_private.pm index 29eaf5d13564..d6a1bb72ab15 100644 --- a/lib/B/Op_private.pm +++ b/lib/B/Op_private.pm @@ -307,7 +307,7 @@ $bits{dorassign}{0} = $bf[0]; $bits{dump}{0} = $bf[0]; $bits{each}{0} = $bf[0]; @{$bits{emptyavhv}}{5,3,2,1,0} = ('OPpEMPTYAVHV_IS_HV', $bf[4], $bf[4], $bf[4], $bf[4]); -@{$bits{entereval}}{5,4,3,2,1,0} = ('OPpEVAL_RE_REPARSING', 'OPpEVAL_COPHH', 'OPpEVAL_BYTES', 'OPpEVAL_UNICODE', 'OPpEVAL_HAS_HH', $bf[0]); +@{$bits{entereval}}{6,5,4,3,2,1,0} = ('OPpEVAL_EVALSV', 'OPpEVAL_RE_REPARSING', 'OPpEVAL_COPHH', 'OPpEVAL_BYTES', 'OPpEVAL_UNICODE', 'OPpEVAL_HAS_HH', $bf[0]); $bits{entergiven}{0} = $bf[0]; $bits{enteriter}{3} = 'OPpITER_DEF'; @{$bits{entersub}}{5,4,0} = ($bf[8], $bf[8], 'OPpENTERSUB_INARGS'); @@ -637,6 +637,7 @@ our %defines = ( OPpENTERSUB_NOPAREN => 128, OPpEVAL_BYTES => 8, OPpEVAL_COPHH => 16, + OPpEVAL_EVALSV => 64, OPpEVAL_HAS_HH => 2, OPpEVAL_RE_REPARSING => 32, OPpEVAL_UNICODE => 4, @@ -751,6 +752,7 @@ our %labels = ( OPpENTERSUB_NOPAREN => 'NO()', OPpEVAL_BYTES => 'BYTES', OPpEVAL_COPHH => 'COPHH', + OPpEVAL_EVALSV => 'EVALSV', OPpEVAL_HAS_HH => 'HAS_HH', OPpEVAL_RE_REPARSING => 'REPARSE', OPpEVAL_UNICODE => 'UNI', @@ -900,6 +902,7 @@ $ops_using{OPpDONT_INIT_GV} = $ops_using{OPpALLOW_FAKE}; $ops_using{OPpENTERSUB_DB} = $ops_using{OPpENTERSUB_AMPER}; $ops_using{OPpENTERSUB_HASTARG} = $ops_using{OPpENTERSUB_AMPER}; $ops_using{OPpEVAL_COPHH} = $ops_using{OPpEVAL_BYTES}; +$ops_using{OPpEVAL_EVALSV} = $ops_using{OPpEVAL_BYTES}; $ops_using{OPpEVAL_HAS_HH} = $ops_using{OPpEVAL_BYTES}; $ops_using{OPpEVAL_RE_REPARSING} = $ops_using{OPpEVAL_BYTES}; $ops_using{OPpEVAL_UNICODE} = $ops_using{OPpEVAL_BYTES}; diff --git a/lib/Internals.pod b/lib/Internals.pod index 454ab0deb504..be679cec873a 100644 --- a/lib/Internals.pod +++ b/lib/Internals.pod @@ -7,6 +7,7 @@ Internals - Reserved special namespace for internals related functions $is_ro= Internals::SvREADONLY($x) $refcnt= Internals::SvREFCNT($x) hv_clear_placeholders(%hash); + if (Internals::stack_refcounted & 1) { .... } =head1 DESCRIPTION @@ -62,6 +63,13 @@ Clear any placeholders from a locked hash. Should not be used directly. You should use the wrapper functions provided by Hash::Util instead. As of 5.25 also available as C< Hash::Util::_clear_placeholders(%hash) > +=item stack_refcounted + +Returns an integer indicating whether the perl binary has been configured +and built with an argument stack which reference-counts any items pushed +onto it. The value should be treated as flag bits. Currently only bit 0 is +used, indicating that C was enabled during the build. + =back =head1 AUTHOR diff --git a/op.c b/op.c index dec2247f96a6..aeaa1d334ba9 100644 --- a/op.c +++ b/op.c @@ -4694,10 +4694,12 @@ Perl_newPROG(pTHX_ OP *o) if (PERLDB_INTER) { CV * const cv = get_cvs("DB::postponed", 0); if (cv) { - dSP; - PUSHMARK(SP); - XPUSHs(MUTABLE_SV(CopFILEGV(&PL_compiling))); - PUTBACK; + PUSHMARK(PL_stack_sp); + SV *comp = MUTABLE_SV(CopFILEGV(&PL_compiling)); +#ifdef PERL_RC_STACK + assert(rpp_stack_is_rc()); +#endif + rpp_xpush_1(comp); call_sv(MUTABLE_SV(cv), G_DISCARD); } } @@ -4946,7 +4948,7 @@ S_fold_constants(pTHX_ OP *const o) PL_op = curop; old_cxix = cxstack_ix; - create_eval_scope(NULL, G_FAKINGEVAL); + create_eval_scope(NULL, PL_stack_sp, G_FAKINGEVAL); /* Verify that we don't need to save it: */ assert(PL_curcop == &PL_compiling); @@ -4966,7 +4968,11 @@ S_fold_constants(pTHX_ OP *const o) switch (ret) { case 0: - sv = *(PL_stack_sp--); + sv = *PL_stack_sp; + if (rpp_stack_is_rc()) + SvREFCNT_dec(sv); + PL_stack_sp--; + if (o->op_targ && sv == PAD_SV(o->op_targ)) { /* grab pad temp? */ pad_swipe(o->op_targ, FALSE); } @@ -5063,7 +5069,7 @@ S_gen_constant_list(pTHX_ OP *o) PL_op = curop; old_cxix = cxstack_ix; - create_eval_scope(NULL, G_FAKINGEVAL); + create_eval_scope(NULL, PL_stack_sp, G_FAKINGEVAL); old_curcop = PL_curcop; StructCopy(old_curcop, ¬_compiling, COP); @@ -10318,10 +10324,11 @@ Perl_newMYSUB(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, OP *block) if (HvTOTALKEYS(hv) > 0 && hv_exists_ent(hv, tmpstr, 0)) { CV * const pcv = GvCV(db_postponed); if (pcv) { - dSP; - PUSHMARK(SP); - XPUSHs(tmpstr); - PUTBACK; + PUSHMARK(PL_stack_sp); +#ifdef PERL_RC_STACK + assert(rpp_stack_is_rc()); +#endif + rpp_xpush_1(tmpstr); call_sv(MUTABLE_SV(pcv), G_DISCARD); } } @@ -10919,10 +10926,11 @@ Perl_newATTRSUB_x(pTHX_ I32 floor, OP *o, OP *proto, OP *attrs, if (HvTOTALKEYS(hv) > 0 && hv_exists_ent(hv, tmpstr, 0)) { CV * const pcv = GvCV(db_postponed); if (pcv) { - dSP; - PUSHMARK(SP); - XPUSHs(tmpstr); - PUTBACK; + PUSHMARK(PL_stack_sp); +#ifdef PERL_RC_STACK + assert(rpp_stack_is_rc()); +#endif + rpp_xpush_1(tmpstr); call_sv(MUTABLE_SV(pcv), G_DISCARD); } } diff --git a/opcode.h b/opcode.h index a5bd0053f948..391e6ec9a52a 100644 --- a/opcode.h +++ b/opcode.h @@ -2367,6 +2367,7 @@ END_EXTERN_C #define OPpCONST_BARE 0x40 #define OPpCOREARGS_SCALARMOD 0x40 #define OPpENTERSUB_DB 0x40 +#define OPpEVAL_EVALSV 0x40 #define OPpEXISTS_SUB 0x40 #define OPpFLIP_LINENUM 0x40 #define OPpINDEX_BOOLNEG 0x40 @@ -2451,6 +2452,7 @@ EXTCONST char PL_op_private_labels[] = { 'E','A','R','L','Y','C','V','\0', 'E','L','E','M','\0', 'E','N','T','E','R','E','D','\0', + 'E','V','A','L','S','V','\0', 'E','X','I','S','T','S','\0', 'F','A','K','E','\0', 'F','I','N','A','L','L','Y','\0', @@ -2536,14 +2538,14 @@ EXTCONST char PL_op_private_labels[] = { EXTCONST I16 PL_op_private_bitfields[] = { 0, 8, -1, 0, 8, -1, - 0, 668, -1, + 0, 675, -1, 0, 8, -1, 0, 8, -1, - 0, 675, -1, - 0, 664, -1, - 1, -1, 0, 625, 1, 39, 2, 312, -1, + 0, 682, -1, + 0, 671, -1, + 1, -1, 0, 632, 1, 39, 2, 319, -1, 4, -1, 1, 185, 2, 192, 3, 199, -1, - 4, -1, 0, 625, 1, 39, 2, 312, 3, 131, -1, + 4, -1, 0, 632, 1, 39, 2, 319, 3, 131, -1, }; @@ -2939,17 +2941,17 @@ EXTCONST I16 PL_op_private_bitdef_ix[] = { [OP_LOCK] = 0, [OP_ONCE] = 0, [OP_CUSTOM] = -1, - [OP_COREARGS] = 230, - [OP_AVHVSWITCH] = 234, + [OP_COREARGS] = 231, + [OP_AVHVSWITCH] = 235, [OP_RUNCV] = 3, [OP_FC] = 0, [OP_PADCV] = -1, [OP_INTROCV] = -1, [OP_CLONECV] = -1, - [OP_PADRANGE] = 236, - [OP_REFASSIGN] = 238, - [OP_LVREF] = 244, - [OP_LVREFSLICE] = 250, + [OP_PADRANGE] = 237, + [OP_REFASSIGN] = 239, + [OP_LVREF] = 245, + [OP_LVREFSLICE] = 251, [OP_LVAVREF] = 16, [OP_ANONCONST] = 0, [OP_ISA] = 12, @@ -2959,7 +2961,7 @@ EXTCONST I16 PL_op_private_bitdef_ix[] = { [OP_LEAVETRYCATCH] = -1, [OP_POPTRY] = -1, [OP_CATCH] = 0, - [OP_PUSHDEFER] = 251, + [OP_PUSHDEFER] = 252, [OP_IS_BOOL] = 0, [OP_IS_WEAK] = 0, [OP_WEAKEN] = 0, @@ -2970,9 +2972,9 @@ EXTCONST I16 PL_op_private_bitdef_ix[] = { [OP_CEIL] = 78, [OP_FLOOR] = 78, [OP_IS_TAINTED] = 0, - [OP_HELEMEXISTSOR] = 253, - [OP_METHSTART] = 255, - [OP_INITFIELD] = 257, + [OP_HELEMEXISTSOR] = 254, + [OP_METHSTART] = 256, + [OP_INITFIELD] = 258, }; @@ -2992,84 +2994,84 @@ EXTCONST I16 PL_op_private_bitdef_ix[] = { EXTCONST U16 PL_op_private_bitdefs[] = { 0x0003, /* scalar, prototype, refgen, srefgen, readline, regcmaybe, regcreset, regcomp, substcont, chop, schop, defined, study, preinc, i_preinc, predec, i_predec, postinc, i_postinc, postdec, i_postdec, negate, i_negate, not, ucfirst, lcfirst, uc, lc, quotemeta, aeach, avalues, each, pop, shift, grepstart, mapstart, mapwhile, range, and, or, dor, andassign, orassign, dorassign, argcheck, method, method_named, method_super, method_redir, method_redir_super, entergiven, leavegiven, enterwhen, leavewhen, untie, tied, dbmclose, getsockname, getpeername, lstat, stat, readlink, readdir, telldir, rewinddir, closedir, localtime, alarm, require, dofile, entertry, ghbyname, gnbyname, gpbyname, shostent, snetent, sprotoent, sservent, gpwnam, gpwuid, ggrnam, ggrgid, lock, once, fc, anonconst, cmpchain_and, cmpchain_dup, entertrycatch, catch, is_bool, is_weak, weaken, unweaken, is_tainted */ - 0x39dc, 0x4ad9, /* pushmark */ + 0x3abc, 0x4bb9, /* pushmark */ 0x00bd, /* wantarray, runcv */ - 0x0558, 0x1b70, 0x4b8c, 0x4728, 0x3f05, /* const */ - 0x39dc, 0x4059, /* gvsv */ + 0x0558, 0x1b70, 0x4c6c, 0x4808, 0x3fe5, /* const */ + 0x3abc, 0x4139, /* gvsv */ 0x19d5, /* gv */ 0x0067, /* gelem, lt, i_lt, gt, i_gt, le, i_le, ge, i_ge, eq, i_eq, ne, i_ne, ncmp, i_ncmp, slt, sgt, sle, sge, seq, sne, scmp, smartmatch, lslice, xor, isa */ - 0x39dc, 0x4ad8, 0x03d7, /* padsv */ - 0x39dc, 0x4ad8, 0x0003, /* padsv_store, lvavref */ - 0x39dc, 0x4ad8, 0x06d4, 0x3acc, 0x48a9, /* padav */ - 0x39dc, 0x4ad8, 0x06d4, 0x0770, 0x3acc, 0x48a8, 0x3541, /* padhv */ - 0x39dc, 0x1d58, 0x03d6, 0x3acc, 0x3e28, 0x4b84, 0x0003, /* rv2gv */ - 0x39dc, 0x4058, 0x03d6, 0x4b84, 0x0003, /* rv2sv */ - 0x3acc, 0x0003, /* av2arylen, akeys, values, keys */ - 0x3d9c, 0x1198, 0x0ef4, 0x014c, 0x4e88, 0x4b84, 0x0003, /* rv2cv */ + 0x3abc, 0x4bb8, 0x03d7, /* padsv */ + 0x3abc, 0x4bb8, 0x0003, /* padsv_store, lvavref */ + 0x3abc, 0x4bb8, 0x06d4, 0x3bac, 0x4989, /* padav */ + 0x3abc, 0x4bb8, 0x06d4, 0x0770, 0x3bac, 0x4988, 0x3621, /* padhv */ + 0x3abc, 0x1e38, 0x03d6, 0x3bac, 0x3f08, 0x4c64, 0x0003, /* rv2gv */ + 0x3abc, 0x4138, 0x03d6, 0x4c64, 0x0003, /* rv2sv */ + 0x3bac, 0x0003, /* av2arylen, akeys, values, keys */ + 0x3e7c, 0x1198, 0x0ef4, 0x014c, 0x4f68, 0x4c64, 0x0003, /* rv2cv */ 0x06d4, 0x0770, 0x0003, /* ref, blessed */ 0x018f, /* bless, glob, sprintf, formline, unpack, pack, join, anonlist, anonhash, splice, warn, die, reset, exit, close, pipe_op, fileno, umask, binmode, tie, dbmopen, sselect, select, getc, read, enterwrite, sysopen, sysseek, sysread, syswrite, eof, tell, seek, truncate, fcntl, ioctl, send, recv, socket, sockpair, bind, connect, listen, accept, shutdown, gsockopt, ssockopt, open_dir, seekdir, gmtime, shmget, shmctl, shmread, shmwrite, msgget, msgctl, msgsnd, msgrcv, semop, semget, semctl, ghbyaddr, gnbyaddr, gpbynumber, gsbyname, gsbyport, syscall */ - 0x423c, 0x4158, 0x2cf4, 0x2c30, 0x0003, /* backtick */ + 0x431c, 0x4238, 0x2dd4, 0x2d10, 0x0003, /* backtick */ 0x06d5, /* subst */ - 0x129c, 0x23d8, 0x0ad4, 0x49ec, 0x2768, 0x5164, 0x08e1, /* trans, transr */ + 0x129c, 0x24b8, 0x0ad4, 0x4acc, 0x2848, 0x5244, 0x08e1, /* trans, transr */ 0x10dc, 0x05f8, 0x0067, /* sassign */ - 0x0d98, 0x0c94, 0x0b90, 0x3acc, 0x06c8, 0x0067, /* aassign */ - 0x4f30, 0x0003, /* chomp, schomp, scomplement, sin, cos, exp, log, sqrt, int, hex, oct, abs, ord, chr, chroot, rmdir, refaddr, reftype, ceil, floor */ - 0x39dc, 0x4ad8, 0x3454, 0x4f30, 0x0003, /* undef */ - 0x06d4, 0x3acc, 0x0003, /* pos */ - 0x4f30, 0x0067, /* pow, multiply, i_multiply, divide, i_divide, modulo, i_modulo, add, i_add, subtract, i_subtract */ + 0x0d98, 0x0c94, 0x0b90, 0x3bac, 0x06c8, 0x0067, /* aassign */ + 0x5010, 0x0003, /* chomp, schomp, scomplement, sin, cos, exp, log, sqrt, int, hex, oct, abs, ord, chr, chroot, rmdir, refaddr, reftype, ceil, floor */ + 0x3abc, 0x4bb8, 0x3534, 0x5010, 0x0003, /* undef */ + 0x06d4, 0x3bac, 0x0003, /* pos */ + 0x5010, 0x0067, /* pow, multiply, i_multiply, divide, i_divide, modulo, i_modulo, add, i_add, subtract, i_subtract */ 0x1658, 0x0067, /* repeat */ - 0x3cb8, 0x4f30, 0x0067, /* concat */ - 0x39dc, 0x0338, 0x1d54, 0x4f30, 0x4c6c, 0x0003, /* multiconcat */ - 0x4f30, 0x018f, /* stringify, atan2, rand, srand, crypt, push, unshift, flock, chdir, chown, unlink, chmod, utime, rename, link, symlink, mkdir, waitpid, system, exec, kill, getpgrp, setpgrp, getpriority, setpriority, sleep */ - 0x4f30, 0x5089, /* left_shift, right_shift, nbit_and, nbit_xor, nbit_or, ncomplement */ - 0x5089, /* bit_and, bit_xor, bit_or, sbit_and, sbit_xor, sbit_or, complement */ - 0x06d4, 0x4f30, 0x0003, /* length */ - 0x4490, 0x3acc, 0x012b, /* substr */ - 0x3acc, 0x0067, /* vec */ - 0x3c38, 0x06d4, 0x4f30, 0x018f, /* index, rindex */ - 0x39dc, 0x4058, 0x06d4, 0x3acc, 0x48a8, 0x4b84, 0x0003, /* rv2av */ + 0x3d98, 0x5010, 0x0067, /* concat */ + 0x3abc, 0x0338, 0x1e34, 0x5010, 0x4d4c, 0x0003, /* multiconcat */ + 0x5010, 0x018f, /* stringify, atan2, rand, srand, crypt, push, unshift, flock, chdir, chown, unlink, chmod, utime, rename, link, symlink, mkdir, waitpid, system, exec, kill, getpgrp, setpgrp, getpriority, setpriority, sleep */ + 0x5010, 0x5169, /* left_shift, right_shift, nbit_and, nbit_xor, nbit_or, ncomplement */ + 0x5169, /* bit_and, bit_xor, bit_or, sbit_and, sbit_xor, sbit_or, complement */ + 0x06d4, 0x5010, 0x0003, /* length */ + 0x4570, 0x3bac, 0x012b, /* substr */ + 0x3bac, 0x0067, /* vec */ + 0x3d18, 0x06d4, 0x5010, 0x018f, /* index, rindex */ + 0x3abc, 0x4138, 0x06d4, 0x3bac, 0x4988, 0x4c64, 0x0003, /* rv2av */ 0x025f, /* aelemfast, aelemfast_lex, aelemfastlex_store */ - 0x39dc, 0x38d8, 0x03d6, 0x3acc, 0x0067, /* aelem, helem */ - 0x39dc, 0x3acc, 0x48a9, /* aslice, hslice */ - 0x3acd, /* kvaslice, kvhslice */ - 0x39dc, 0x47f8, 0x35f4, 0x0003, /* delete */ - 0x4db8, 0x0003, /* exists */ - 0x39dc, 0x4058, 0x06d4, 0x0770, 0x3acc, 0x48a8, 0x4b84, 0x3541, /* rv2hv */ - 0x39dc, 0x38d8, 0x1314, 0x1c70, 0x3acc, 0x4b84, 0x0003, /* multideref */ - 0x39dc, 0x4058, 0x0410, 0x36ec, 0x2a69, /* split */ - 0x39dc, 0x2499, /* list */ - 0x39dc, 0x4ad8, 0x0214, 0x4f30, 0x018f, /* emptyavhv */ - 0x15b0, 0x322c, 0x4588, 0x3324, 0x3fc1, /* sort */ - 0x322c, 0x0003, /* reverse */ + 0x3abc, 0x39b8, 0x03d6, 0x3bac, 0x0067, /* aelem, helem */ + 0x3abc, 0x3bac, 0x4989, /* aslice, hslice */ + 0x3bad, /* kvaslice, kvhslice */ + 0x3abc, 0x48d8, 0x36d4, 0x0003, /* delete */ + 0x4e98, 0x0003, /* exists */ + 0x3abc, 0x4138, 0x06d4, 0x0770, 0x3bac, 0x4988, 0x4c64, 0x3621, /* rv2hv */ + 0x3abc, 0x39b8, 0x1314, 0x1d50, 0x3bac, 0x4c64, 0x0003, /* multideref */ + 0x3abc, 0x4138, 0x0410, 0x37cc, 0x2b49, /* split */ + 0x3abc, 0x2579, /* list */ + 0x3abc, 0x4bb8, 0x0214, 0x5010, 0x018f, /* emptyavhv */ + 0x15b0, 0x330c, 0x4668, 0x3404, 0x40a1, /* sort */ + 0x330c, 0x0003, /* reverse */ 0x06d4, 0x0003, /* grepwhile */ - 0x3778, 0x0003, /* flip, flop */ - 0x39dc, 0x0003, /* cond_expr */ - 0x39dc, 0x1198, 0x03d6, 0x014c, 0x4e88, 0x4b84, 0x2b41, /* entersub */ - 0x42f8, 0x0003, /* leavesub, leavesublv, leavewrite, leaveeval */ + 0x3858, 0x0003, /* flip, flop */ + 0x3abc, 0x0003, /* cond_expr */ + 0x3abc, 0x1198, 0x03d6, 0x014c, 0x4f68, 0x4c64, 0x2c21, /* entersub */ + 0x43d8, 0x0003, /* leavesub, leavesublv, leavewrite, leaveeval */ 0x02aa, 0x0003, /* argelem */ - 0x295c, 0x2838, 0x0003, /* argdefelem */ + 0x2a3c, 0x2918, 0x0003, /* argdefelem */ 0x00bc, 0x018f, /* caller */ - 0x2675, /* nextstate, dbstate */ - 0x387c, 0x42f9, /* leave */ - 0x39dc, 0x4058, 0x120c, 0x4605, /* enteriter */ - 0x4605, /* iter */ - 0x387c, 0x0067, /* leaveloop */ - 0x529c, 0x0003, /* last, next, redo, dump, goto */ - 0x423c, 0x4158, 0x2cf4, 0x2c30, 0x018f, /* open */ - 0x2010, 0x226c, 0x2128, 0x1ee4, 0x0003, /* ftrread, ftrwrite, ftrexec, fteread, ftewrite, fteexec */ - 0x2010, 0x226c, 0x2128, 0x0003, /* ftis, ftsize, ftmtime, ftatime, ftctime, ftrowned, fteowned, ftzero, ftsock, ftchr, ftblk, ftfile, ftdir, ftpipe, ftsuid, ftsgid, ftsvtx, ftlink, fttty, fttext, ftbinary */ - 0x4f31, /* wait, getppid, time */ - 0x4394, 0x0fb0, 0x082c, 0x5008, 0x2584, 0x0003, /* entereval */ - 0x3b9c, 0x0018, 0x14c4, 0x13e1, /* coreargs */ - 0x3acc, 0x00c7, /* avhvswitch */ - 0x39dc, 0x01fb, /* padrange */ - 0x39dc, 0x4ad8, 0x04f6, 0x33ac, 0x1ac8, 0x0067, /* refassign */ - 0x39dc, 0x4ad8, 0x04f6, 0x33ac, 0x1ac8, 0x0003, /* lvref */ - 0x39dd, /* lvrefslice */ - 0x1dfc, 0x0003, /* pushdefer */ + 0x2755, /* nextstate, dbstate */ + 0x395c, 0x43d9, /* leave */ + 0x3abc, 0x4138, 0x120c, 0x46e5, /* enteriter */ + 0x46e5, /* iter */ + 0x395c, 0x0067, /* leaveloop */ + 0x537c, 0x0003, /* last, next, redo, dump, goto */ + 0x431c, 0x4238, 0x2dd4, 0x2d10, 0x018f, /* open */ + 0x20f0, 0x234c, 0x2208, 0x1fc4, 0x0003, /* ftrread, ftrwrite, ftrexec, fteread, ftewrite, fteexec */ + 0x20f0, 0x234c, 0x2208, 0x0003, /* ftis, ftsize, ftmtime, ftatime, ftctime, ftrowned, fteowned, ftzero, ftsock, ftchr, ftblk, ftfile, ftdir, ftpipe, ftsuid, ftsgid, ftsvtx, ftlink, fttty, fttext, ftbinary */ + 0x5011, /* wait, getppid, time */ + 0x1c78, 0x4474, 0x0fb0, 0x082c, 0x50e8, 0x2664, 0x0003, /* entereval */ + 0x3c7c, 0x0018, 0x14c4, 0x13e1, /* coreargs */ + 0x3bac, 0x00c7, /* avhvswitch */ + 0x3abc, 0x01fb, /* padrange */ + 0x3abc, 0x4bb8, 0x04f6, 0x348c, 0x1ac8, 0x0067, /* refassign */ + 0x3abc, 0x4bb8, 0x04f6, 0x348c, 0x1ac8, 0x0003, /* lvref */ + 0x3abd, /* lvrefslice */ + 0x1edc, 0x0003, /* pushdefer */ 0x131c, 0x0003, /* helemexistsor */ - 0x2d9c, 0x0003, /* methstart */ - 0x3088, 0x2ee4, 0x0003, /* initfield */ + 0x2e7c, 0x0003, /* methstart */ + 0x3168, 0x2fc4, 0x0003, /* initfield */ }; @@ -3426,7 +3428,7 @@ EXTCONST U8 PL_op_private_valid[] = { [OP_REQUIRE] = (OPpARG1_MASK), [OP_DOFILE] = (OPpARG1_MASK), [OP_HINTSEVAL] = (0), - [OP_ENTEREVAL] = (OPpARG1_MASK|OPpEVAL_HAS_HH|OPpEVAL_UNICODE|OPpEVAL_BYTES|OPpEVAL_COPHH|OPpEVAL_RE_REPARSING), + [OP_ENTEREVAL] = (OPpARG1_MASK|OPpEVAL_HAS_HH|OPpEVAL_UNICODE|OPpEVAL_BYTES|OPpEVAL_COPHH|OPpEVAL_RE_REPARSING|OPpEVAL_EVALSV), [OP_LEAVEEVAL] = (OPpARG1_MASK|OPpREFCOUNTED), [OP_ENTERTRY] = (OPpARG1_MASK), [OP_LEAVETRY] = (0), diff --git a/pad.c b/pad.c index 1e6449ad694a..9085f7536cb4 100644 --- a/pad.c +++ b/pad.c @@ -229,7 +229,9 @@ Perl_pad_new(pTHX_ int flags) if (flags & padnew_CLONE) { AV * const a0 = newAV(); /* will be @_ */ AvARRAY(pad)[0] = MUTABLE_SV(a0); +#ifndef PERL_RC_STACK AvREIFY_only(a0); +#endif PadnamelistREFCNT(padname = PL_comppad_name)++; } @@ -1714,7 +1716,9 @@ Perl_pad_tidy(pTHX_ padtidy_type type) else if (type == padtidy_SUB) { AV * const av = newAV(); /* Will be @_ */ av_store(PL_comppad, 0, MUTABLE_SV(av)); +#ifndef PERL_RC_STACK AvREIFY_only(av); +#endif } if (type == padtidy_SUB || type == padtidy_FORMAT) { @@ -2469,7 +2473,9 @@ Perl_pad_push(pTHX_ PADLIST *padlist, int depth) } av = newAV(); AvARRAY(newpad)[0] = MUTABLE_SV(av); +#ifndef PERL_RC_STACK AvREIFY_only(av); +#endif padlist_store(padlist, depth, newpad); } @@ -2582,7 +2588,9 @@ Perl_padlist_dup(pTHX_ PADLIST *srcpad, CLONE_PARAMS *param) if (oldpad[0]) { args = newAV(); /* Will be @_ */ +#ifndef PERL_RC_STACK AvREIFY_only(args); +#endif pad1a[0] = (SV *)args; } } diff --git a/perl.c b/perl.c index c520d2c53def..f7a511896c4c 100644 --- a/perl.c +++ b/perl.c @@ -67,12 +67,6 @@ static I32 read_e_script(pTHX_ int idx, SV *buf_sv, int maxlen); # define validate_suid(rsfp) S_validate_suid(aTHX_ rsfp) #endif -#define CALL_BODY_SUB(myop) \ - if (PL_op == (myop)) \ - PL_op = PL_ppaddr[OP_ENTERSUB](aTHX); \ - if (PL_op) \ - CALLRUNOPS(aTHX); - #define CALL_LIST_BODY(cv) \ PUSHMARK(PL_stack_sp); \ call_sv(MUTABLE_SV((cv)), G_EVAL|G_DISCARD|G_VOID); @@ -2036,6 +2030,9 @@ S_Internals_V(pTHX_ CV *cv) # ifdef PERL_RELOCATABLE_INCPUSH " PERL_RELOCATABLE_INCPUSH" # endif +# ifdef PERL_RC_STACK + " PERL_RC_STACK" +# endif # ifdef PERL_USE_DEVEL " PERL_USE_DEVEL" # endif @@ -2974,16 +2971,23 @@ Perl_call_argv(pTHX_ const char *sub_name, I32 flags, char **argv) /* See G_* flags in cop.h */ /* null terminated arg list */ { - dSP; - PERL_ARGS_ASSERT_CALL_ARGV; - PUSHMARK(SP); + bool is_rc = +#ifdef PERL_RC_STACK + rpp_stack_is_rc(); +#else + 0; +#endif + PUSHMARK(PL_stack_sp); while (*argv) { - mXPUSHs(newSVpv(*argv,0)); + SV *newsv = newSVpv(*argv,0); + rpp_extend(1); + *++PL_stack_sp = newsv; + if (!is_rc) + sv_2mortal(newsv); argv++; } - PUTBACK; return call_pv(sub_name, flags); } @@ -3087,14 +3091,18 @@ Perl_call_sv(pTHX_ SV *sv, volatile I32 flags) if (!(flags & G_NOARGS)) myop.op_flags |= OPf_STACKED; myop.op_flags |= OP_GIMME_REVERSE(flags); + myop.op_ppaddr = PL_ppaddr[OP_ENTERSUB]; + myop.op_type = OP_ENTERSUB; SAVEOP(); PL_op = (OP*)&myop; if (!(flags & G_METHOD_NAMED)) { - dSP; - EXTEND(SP, 1); - PUSHs(sv); - PUTBACK; + rpp_extend(1); + *++PL_stack_sp = sv; +#ifdef PERL_RC_STACK + if (rpp_stack_is_rc()) + SvREFCNT_inc_simple_void_NN(sv); +#endif } oldmark = TOPMARK; @@ -3119,13 +3127,11 @@ Perl_call_sv(pTHX_ SV *sv, volatile I32 flags) method_op.op_ppaddr = PL_ppaddr[OP_METHOD]; method_op.op_type = OP_METHOD; } - myop.op_ppaddr = PL_ppaddr[OP_ENTERSUB]; - myop.op_type = OP_ENTERSUB; } if (!(flags & G_EVAL)) { CATCH_SET(TRUE); - CALL_BODY_SUB((OP*)&myop); + CALLRUNOPS(aTHX); retval = PL_stack_sp - (PL_stack_base + oldmark); CATCH_SET(oldcatch); } @@ -3134,7 +3140,7 @@ Perl_call_sv(pTHX_ SV *sv, volatile I32 flags) myop.op_other = (OP*)&myop; (void)POPMARK; old_cxix = cxstack_ix; - create_eval_scope(NULL, flags|G_FAKINGEVAL); + create_eval_scope( NULL, PL_stack_base + oldmark, flags|G_FAKINGEVAL); INCMARK; JMPENV_PUSH(ret); @@ -3142,7 +3148,7 @@ Perl_call_sv(pTHX_ SV *sv, volatile I32 flags) switch (ret) { case 0: redo_body: - CALL_BODY_SUB((OP*)&myop); + CALLRUNOPS(aTHX); retval = PL_stack_sp - (PL_stack_base + oldmark); if (!(flags & G_KEEPERR)) { CLEAR_ERRSV(); @@ -3165,6 +3171,11 @@ Perl_call_sv(pTHX_ SV *sv, volatile I32 flags) PL_restartop = 0; goto redo_body; } + /* Should be nothing left in stack frame apart from a possible + * scalar context undef. Assert it's safe to reset the stack */ + assert( PL_stack_sp == PL_stack_base + oldmark + || (PL_stack_sp == PL_stack_base + oldmark + 1 + && *PL_stack_sp == &PL_sv_undef)); PL_stack_sp = PL_stack_base + oldmark; if ((flags & G_WANT) == G_LIST) retval = 0; @@ -3186,7 +3197,12 @@ Perl_call_sv(pTHX_ SV *sv, volatile I32 flags) } if (flags & G_DISCARD) { - PL_stack_sp = PL_stack_base + oldmark; +#ifdef PERL_RC_STACK + if (rpp_stack_is_rc()) + rpp_popfree_to(PL_stack_base + oldmark); + else +#endif + PL_stack_sp = PL_stack_base + oldmark; retval = 0; FREETMPS; LEAVE; @@ -3232,13 +3248,16 @@ Perl_eval_sv(pTHX_ SV *sv, I32 flags) SAVEOP(); PL_op = (OP*)&myop; Zero(&myop, 1, UNOP); - { - dSP; - oldmark = SP - PL_stack_base; - EXTEND(SP, 1); - PUSHs(sv); - PUTBACK; - } + myop.op_ppaddr = PL_ppaddr[OP_ENTEREVAL]; + myop.op_type = OP_ENTEREVAL; + + oldmark = PL_stack_sp - PL_stack_base; + rpp_extend(1); + *++PL_stack_sp = sv; +#ifdef PERL_RC_STACK + if (rpp_stack_is_rc()) + SvREFCNT_inc_simple_void_NN(sv); +#endif if (!(flags & G_NOARGS)) myop.op_flags = OPf_STACKED; @@ -3247,8 +3266,9 @@ Perl_eval_sv(pTHX_ SV *sv, I32 flags) if (flags & G_KEEPERR) myop.op_flags |= OPf_SPECIAL; + myop.op_private = (OPpEVAL_EVALSV); /* tell pp_entereval we're the caller */ if (flags & G_RE_REPARSING) - myop.op_private = (OPpEVAL_COPHH | OPpEVAL_RE_REPARSING); + myop.op_private |= (OPpEVAL_COPHH | OPpEVAL_RE_REPARSING); /* fail now; otherwise we could fail after the JMPENV_PUSH but * before a cx_pusheval(), which corrupts the stack after a croak */ @@ -3257,13 +3277,15 @@ Perl_eval_sv(pTHX_ SV *sv, I32 flags) JMPENV_PUSH(ret); switch (ret) { case 0: - redo_body: - if (PL_op == (OP*)(&myop)) { - PL_op = PL_ppaddr[OP_ENTEREVAL](aTHX); - if (!PL_op) - goto fail; /* failed in compilation */ - } CALLRUNOPS(aTHX); + if (!*PL_stack_sp) { + /* In the presence of the OPpEVAL_EVALSV flag, + * pp_entereval() pushes a NULL pointer onto the stack to + * indicate compilation failure */ + PL_stack_sp--; + goto fail; + } + redone_body: retval = PL_stack_sp - (PL_stack_base + oldmark); if (!(flags & G_KEEPERR)) { CLEAR_ERRSV(); @@ -3284,14 +3306,19 @@ Perl_eval_sv(pTHX_ SV *sv, I32 flags) PL_restartjmpenv = NULL; PL_op = PL_restartop; PL_restartop = 0; - goto redo_body; + CALLRUNOPS(aTHX); + goto redone_body; } fail: if (flags & G_RETHROW) { JMPENV_POP; croak_sv(ERRSV); } - + /* Should be nothing left in stack frame apart from a possible + * scalar context undef. Assert it's safe to reset the stack */ + assert( PL_stack_sp == PL_stack_base + oldmark + || (PL_stack_sp == PL_stack_base + oldmark + 1 + && *PL_stack_sp == &PL_sv_undef)); PL_stack_sp = PL_stack_base + oldmark; if ((flags & G_WANT) == G_LIST) retval = 0; @@ -3304,7 +3331,12 @@ Perl_eval_sv(pTHX_ SV *sv, I32 flags) JMPENV_POP; if (flags & G_DISCARD) { - PL_stack_sp = PL_stack_base + oldmark; +#ifdef PERL_RC_STACK + if (rpp_stack_is_rc()) + rpp_popfree_to(PL_stack_base + oldmark); + else +#endif + PL_stack_sp = PL_stack_base + oldmark; retval = 0; FREETMPS; LEAVE; @@ -3337,11 +3369,16 @@ Perl_eval_pv(pTHX_ const char *p, I32 croak_on_error) SvREFCNT_dec(sv); } - { - dSP; - sv = POPs; - PUTBACK; + sv = *PL_stack_sp; + +#ifdef PERL_RC_STACK + if (rpp_stack_is_rc()) { + SvREFCNT_inc_NN(sv_2mortal(sv)); + rpp_popfree_1(); } + else +#endif + PL_stack_sp--; return sv; } @@ -4433,8 +4470,14 @@ Perl_init_stacks(pTHX) SSize_t size; /* start with 128-item stack and 8K cxstack */ - PL_curstackinfo = new_stackinfo(REASONABLE(128), - REASONABLE(8192/sizeof(PERL_CONTEXT) - 1)); + PL_curstackinfo = new_stackinfo_flags(REASONABLE(128), + REASONABLE(8192/sizeof(PERL_CONTEXT) - 1), +#ifdef PERL_RC_STACK + 1 +#else + 0 +#endif + ); PL_curstackinfo->si_type = PERLSI_MAIN; #if defined DEBUGGING && !defined DEBUGGING_RE_ONLY PL_curstackinfo->si_stack_hwm = 0; @@ -5417,8 +5460,8 @@ S_my_exit_jump(pTHX) POPSTACK_TO(PL_mainstack); if (cxstack_ix >= 0) { dounwind(-1); - cx_popblock(cxstack); } + rpp_obliterate_stack_to(0); LEAVE_SCOPE(0); JMPENV_JUMP(2); diff --git a/perl.h b/perl.h index 1247788682ce..12094bf87137 100644 --- a/perl.h +++ b/perl.h @@ -22,6 +22,11 @@ #define USE_STDIO #endif /* PERL_FOR_X2P */ +/* Treat the SVs on the argument stack as having been reference counted. + * (Experimental). + */ +/* #define PERL_RC_STACK */ + #ifdef PERL_MICRO # include "uconfig.h" #else @@ -229,7 +234,11 @@ Now a no-op. # define MEMBER_TO_FPTR(name) name #endif /* !PERL_CORE */ -#define CALLRUNOPS PL_runops +#ifdef PERL_RC_STACK +# define CALLRUNOPS Perl_runops_wrap +#else +# define CALLRUNOPS PL_runops +#endif #define CALLREGCOMP(sv, flags) Perl_pregcomp(aTHX_ (sv),(flags)) diff --git a/pod/perlguts.pod b/pod/perlguts.pod index f7dcadfef955..86f822659724 100644 --- a/pod/perlguts.pod +++ b/pod/perlguts.pod @@ -4348,6 +4348,478 @@ C and C. Note that it doesn't do a C. +=head1 Reference-counted argument stack + +=head2 Introduction + +As of perl 5.38 (XXX check version) there is a build option, +C, not enabled by default, which requires that items pushed +onto, or popped off the argument stack have their reference counts +adjusted. It is intended that eventually this will be the default way and +finally the only way to configure perl. + +The macros which manipulate the stack such as PUSHs() and POPs() don't +adjust the reference count of the SV. Most of the time this is fine, since +something else is keeping the SV alive while on the argument stack, such +a pointer from the TEMPs stack, or from the pad (e.g. a lexical variable +or a C). Occasionally this can go horribly wrong. For example, +this code: + + my @a = (1,2,3); + sub f { @a = (); print "(@_)\n" }; + f(@a, 4); + +may print undefined or random freed values, since some of the elements of +@_, which have been aliased to the elements of @a, have been freed. +C is intended to fix this by making each SV pointer on the +argument stack increment the reference count (RC) of the SV by one. + +In this new environment, unmodified existing PP and XS functions, which +have been written assuming a non reference-counted (non-RC for short) +stack, are called via special wrapper functions which adjust the stack +before and after. At the moment there is no API to write an RC XS +function, so all XS code will continue to be called via a wrapper (which +makes them slightly slower), but means that in general, CPAN distributions +containing XS code code continue to work without modification. + +However, PP functions, either in perl core, or those in XS functions used +to implement custom ops or to override the pp functions for built-in ops, +need dealing with specially. For the latter, they can just be wrapped; +this involves the least work, but has a performance impact. In the longer +term, and for core PP functions, they need unwrapping and rewriting using +a new API. With this, the old macros such as PUSHs() have been replaced +with a new set of (mostly inline) functions with a common prefix, such as +rpp_push_1(). "RPP" stands for "reference-counted push and pop functions". +The new functions modify the reference count on C builds, +while leaving them unadjusted otherwise. Thus in core they generally work +in both cases, while in XS code they are portable to older perl versions +via C (XXX assuming that they get been added to C). + +The rest of this section is mainly concerned with how to convert existing +PP functions and how to write new PP functions to use the new C API. + +A reference-counted perl can be built using the PERL_RC_STACK define. +For development and debugging purposes, it is best to enable leaking +scalar debugging too, as that displays extra information about scalars +that have leaked or been prematurely freed, and it also enables extra +assertions in the macros and functions which manipulate the stack: + + Configure -DDEBUGGING \ + -Accflags='-DPERL_RC_STACK -DDEBUG_LEAKING_SCALARS' + +=head2 Reference counted stack states + +In the new regime, the current argument stack can be in one of three +states, which can be determined by the shown expression. + +=over + +=item * not reference-counted + + !AvREAL(PL_curstack) + +In this case, perl will assume when emptying the stack (such as during a +croak()) that the items on it don't need freeing. This is the traditional +perl behaviour. On C builds, such stacks will be rarely +encountered. + +=item * fully reference-counted + + AvREAL(PL_curstack) && !PL_curstackinfo->si_stack_nonrc_base + +All the items on the stack are reference counted, and will be freed by +functions like rpp_popfree_1() or if perl croak()s. This is the normal +state of the stack in C builds. + +=item * partially reference-counted (split) + + AvREAL(PL_curstack) && PL_curstackinfo->si_stack_nonrc_base > 0 + +In this case, items on the stack from the index C +upwards are non-RC; those below are RC. This state occurs when a PP or XS +function has been wrapped. In this case, the wrapper function pushes a +non-RC copy of the arg pointers above the cut then calls the real +function. When that returns, any returned args have their ref counts +bumped up. See below for more details. + +=back + +Note that perl uses a stack-of-stacks, and the AvREAL() and +C states are per stack. When perl starts up, the main +stack is RC, but by default, new stacks pushed in XS code via PUSHSTACKi() +are non-RC, so it is quite possible to get a mixture. The perl core itself +uses the new push_stackinfo() function which replaces PUSHSTACKi() and +allows you to specify that the new stack should be RC by default. +(XXX core hasn't been updated mostly yet to use push_stackinfo()) + +Most places in the core assume a particular RC environment. In particular, +it is assumed that within a runops loop, all the PP functions are +RC-aware, either because they have been (re)written to be aware, or +because they have been wrapped. Whenever a runops loop is entered via +CALLRUNOPS(), it will check the current state of the stack, and if it's +not fully RC, will temporarily update its contents to be fully RC before +entering the main runops loop. Then if necessary it will restore the stack +to its old state on return. This means that functions like call_sv(), +which can be called from any environment (e.g. RC core or wrapped and +temporarily non-RC XS code) will always do the Right Thing when invoking +the runops loop, no matter what the current stack state is. + +Similarly, croaks and the like (which can occur anywhere) have to be able +to handle both stack types. So there are a few places in core - call_sv(), +eval_sv() etc, Perl_die_unwind() and S_my_exit_jump(), which have been +specially crafted to handle both cases; everything else can assume a fixed +environment. + +=head2 Wrapping + +Normally a core PP function is declared like this: + + PP(pp_foo) + { + ... + } + +This expands to something like: + + OP* Perl_foo(pTHX) + { + ... + } + +When such a function needs to be wrapped, it is instead declared as: + + PP_wrapped(pp_foo, nargs, nlists) + { + ... + } + +which on non-RC builds, expands to the same as PP() (the extra args are +ignored). On RC builds it expands to something like + + OP* Perl_pp_foo(pTHX) + { + return Perl_pp_wrap(aTHX_ S_Perl_pp_foo_norc, nargs, nlists); + } + + STATIC OP* S_Perl_pp_foo_norc(pTHX) + { + ... + } + +Here the externally visible PP function calls pp_wrap(), which adjusts +the stack contents, then calls the hidden real body of the PP function, +then on return, adjusts the stack back. + +There is an API macro, XSPP_wrapped(), intended for use on PP functions +declared in XS code, It is identical to PP_wrapped(), except that it +doesn't prepend a C prefix to the function name. + +The C and C parameters to the macro are numeric constants +or simple expressions which specify how many arguments the PP function +expects, or how many lists it expects. For example, + + PP_wrapped(pp_add, 2, 0); /* consumes two args off the stack */ + + PP_wrapped(pp_readline, /* consumes two or three args */ + ((PL_op->op_flags & OPf_STACKED) ? 2 : 1), 0); + + PP_wrapped(pp_push, 0, 1); /* consumes one list */ + + PP_wrapped(pp_aassign, 0, 2); /* consumes two lists */ + +To understand what pp_wrap() does, consider calling Perl_pp_foo() which +expects three arguments. On entry the stack may look like: + + ... A+ B+ C+ + +(where the C<+> indicates that the pointers to A, B and C are each +reference counted). The wrapper function pp_wrap() marks a cut at the +current stack position using C, then, based on the +value of C, pushes a copy of those three pointers above the cut: + + ... A+ B+ C+ | A0 B0 C0 + +(where the C<0> indicates that the pointers aren't RC), then calls the +real PP function, S_Perl_pp_foo_norc(). That function processes A, B and C, +pops them off the stack, and pushes some result SVs. None of this +manipulation adjusts any RCs. On return to pp_wrap(), the stack may look +something like: + + ... A+ B+ C+ | X0 Y0 + +The wrapper function bumps up the RCs of X and Y, decrements A B C, +shifts the results down and sets C to zero, leaving +the stack as: + + ... X+ Y+ + +In places like pp_entersub(), a similar wrapping (via the function +xs_wrap()) is done when calling XS subs. + +A complex calling environment might have multiple nested stacks with +different RC states. Perl starts off with an RC stack. Then for example, +pp_entersub() is called, which (via xs_wrap()) splits the stack and +executes the XS function in a non-RC environment. That function may call +PUSHSTACKi(), which creates a new non-RC stack, then calls call_sv(), which +does CALLRUNOPS(), which causes the new stack to temporarily become RC. +Then a tied method is called, which pushes a new RC stack, and so on. (XXX +currently tied methods actually push a non-RC stack. To be fixed soon). + +=head2 (Re)writing a PP function using the rpp_ API + +Wrapping a pp function has a performance overhead, and is there mainly as +a temporary crutch. Eventually, PP functions should be updated to use +rpp_() functions, and any new PP functions should be written this way from +scratch and thus not ever need wrapping. + +The traditional PP stack API consisted of a C declaration, plus a +number of macros to push, pop and extend the stack. A I +pp_add() function might look something like: + + PP(pp_add) + { + dSP; + dTARGET; + IV right = SvIV(POPs); + IV left = SvIV(POPs); + TARGi(left + right, 1); + PUSHs(TARG); + PUTBACK; + return NORMAL; + } + +which expands to something like: + + { + SV **sp = PL_stack_sp; + Sv *targ = PAD_SV(PL_op->op_targ); + IV right = SvIV(*sp--); + IV left = SvIV(*sp--); + sv_setiv(targ, left + right); + *++sp = targ; + PL_stack_sp = sp; + return PL_op->op_next; + } + +The whole C thing harks back to the days before decent optimising +compilers. It was always error-prone, e.g. if you forgot a C or +C. The new API always just accesses C directly. In +fact the first step of upgrading a PP function is always to remove the +C declaration. This has the happy side effect that any old-style +macros left in the pp function which implicitly use C will become +compile errors. The existence of a C somewhere in core is a good sign +that that function still needs updating. + +An obvious question is: why not just modify the definitions of the PUSHs() +etc macros to modify reference counts on RC builds? The issue is that perl +can croak at basically any point in execution (e.g. the SvIV() above might +call FETCH() on a tied variable which then croaks). Thus at all times the +RC of each SV must be properly accounted for. For example, doing something +like + + SV *sv = SvREFCNT_dec(*PL_stack--); + IV i = SvIV(sv); + +means that C leaks if SvIV() triggers a croak. Also, SvIV() would be +accessing a freed SV if C had an RC of 1. + +The new regime has the general outline that arguments are left on the +stack until they are finished with, then removed and their reference count +adjusted at that point. With the new API, the pp_add() function looks +something like: + + { + dTARGET; + IV right = SvIV(PL_stack_sp[ 0]); + IV left = SvIV(PL_stack_sp[-1]); + TARGi(left + right, 1); + rpp_replace_2_1(targ); + return NORMAL; + } + +The rpp_replace_2_1() function pops two values off the stack and pushes +one new value on, while adjusting reference counts as appropriate +(depending on whether built with C or not). + +The rpp_() functions in the new API are as follows. + + new function approximate old equivant + ------------ ----------------------- + + rpp_extend(n) EXTEND(SP, n) + + rpp_push_1(sv) PUSHs(sv) + rpp_push_2(sv1, sv2)) PUSHs(sv1); PUSHs(sv2) + rpp_xpush_1(sv) XPUSHs(sv) + rpp_xpush_2(sv1, sv2)) EXTEND(SP,2); PUSHs(sv1); PUSHs(sv2); + + rpp_push_1_norc(sv) mPUSHs(sv) // on RC bulds, skips RC++; + // on non-RC builds, mortalises + rpp_popfree_1() (void)POPs; + rpp_popfree_2() (void)POPs; (void)POPs; + rpp_popfree_to(svp) PL_stack_sp = svp; + rpp_obliterate_stack_to(svp) // see description below + + sv = rpp_pop_1_norc() sv = SvREFCNT_inc(POPs) + + rpp_replace_1_1(sv) (void)POPs; PUSHs(sv); + rpp_replace_2_1(sv) (void)POPs; (void)POPs; PUSHs(sv); + + rpp_try_AMAGIC_1() tryAMAGICun_MG() + rpp_try_AMAGIC_2() tryAMAGICbin_MG() + + (no replacement) dATARGET // just write the macro body in full + +Other new C and perl functions related to reference-counted stacks are: + + push_stackinfo(type,rc) PUSHSTACKi(type) + pop_stackinfo() POPSTACK() + switch_argstack(to) SWITCHSTACK(from,to) + + (Internals::stack_refcounted() & 1) # perl built with PERL_RC_STACK + +Note that rpp_popfree_1() etc aren't direct replacements for C. The +rpp_() variants don't return a value and are intended to be called when +the SV is finished with. So + + SV *sv = POPs; + ... do stuff with sv ... + +becomes + + SV *sv = *PL_stack_sp; + ... do stuff with sv ... + rpp_popfree_1(); /* does SvREFCNT_dec(*PL_stack_sp--) */ + +The rpp_replace_M_N() functions are shortcuts for popping and freeing C +items then pushing and bumping up the RCs of C items. Note that they +handle edge cases such as an old and new SV being the same. + +rpp_popfree_to(svp) is designed to replace code like + + PL_stack_sp = PL_stack_base + cx->blk_oldsp; + +which typically appears in list ops or scope exits when the arguments are +finished with. Left unaltered, all the SVs above C would leak. The +new approach is + + rpp_popfree_to(PL_stack_base + cx->blk_oldsp); + +There is a rarely-used variant of this, rpp_obliterate_stack_to(), which +pops the stack back to the specified index regardless of the current RC +state of the stack. So for example if the stack is split, it will only +adjust the RCs of any SVs which are below the split point, while +rpp_popfree_to() would mindlessly free I SVs (on RC builds anyway). +For normal PP functions you should only ever use rpp_popfree_to(), which +is faster. + +There are no new equivalents for all the convenience macros like POPi() +and (shudder) dPOPPOPiirl(). These should be replaced with the rpp_() +functions above and with the conversions and variable declarations being +made explicit, e.g. dPOPPOPiirl() becomes: + + IV right = SvIV(PL_stack_sp[ 0]); + IV left = SvIV(PL_stack_sp[-1]); + rpp_popfree_2(); + +A couple of the rpp_() functions with C in their names don't +adjust the reference count on RC builds, but do on non-RC builds (so the +reverse of most C functions). + +rpp_push_1_norc(sv) does a simple C<*++PL_stack_sp = sv> on RC builds. It +is typically used to "root" a newly-created SV, which already has an RC of +1. On non-RC builds it mortalises the SV instead. So for example, code +which used to look like + + mPUSHs(newSViv(i)); + +which expanded to the equivalent of: + + PUSHs(sv_2mortal(newSViv(i)); + +should be rewritten as: + + rpp_push_1_norc(newSViv(i)); + +This is because when the stack is reference-counted, rooting the SV on the +stack is sufficient; there's no longer a need to root it via the temps +stack. + +Similarly, on RC builds, C does a simple +C without adjusting the reference count, while on +non-RC builds it actually increments the SV's reference count. It is +intended for cases where you immediately want to increment the reference +count again after popping, e.g. where the SV is to be immediately embedded +somewhere. For example this code: + + SV *sv = PL_stack_sp[0]; + SvREFCNT_inc(sv); + av_store(av, i, sv); /* in real life should check return value */ + rpp_popfree_1(); + +can be more efficiently written as + + av_store(av, i, rpp_pop_1_norc()); + +By using this function, the code works correctly on both RC and non-RC +builds. + +The macros which appear at the start of many PP functions to check for +unary or binary op overloading (among other things) have been replaced +with rpp_try_AMAGIC_1() and _2() inline functions, which now rely on the +calling PP function to choose whether to return immediately rather than +the return being hidden away in the macro. + +In the spirit of hiding away less in macros, C hasn't been given +a replacement; where its effect is needed, it is now written out in full; +see pp_add() for an example. + +Finally, a couple of rpp() functions provide information rather than +manipulate the stack. + +rpp_is_lone(sv) indicates whether C, assumed to be still on the stack, +it kept alive only by a reference-counted pointer from the argument and/or +temps stacks, and thus is a candidate for some optimisations (like +skipping the copying of return arguments from a subroutine call). + +rpp_stack_is_rc() indicates whether the current stack is currently +reference-counted. It's used mainly in a few places like call_sv() which +can be called from anywhere, and thus have to deal with both cases. + +So for example, rather than using rpp_xpush_1(), call_sv() has lines like: + + rpp_extend(1); + *++PL_stack_sp = sv; + #ifdef PERL_RC_STACK + if (rpp_stack_is_rc()) + SvREFCNT_inc_simple_void_NN(sv); + #endif + +which works on both standard builds and RC builds, and works whether +call_sv() is called from a standard PP function (rpp_stack_is_rc() is +true) or from a wrapped PP or XS function (rpp_stack_is_rc() is false). +Note that you're unlikely to need to use this function, as in most places, +such as PP or XS functions, it is always RC or non-RC respectively. In +fact, under C, PUSHs() and similar macros include +an C, while rpp_push_1() and similar functions +have C. + +The macros for pushing new stackinfos have been replaced with inline +functions which don't rely on C being in scope, and which have less +ambiguous names: they make it clear that a new I is being +pushed, rather than just some sort of I. push_stackinfo() also has +a boolean argument indicating whether the new argument stack should be +reference-counted or not. For backwards compatibility, PUSHSTACKi(type) is +defined to be push_stackinfo(type, 0). + +Some test scripts check for things like leaks by testing that the +reference count of a particular variable has an expected value. If this +is different on a perl built with C, then the perl +function Internals::stack_refcounted() can be used. This returns an +integer, the lowest bit of which indicates that perl was built with +C. Other bits are reserved for future use and should be +masked out. + =head1 Slab-based operator allocation B this section describes a non-public internal API that is subject diff --git a/pp.c b/pp.c index e2a70151b35e..c603674d2fa3 100644 --- a/pp.c +++ b/pp.c @@ -34,7 +34,7 @@ /* variations on pp_null */ -PP(pp_stub) +PP_wrapped(pp_stub, 0, 0) { dSP; if (GIMME_V == G_SCALAR) @@ -46,7 +46,7 @@ PP(pp_stub) -PP(pp_padcv) +PP_wrapped(pp_padcv, 0, 0) { dSP; dTARGET; assert(SvTYPE(TARG) == SVt_PVCV); @@ -187,7 +187,7 @@ S_rv2gv(pTHX_ SV *sv, const bool vivify_sv, const bool strict, return sv; } -PP(pp_rv2gv) +PP_wrapped(pp_rv2gv, 1, 0) { dSP; dTOPss; @@ -248,7 +248,7 @@ Perl_softref2xv(pTHX_ SV *const sv, const char *const what, return gv; } -PP(pp_rv2sv) +PP_wrapped(pp_rv2sv, 1, 0) { dSP; dTOPss; GV *gv = NULL; @@ -290,7 +290,7 @@ PP(pp_rv2sv) RETURN; } -PP(pp_av2arylen) +PP_wrapped(pp_av2arylen, 1, 0) { dSP; AV * const av = MUTABLE_AV(TOPs); @@ -308,7 +308,7 @@ PP(pp_av2arylen) RETURN; } -PP(pp_pos) +PP_wrapped(pp_pos, 1, 0) { dSP; dTOPss; @@ -338,7 +338,7 @@ PP(pp_pos) return NORMAL; } -PP(pp_rv2cv) +PP_wrapped(pp_rv2cv, 1, 0) { dSP; GV *gv; @@ -365,7 +365,7 @@ PP(pp_rv2cv) return NORMAL; } -PP(pp_prototype) +PP_wrapped(pp_prototype, 1, 0) { dSP; CV *cv; @@ -398,7 +398,7 @@ PP(pp_prototype) RETURN; } -PP(pp_anoncode) +PP_wrapped(pp_anoncode, 0, 0) { dSP; CV *cv = MUTABLE_CV(PAD_SV(PL_op->op_targ)); @@ -417,14 +417,14 @@ PP(pp_anoncode) RETURN; } -PP(pp_srefgen) +PP_wrapped(pp_srefgen, 1, 0) { dSP; *SP = refto(*SP); return NORMAL; } -PP(pp_refgen) +PP_wrapped(pp_refgen, 0, 1) { dSP; dMARK; if (GIMME_V != G_LIST) { @@ -480,7 +480,7 @@ S_refto(pTHX_ SV *sv) return rv; } -PP(pp_ref) +PP_wrapped(pp_ref, 1, 0) { dSP; SV * const sv = TOPs; @@ -530,7 +530,7 @@ PP(pp_ref) } -PP(pp_bless) +PP_wrapped(pp_bless, MAXARG, 0) { dSP; HV *stash; @@ -571,7 +571,7 @@ PP(pp_bless) RETURN; } -PP(pp_gelem) +PP_wrapped(pp_gelem, 2, 0) { dSP; @@ -647,7 +647,7 @@ PP(pp_gelem) /* Pattern matching */ -PP(pp_study) +PP_wrapped(pp_study, 1, 0) { dSP; dTOPss; STRLEN len; @@ -668,7 +668,7 @@ PP(pp_study) /* also used for: pp_transr() */ -PP(pp_trans) +PP_wrapped(pp_trans, ((PL_op->op_flags & OPf_STACKED) ? 1 : 0), 0) { dSP; SV *sv; @@ -844,7 +844,7 @@ S_do_chomp(pTHX_ SV *retval, SV *sv, bool chomping) /* also used for: pp_schomp() */ -PP(pp_schop) +PP_wrapped(pp_schop, 1, 0) { dSP; dTARGET; const bool chomping = PL_op->op_type == OP_SCHOMP; @@ -859,7 +859,7 @@ PP(pp_schop) /* also used for: pp_chomp() */ -PP(pp_chop) +PP_wrapped(pp_chop, 0, 1) { dSP; dMARK; dTARGET; dORIGMARK; const bool chomping = PL_op->op_type == OP_CHOMP; @@ -874,7 +874,9 @@ PP(pp_chop) RETURN; } -PP(pp_undef) +PP_wrapped(pp_undef, + ((!PL_op->op_private || (PL_op->op_private & OPpTARGET_MY)) ? 0 : 1), + 0) { dSP; SV *sv; @@ -1022,7 +1024,7 @@ S_postincdec_common(pTHX_ SV *sv, SV *targ) /* also used for: pp_i_postinc() */ -PP(pp_postinc) +PP_wrapped(pp_postinc, 1, 0) { dSP; dTARGET; SV *sv = TOPs; @@ -1047,7 +1049,7 @@ PP(pp_postinc) /* also used for: pp_i_postdec() */ -PP(pp_postdec) +PP_wrapped(pp_postdec, 1, 0) { dSP; dTARGET; SV *sv = TOPs; @@ -1072,7 +1074,7 @@ PP(pp_postdec) /* Ordinary operators. */ -PP(pp_pow) +PP_wrapped(pp_pow, 2, 0) { dSP; dATARGET; SV *svl, *svr; #ifdef PERL_PRESERVE_IVUV @@ -1253,7 +1255,7 @@ PP(pp_pow) } } -PP(pp_multiply) +PP_wrapped(pp_multiply, 2, 0) { dSP; dATARGET; SV *svl, *svr; tryAMAGICbin_MG(mult_amg, AMGf_assign|AMGf_numeric); @@ -1446,7 +1448,7 @@ PP(pp_multiply) } } -PP(pp_divide) +PP_wrapped(pp_divide, 2, 0) { dSP; dATARGET; SV *svl, *svr; tryAMAGICbin_MG(div_amg, AMGf_assign|AMGf_numeric); @@ -1563,7 +1565,7 @@ PP(pp_divide) } } -PP(pp_modulo) +PP_wrapped(pp_modulo, 2, 0) { dSP; dATARGET; tryAMAGICbin_MG(modulo_amg, AMGf_assign|AMGf_numeric); @@ -1690,7 +1692,10 @@ PP(pp_modulo) } } -PP(pp_repeat) +PP_wrapped(pp_repeat, + /* two scalar args or one list */ + ((PL_op->op_private & OPpREPEAT_DOLIST) ? 0 : 2), + ((PL_op->op_private & OPpREPEAT_DOLIST) ? 1 : 0)) { dSP; dATARGET; IV count; @@ -1834,7 +1839,7 @@ PP(pp_repeat) RETURN; } -PP(pp_subtract) +PP_wrapped(pp_subtract, 2, 0) { dSP; dATARGET; bool useleft; SV *svl, *svr; tryAMAGICbin_MG(subtr_amg, AMGf_assign|AMGf_numeric); @@ -2064,7 +2069,7 @@ static IV S_iv_shift(IV iv, int shift, bool left) #define IV_LEFT_SHIFT(iv, shift) S_iv_shift(iv, shift, TRUE) #define IV_RIGHT_SHIFT(iv, shift) S_iv_shift(iv, shift, FALSE) -PP(pp_left_shift) +PP_wrapped(pp_left_shift, 2, 0) { dSP; dATARGET; SV *svl, *svr; tryAMAGICbin_MG(lshift_amg, AMGf_assign|AMGf_numeric); @@ -2082,7 +2087,7 @@ PP(pp_left_shift) } } -PP(pp_right_shift) +PP_wrapped(pp_right_shift, 2, 0) { dSP; dATARGET; SV *svl, *svr; tryAMAGICbin_MG(rshift_amg, AMGf_assign|AMGf_numeric); @@ -2100,7 +2105,7 @@ PP(pp_right_shift) } } -PP(pp_lt) +PP_wrapped(pp_lt, 2, 0) { dSP; SV *left, *right; @@ -2122,7 +2127,7 @@ PP(pp_lt) RETURN; } -PP(pp_gt) +PP_wrapped(pp_gt, 2, 0) { dSP; SV *left, *right; @@ -2144,7 +2149,7 @@ PP(pp_gt) RETURN; } -PP(pp_le) +PP_wrapped(pp_le, 2, 0) { dSP; SV *left, *right; @@ -2166,7 +2171,7 @@ PP(pp_le) RETURN; } -PP(pp_ge) +PP_wrapped(pp_ge, 2, 0) { dSP; SV *left, *right; @@ -2188,7 +2193,7 @@ PP(pp_ge) RETURN; } -PP(pp_ne) +PP_wrapped(pp_ne, 2, 0) { dSP; SV *left, *right; @@ -2282,7 +2287,7 @@ Perl_do_ncmp(pTHX_ SV* const left, SV * const right) } -PP(pp_ncmp) +PP_wrapped(pp_ncmp, 2, 0) { dSP; SV *left, *right; @@ -2304,7 +2309,7 @@ PP(pp_ncmp) /* also used for: pp_sge() pp_sgt() pp_slt() */ -PP(pp_sle) +PP_wrapped(pp_sle, 2, 0) { dSP; @@ -2346,7 +2351,7 @@ PP(pp_sle) } } -PP(pp_seq) +PP_wrapped(pp_seq, 2, 0) { dSP; tryAMAGICbin_MG(seq_amg, 0); @@ -2357,7 +2362,7 @@ PP(pp_seq) } } -PP(pp_sne) +PP_wrapped(pp_sne, 2, 0) { dSP; tryAMAGICbin_MG(sne_amg, 0); @@ -2368,7 +2373,7 @@ PP(pp_sne) } } -PP(pp_scmp) +PP_wrapped(pp_scmp, 2, 0) { dSP; dTARGET; tryAMAGICbin_MG(scmp_amg, 0); @@ -2386,7 +2391,7 @@ PP(pp_scmp) } } -PP(pp_bit_and) +PP_wrapped(pp_bit_and, 2, 0) { dSP; dATARGET; tryAMAGICbin_MG(band_amg, AMGf_assign); @@ -2414,7 +2419,7 @@ PP(pp_bit_and) } } -PP(pp_nbit_and) +PP_wrapped(pp_nbit_and, 2, 0) { dSP; tryAMAGICbin_MG(band_amg, AMGf_assign|AMGf_numarg); @@ -2432,7 +2437,7 @@ PP(pp_nbit_and) RETURN; } -PP(pp_sbit_and) +PP_wrapped(pp_sbit_and, 2, 0) { dSP; tryAMAGICbin_MG(sband_amg, AMGf_assign); @@ -2445,7 +2450,7 @@ PP(pp_sbit_and) /* also used for: pp_bit_xor() */ -PP(pp_bit_or) +PP_wrapped(pp_bit_or, 2, 0) { dSP; dATARGET; const int op_type = PL_op->op_type; @@ -2481,7 +2486,7 @@ PP(pp_bit_or) /* also used for: pp_nbit_xor() */ -PP(pp_nbit_or) +PP_wrapped(pp_nbit_or, 2, 0) { dSP; const int op_type = PL_op->op_type; @@ -2508,7 +2513,7 @@ PP(pp_nbit_or) /* also used for: pp_sbit_xor() */ -PP(pp_sbit_or) +PP_wrapped(pp_sbit_or, 2, 0) { dSP; const int op_type = PL_op->op_type; @@ -2546,7 +2551,7 @@ S_negate_string(pTHX) return TRUE; } -PP(pp_negate) +PP_wrapped(pp_negate, 1, 0) { dSP; dTARGET; tryAMAGICun_MG(neg_amg, AMGf_numeric); @@ -2592,12 +2597,9 @@ PP(pp_negate) PP(pp_not) { - dSP; - SV *sv; - - tryAMAGICun_MG(not_amg, 0); - sv = *PL_stack_sp; - *PL_stack_sp = boolSV(!SvTRUE_nomg_NN(sv)); + if (rpp_try_AMAGIC_1(not_amg, 0)) + return NORMAL; + rpp_replace_1_1(boolSV(!SvTRUE_nomg_NN(*PL_stack_sp))); return NORMAL; } @@ -2635,7 +2637,7 @@ S_scomplement(pTHX_ SV *targ, SV *sv) *tmps = ~*tmps; } -PP(pp_complement) +PP_wrapped(pp_complement, 1, 0) { dSP; dTARGET; tryAMAGICun_MG(compl_amg, AMGf_numeric); @@ -2659,7 +2661,7 @@ PP(pp_complement) } } -PP(pp_ncomplement) +PP_wrapped(pp_ncomplement, 1, 0) { dSP; tryAMAGICun_MG(compl_amg, AMGf_numeric|AMGf_numarg); @@ -2677,7 +2679,7 @@ PP(pp_ncomplement) return NORMAL; } -PP(pp_scomplement) +PP_wrapped(pp_scomplement, 1, 0) { dSP; tryAMAGICun_MG(scompl_amg, AMGf_numeric); @@ -2691,7 +2693,7 @@ PP(pp_scomplement) /* integer versions of some of the above */ -PP(pp_i_multiply) +PP_wrapped(pp_i_multiply, 2, 0) { dSP; dATARGET; tryAMAGICbin_MG(mult_amg, AMGf_assign); @@ -2702,7 +2704,7 @@ PP(pp_i_multiply) } } -PP(pp_i_divide) +PP_wrapped(pp_i_divide, 2, 0) { IV num; dSP; dATARGET; @@ -2724,7 +2726,7 @@ PP(pp_i_divide) } } -PP(pp_i_modulo) +PP_wrapped(pp_i_modulo, 2, 0) { dSP; dATARGET; tryAMAGICbin_MG(modulo_amg, AMGf_assign); @@ -2741,7 +2743,7 @@ PP(pp_i_modulo) } } -PP(pp_i_add) +PP_wrapped(pp_i_add, 2, 0) { dSP; dATARGET; tryAMAGICbin_MG(add_amg, AMGf_assign); @@ -2752,7 +2754,7 @@ PP(pp_i_add) } } -PP(pp_i_subtract) +PP_wrapped(pp_i_subtract, 2, 0) { dSP; dATARGET; tryAMAGICbin_MG(subtr_amg, AMGf_assign); @@ -2763,7 +2765,7 @@ PP(pp_i_subtract) } } -PP(pp_i_lt) +PP_wrapped(pp_i_lt, 2, 0) { dSP; tryAMAGICbin_MG(lt_amg, 0); @@ -2774,7 +2776,7 @@ PP(pp_i_lt) } } -PP(pp_i_gt) +PP_wrapped(pp_i_gt, 2, 0) { dSP; tryAMAGICbin_MG(gt_amg, 0); @@ -2785,7 +2787,7 @@ PP(pp_i_gt) } } -PP(pp_i_le) +PP_wrapped(pp_i_le, 2, 0) { dSP; tryAMAGICbin_MG(le_amg, 0); @@ -2796,7 +2798,7 @@ PP(pp_i_le) } } -PP(pp_i_ge) +PP_wrapped(pp_i_ge, 2, 0) { dSP; tryAMAGICbin_MG(ge_amg, 0); @@ -2807,7 +2809,7 @@ PP(pp_i_ge) } } -PP(pp_i_eq) +PP_wrapped(pp_i_eq, 2, 0) { dSP; tryAMAGICbin_MG(eq_amg, 0); @@ -2818,7 +2820,7 @@ PP(pp_i_eq) } } -PP(pp_i_ne) +PP_wrapped(pp_i_ne, 2, 0) { dSP; tryAMAGICbin_MG(ne_amg, 0); @@ -2829,7 +2831,7 @@ PP(pp_i_ne) } } -PP(pp_i_ncmp) +PP_wrapped(pp_i_ncmp, 2, 0) { dSP; dTARGET; tryAMAGICbin_MG(ncmp_amg, 0); @@ -2848,7 +2850,7 @@ PP(pp_i_ncmp) } } -PP(pp_i_negate) +PP_wrapped(pp_i_negate, 2, 0) { dSP; dTARGET; tryAMAGICun_MG(neg_amg, 0); @@ -2863,7 +2865,7 @@ PP(pp_i_negate) /* High falutin' math. */ -PP(pp_atan2) +PP_wrapped(pp_atan2, 2, 0) { dSP; dTARGET; tryAMAGICbin_MG(atan2_amg, 0); @@ -2877,7 +2879,7 @@ PP(pp_atan2) /* also used for: pp_cos() pp_exp() pp_log() pp_sqrt() */ -PP(pp_sin) +PP_wrapped(pp_sin, 1, 0) { dSP; dTARGET; int amg_type = fallback_amg; @@ -2944,7 +2946,7 @@ PP(pp_sin) --Jarkko Hietaniemi 27 September 1998 */ -PP(pp_rand) +PP_wrapped(pp_rand, MAXARG, 0) { if (!PL_srand_called) { Rand_seed_t s; @@ -2994,7 +2996,7 @@ PP(pp_rand) return NORMAL; } -PP(pp_srand) +PP_wrapped(pp_srand, MAXARG, 0) { dSP; dTARGET; UV anum; @@ -3038,7 +3040,7 @@ PP(pp_srand) RETURN; } -PP(pp_int) +PP_wrapped(pp_int, 1, 0) { dSP; dTARGET; tryAMAGICun_MG(int_amg, AMGf_numeric); @@ -3082,7 +3084,7 @@ PP(pp_int) return NORMAL; } -PP(pp_abs) +PP_wrapped(pp_abs, 1, 0) { dSP; dTARGET; tryAMAGICun_MG(abs_amg, AMGf_numeric); @@ -3125,7 +3127,7 @@ PP(pp_abs) /* also used for: pp_hex() */ -PP(pp_oct) +PP_wrapped(pp_oct, 1, 0) { dSP; dTARGET; const char *tmps; @@ -3182,7 +3184,7 @@ PP(pp_oct) /* String stuff. */ -PP(pp_length) +PP_wrapped(pp_length, 1, 0) { dSP; dTARGET; SV * const sv = TOPs; @@ -3316,7 +3318,10 @@ Perl_translate_substr_offsets( STRLEN curlen, IV pos1_iv, return TRUE; } -PP(pp_substr) +PP_wrapped(pp_substr, + (PL_op->op_private & 7) + + ((PL_op->op_private & OPpSUBSTR_REPL_FIRST) ? 1 : 0), + 0) { dSP; dTARGET; SV *sv; @@ -3456,7 +3461,7 @@ PP(pp_substr) RETPUSHUNDEF; } -PP(pp_vec) +PP_wrapped(pp_vec, 3, 0) { dSP; const IV size = POPi; @@ -3513,7 +3518,7 @@ PP(pp_vec) /* also used for: pp_rindex() */ -PP(pp_index) +PP_wrapped(pp_index, MAXARG, 0) { dSP; dTARGET; SV *big; @@ -3634,7 +3639,7 @@ PP(pp_index) RETURN; } -PP(pp_sprintf) +PP_wrapped(pp_sprintf, 0, 1) { dSP; dMARK; dORIGMARK; dTARGET; SvTAINTED_off(TARG); @@ -3645,7 +3650,7 @@ PP(pp_sprintf) RETURN; } -PP(pp_ord) +PP_wrapped(pp_ord, 1, 0) { dSP; dTARGET; @@ -3660,7 +3665,7 @@ PP(pp_ord) return NORMAL; } -PP(pp_chr) +PP_wrapped(pp_chr, 1, 0) { dSP; dTARGET; char *tmps; @@ -3718,7 +3723,7 @@ PP(pp_chr) return NORMAL; } -PP(pp_crypt) +PP_wrapped(pp_crypt, 2, 0) { #ifdef HAS_CRYPT dSP; dTARGET; @@ -3769,7 +3774,7 @@ PP(pp_crypt) /* also used for: pp_lcfirst() */ -PP(pp_ucfirst) +PP_wrapped(pp_ucfirst, 1, 0) { /* Actually is both lcfirst() and ucfirst(). Only the first character * changes. This means that possibly we can change in-place, ie., just @@ -4117,7 +4122,8 @@ PP(pp_ucfirst) return NORMAL; } -PP(pp_uc) + +PP_wrapped(pp_uc, 1, 0) { dSP; SV *source = TOPs; @@ -4441,7 +4447,7 @@ PP(pp_uc) return NORMAL; } -PP(pp_lc) +PP_wrapped(pp_lc, 1, 0) { dSP; SV *source = TOPs; @@ -4658,7 +4664,7 @@ PP(pp_lc) return NORMAL; } -PP(pp_quotemeta) +PP_wrapped(pp_quotemeta, 1, 0) { dSP; dTARGET; SV * const sv = TOPs; @@ -4736,7 +4742,7 @@ PP(pp_quotemeta) return NORMAL; } -PP(pp_fc) +PP_wrapped(pp_fc, 1, 0) { dTARGET; dSP; @@ -4946,7 +4952,7 @@ PP(pp_fc) /* Arrays. */ -PP(pp_aslice) +PP_wrapped(pp_aslice, 0, 1) { dSP; dMARK; dORIGMARK; AV *const av = MUTABLE_AV(POPs); @@ -5010,7 +5016,7 @@ PP(pp_aslice) RETURN; } -PP(pp_kvaslice) +PP_wrapped(pp_kvaslice, 0, 1) { dSP; dMARK; AV *const av = MUTABLE_AV(POPs); @@ -5056,7 +5062,7 @@ PP(pp_kvaslice) } -PP(pp_aeach) +PP_wrapped(pp_aeach, 1, 0) { dSP; AV *array = MUTABLE_AV(POPs); @@ -5082,7 +5088,7 @@ PP(pp_aeach) } /* also used for: pp_avalues()*/ -PP(pp_akeys) +PP_wrapped(pp_akeys, 1, 0) { dSP; AV *array = MUTABLE_AV(POPs); @@ -5129,7 +5135,7 @@ PP(pp_akeys) /* Associative arrays. */ -PP(pp_each) +PP_wrapped(pp_each, 1, 0) { dSP; HV * hash = MUTABLE_HV(POPs); @@ -5265,7 +5271,9 @@ S_do_delete_local(pTHX) RETURN; } -PP(pp_delete) +PP_wrapped(pp_delete, + ((PL_op->op_private & (OPpSLICE|OPpKVSLICE)) ? 0 : 2), + ((PL_op->op_private & (OPpSLICE|OPpKVSLICE)) ? 1 : 0)) { dSP; U8 gimme; @@ -5343,7 +5351,7 @@ PP(pp_delete) RETURN; } -PP(pp_exists) +PP_wrapped(pp_exists, ((PL_op->op_private & OPpEXISTS_SUB) ? 1 : 2), 0) { dSP; SV *tmpsv; @@ -5405,7 +5413,7 @@ PP(pp_exists) * as required. */ -PP(pp_helemexistsor) +PP_wrapped(pp_helemexistsor, 2, 0) { dSP; SV *keysv = POPs; @@ -5449,7 +5457,7 @@ PP(pp_helemexistsor) RETURN; } -PP(pp_hslice) +PP_wrapped(pp_hslice, 0, 1) { dSP; dMARK; dORIGMARK; HV * const hv = MUTABLE_HV(POPs); @@ -5506,7 +5514,7 @@ PP(pp_hslice) RETURN; } -PP(pp_kvhslice) +PP_wrapped(pp_kvhslice, 0, 1) { dSP; dMARK; HV * const hv = MUTABLE_HV(POPs); @@ -5558,7 +5566,7 @@ PP(pp_kvhslice) /* List operators. */ -PP(pp_list) +PP_wrapped(pp_list, 0, 1) { I32 markidx = POPMARK; if (GIMME_V != G_LIST) { @@ -5577,7 +5585,7 @@ PP(pp_list) return NORMAL; } -PP(pp_lslice) +PP_wrapped(pp_lslice, 0, 2) { dSP; SV ** const lastrelem = PL_stack_sp; @@ -5630,7 +5638,7 @@ PP(pp_lslice) RETURN; } -PP(pp_anonlist) +PP_wrapped(pp_anonlist, 0, 1) { dSP; dMARK; const I32 items = SP - MARK; @@ -5644,7 +5652,7 @@ PP(pp_anonlist) /* When an anonlist or anonhash will (1) be empty and (2) return an RV * pointing to the new AV/HV, the peephole optimizer can swap in this * simpler function and op_null the originally associated PUSHMARK. */ -PP(pp_emptyavhv) +PP_wrapped(pp_emptyavhv, 0,0) { dSP; OP * const op = PL_op; @@ -5686,7 +5694,7 @@ PP(pp_emptyavhv) RETURN; } -PP(pp_anonhash) +PP_wrapped(pp_anonhash, 0, 1) { dSP; dMARK; dORIGMARK; HV* const hv = newHV(); @@ -5723,7 +5731,7 @@ PP(pp_anonhash) RETURN; } -PP(pp_splice) +PP_wrapped(pp_splice, 0, 1) { dSP; dMARK; dORIGMARK; int num_args = (SP - MARK); @@ -5940,7 +5948,7 @@ PP(pp_splice) RETURN; } -PP(pp_push) +PP_wrapped(pp_push, 0, 1) { dSP; dMARK; dORIGMARK; dTARGET; AV * const ary = MUTABLE_AV(*++MARK); @@ -5982,7 +5990,7 @@ PP(pp_push) } /* also used for: pp_pop()*/ -PP(pp_shift) +PP_wrapped(pp_shift, (PL_op->op_flags & OPf_SPECIAL ? 0 : 1), 0) { dSP; AV * const av = PL_op->op_flags & OPf_SPECIAL @@ -5996,7 +6004,7 @@ PP(pp_shift) RETURN; } -PP(pp_unshift) +PP_wrapped(pp_unshift, 0, 1) { dSP; dMARK; dORIGMARK; dTARGET; AV *ary = MUTABLE_AV(*++MARK); @@ -6055,7 +6063,7 @@ PP(pp_unshift) RETURN; } -PP(pp_reverse) +PP_wrapped(pp_reverse, 0, 1) { dSP; dMARK; @@ -6190,7 +6198,11 @@ PP(pp_reverse) RETURN; } -PP(pp_split) +PP_wrapped(pp_split, + ( (PL_op->op_private & OPpSPLIT_ASSIGN) + && (PL_op->op_flags & OPf_STACKED)) + ? 3 : 2, + 0) { dSP; dTARG; AV *ary = ( (PL_op->op_private & OPpSPLIT_ASSIGN) /* @a = split */ @@ -6643,18 +6655,17 @@ PP(pp_split) PP(pp_once) { - dSP; SV *const sv = PAD_SVl(PL_op->op_targ); if (SvPADSTALE(sv)) { /* First time. */ SvPADSTALE_off(sv); - RETURNOP(cLOGOP->op_other); + return cLOGOP->op_other; } - RETURNOP(cLOGOP->op_next); + return cLOGOP->op_next; } -PP(pp_lock) +PP_wrapped(pp_lock, 1, 0) { dSP; dTOPss; @@ -6703,7 +6714,7 @@ S_maybe_unwind_defav(pTHX) } /* For sorting out arguments passed to a &CORE:: subroutine */ -PP(pp_coreargs) +PP_wrapped(pp_coreargs, 0, 0) { dSP; int opnum = SvIOK(cSVOP_sv) ? (int)SvUV(cSVOP_sv) : 0; @@ -6748,8 +6759,7 @@ PP(pp_coreargs) to come in between two things this function does (stack reset and arg pushing). This seems the easiest way to do it. */ if (pushmark) { - PUTBACK; - (void)Perl_pp_pushmark(aTHX); + PUSHMARK(SP); } EXTEND(SP, maxargs == I32_MAX ? numargs : maxargs); @@ -6880,14 +6890,13 @@ PP(pp_coreargs) PP(pp_avhvswitch) { - dSP; return PL_ppaddr[ - (SvTYPE(TOPs) == SVt_PVAV ? OP_AEACH : OP_EACH) + (SvTYPE(*PL_stack_sp) == SVt_PVAV ? OP_AEACH : OP_EACH) + (PL_op->op_private & OPpAVHVSWITCH_MASK) ](aTHX); } -PP(pp_runcv) +PP_wrapped(pp_runcv, 0, 0) { dSP; CV *cv; @@ -6948,7 +6957,11 @@ S_localise_gv_slot(pTHX_ GV *gv, U8 type) } -PP(pp_refassign) +PP_wrapped(pp_refassign, + !!(PL_op->op_private & OPpLVREF_ELEM) + + !!(PL_op->op_flags & OPf_STACKED) + +1, + 0) { dSP; SV * const key = PL_op->op_private & OPpLVREF_ELEM ? POPs : NULL; @@ -7022,7 +7035,9 @@ PP(pp_refassign) } } -PP(pp_lvref) +PP_wrapped(pp_lvref, + !!(PL_op->op_private & OPpLVREF_ELEM) + !!(PL_op->op_flags & OPf_STACKED), + 0) { dSP; SV * const ret = newSV_type_mortal(SVt_PVMG); @@ -7058,7 +7073,7 @@ PP(pp_lvref) RETURN; } -PP(pp_lvrefslice) +PP_wrapped(pp_lvrefslice, 0, 1) { dSP; dMARK; AV * const av = (AV *)POPs; @@ -7099,7 +7114,7 @@ PP(pp_lvrefslice) RETURN; } -PP(pp_lvavref) +PP_wrapped(pp_lvavref, !!(PL_op->op_flags & OPf_STACKED), 0) { if (PL_op->op_flags & OPf_STACKED) Perl_pp_rv2av(aTHX); @@ -7114,7 +7129,7 @@ PP(pp_lvavref) } } -PP(pp_anonconst) +PP_wrapped(pp_anonconst, 1, 0) { dSP; dTOPss; @@ -7155,7 +7170,10 @@ PP(pp_anonconst) * etc */ -PP(pp_argelem) +PP_wrapped(pp_argelem, + !!( (PL_op->op_private & OPpARGELEM_MASK) == OPpARGELEM_SV + && (PL_op->op_flags & OPf_STACKED)), + 0) { dTARG; SV *val; @@ -7302,7 +7320,7 @@ PP(pp_argelem) * into PL_curpad. */ -PP(pp_argdefelem) +PP_wrapped(pp_argdefelem, 0, 0) { OP * const o = PL_op; AV *defav = GvAV(PL_defgv); /* @_ */ @@ -7392,7 +7410,7 @@ PP(pp_argcheck) return NORMAL; } -PP(pp_isa) +PP_wrapped(pp_isa, 2, 0) { dSP; SV *left, *right; @@ -7404,7 +7422,7 @@ PP(pp_isa) RETURN; } -PP(pp_cmpchain_and) +PP_wrapped(pp_cmpchain_and, 2, 0) { dSP; SV *result = POPs; @@ -7417,7 +7435,7 @@ PP(pp_cmpchain_and) } } -PP(pp_cmpchain_dup) +PP_wrapped(pp_cmpchain_dup, 2, 0) { dSP; SV *right = TOPs; @@ -7428,7 +7446,7 @@ PP(pp_cmpchain_dup) RETURN; } -PP(pp_is_bool) +PP_wrapped(pp_is_bool, 1, 0) { SV *arg = *PL_stack_sp; @@ -7438,7 +7456,7 @@ PP(pp_is_bool) return NORMAL; } -PP(pp_is_weak) +PP_wrapped(pp_is_weak, 1, 0) { SV *arg = *PL_stack_sp; @@ -7448,7 +7466,7 @@ PP(pp_is_weak) return NORMAL; } -PP(pp_weaken) +PP_wrapped(pp_weaken, 1, 0) { dSP; SV *arg = POPs; @@ -7457,7 +7475,7 @@ PP(pp_weaken) RETURN; } -PP(pp_unweaken) +PP_wrapped(pp_unweaken, 1, 0) { dSP; SV *arg = POPs; @@ -7466,7 +7484,7 @@ PP(pp_unweaken) RETURN; } -PP(pp_blessed) +PP_wrapped(pp_blessed, 1, 0) { dSP; SV *arg = TOPs; @@ -7502,7 +7520,7 @@ PP(pp_blessed) RETURN; } -PP(pp_refaddr) +PP_wrapped(pp_refaddr, 1, 0) { dSP; dTARGET; @@ -7519,7 +7537,7 @@ PP(pp_refaddr) RETURN; } -PP(pp_reftype) +PP_wrapped(pp_reftype, 1, 0) { dSP; dTARGET; @@ -7536,7 +7554,7 @@ PP(pp_reftype) RETURN; } -PP(pp_ceil) +PP_wrapped(pp_ceil, 1, 0) { dSP; dTARGET; @@ -7544,7 +7562,7 @@ PP(pp_ceil) RETURN; } -PP(pp_floor) +PP_wrapped(pp_floor, 1, 0) { dSP; dTARGET; @@ -7552,7 +7570,7 @@ PP(pp_floor) RETURN; } -PP(pp_is_tainted) +PP_wrapped(pp_is_tainted, 1, 0) { SV *arg = *PL_stack_sp; diff --git a/pp.h b/pp.h index 65d07ce4d9fc..2e1ed31e23ea 100644 --- a/pp.h +++ b/pp.h @@ -8,6 +8,57 @@ * */ +/* +=for apidoc_section $rpp + +=for apidoc Amux||XSPP_wrapped|xsppw_name|I32 xsppw_nargs|I32 xsppw_nlists +Declare and wrap a non-reference-counted PP-style function. +On traditional perl builds where the stack isn't reference-counted, this +just produces a function declaration like + + OP * xsppw_name(pTHX) + +Conversely, in ref-counted builds it creates xsppw_name() as a small +wrapper function which calls the real function via a wrapper which +processes the args and return values to ensure that reference counts are +properly handled for code which uses old-style dSP, PUSHs(), POPs() etc, +which don't adjust the reference counts of the items they manipulate. + +xsppw_nargs indicates how many arguments the function consumes off the +stack. It can be a constant value or an expression, such as + + ((PL_op->op_flags & OPf_STACKED) ? 2 : 1) + +Alternatively if xsppw_nlists is 1, it indicates that the PP function +consumes a list (or - rarely - if 2, consumes two lists, like +pp_aassign()), as indicated by the top markstack position. + +This is intended as a temporary fix when converting XS code to run under +PERL_RC_STACK builds. In the longer term, the PP function should be +rewritten to replace PUSHs() etc with rpp_push_1() etc. + +=cut +*/ + +#ifdef PERL_RC_STACK +# define XSPP_wrapped(xsppw_name, xsppw_nargs, xsppw_nlists) \ + \ +STATIC OP* S_##xsppw_name##_norc(pTHX); \ +OP* xsppw_name(pTHX) \ +{ \ + return Perl_pp_wrap(aTHX_ S_##xsppw_name##_norc, \ + (xsppw_nargs), (xsppw_nlists)); \ +} \ +STATIC OP* S_##xsppw_name##_norc(pTHX) + +#else +# define XSPP_wrapped(xsppw_name, xsppw_nargs, xsppw_nlists) \ + OP * xsppw_name(pTHX) +#endif + +#define PP_wrapped(ppw_name, ppw_nargs, ppw_nlists) \ + XSPP_wrapped(Perl_##ppw_name, ppw_nargs, ppw_nlists) + #define PP(s) OP * Perl_##s(pTHX) /* @@ -159,7 +210,12 @@ Pops an unsigned long off the stack. #define RETURNOP(o) return (PUTBACK, o) #define RETURNX(x) return (x, PUTBACK, NORMAL) -#define POPs (*sp--) +#if defined(PERL_RC_STACK) && defined(DEBUG_LEAKING_SCALARS) +# define POPs (assert(!rpp_stack_is_rc()), *sp--) +#else +# define POPs (*sp--) +#endif + #define POPp POPpx #define POPpx (SvPVx_nolen(POPs)) #define POPpconstx (SvPVx_nolen_const(POPs)) @@ -487,7 +543,12 @@ Does not use C. See also C>, C> and C>. sv_setnv_mg(targ, TARGn_nv); \ } STMT_END -#define PUSHs(s) (*++sp = (s)) +#if defined(PERL_RC_STACK) && defined(DEBUG_LEAKING_SCALARS) +# define PUSHs(s) (assert(!rpp_stack_is_rc()), *++sp = (s)) +#else +# define PUSHs(s) (*++sp = (s)) +#endif + #define PUSHTARG STMT_START { SvSETMAGIC(TARG); PUSHs(TARG); } STMT_END #define PUSHp(p,l) STMT_START { sv_setpvn(TARG, (p), (l)); PUSHTARG; } STMT_END #define PUSHpvs(s) PUSHp("" s "", sizeof(s)-1) @@ -495,7 +556,7 @@ Does not use C. See also C>, C> and C>. #define PUSHi(i) STMT_START { TARGi(i,1); PUSHs(TARG); } STMT_END #define PUSHu(u) STMT_START { TARGu(u,1); PUSHs(TARG); } STMT_END -#define XPUSHs(s) STMT_START { EXTEND(sp,1); *++sp = (s); } STMT_END +#define XPUSHs(s) STMT_START { EXTEND(sp,1); PUSHs(s); } STMT_END #define XPUSHTARG STMT_START { SvSETMAGIC(TARG); XPUSHs(TARG); } STMT_END #define XPUSHp(p,l) STMT_START { sv_setpvn(TARG, (p), (l)); XPUSHTARG; } STMT_END #define XPUSHpvs(s) XPUSHp("" s "", sizeof(s)-1) @@ -574,13 +635,14 @@ Does not use C. See also C>, C> and C>. #define MAXARG (PL_op->op_private & OPpARG4_MASK) +/* for backcompat - use switch_argstack() instead */ + #define SWITCHSTACK(f,t) \ - STMT_START { \ - AvFILLp(f) = sp - PL_stack_base; \ - PL_stack_base = AvARRAY(t); \ - PL_stack_max = PL_stack_base + AvMAX(t); \ - sp = PL_stack_sp = PL_stack_base + AvFILLp(t); \ - PL_curstack = t; \ + STMT_START { \ + PL_curstack = f; \ + PL_stack_sp = sp; \ + switch_argstack(t); \ + sp = PL_stack_sp; \ } STMT_END #define EXTEND_MORTAL(n) \ diff --git a/pp_ctl.c b/pp_ctl.c index de5c27cff57d..555cbf181b03 100644 --- a/pp_ctl.c +++ b/pp_ctl.c @@ -42,7 +42,7 @@ #define dopoptosub(plop) dopoptosub_at(cxstack, (plop)) -PP(pp_wantarray) +PP_wrapped(pp_wantarray, 0, 0) { dSP; I32 cxix; @@ -77,7 +77,6 @@ PP(pp_regcreset) PP(pp_regcomp) { - dSP; PMOP *pm = cPMOPx(cLOGOP->op_other); SV **args; int nargs; @@ -88,20 +87,18 @@ PP(pp_regcomp) if (PL_op->op_flags & OPf_STACKED) { dMARK; - nargs = SP - MARK; + nargs = PL_stack_sp - MARK; args = ++MARK; } else { nargs = 1; - args = SP; + args = PL_stack_sp; } /* prevent recompiling under /o and ithreads. */ #if defined(USE_ITHREADS) - if (pm->op_pmflags & PMf_KEEP && PM_GETRE(pm)) { - SP = args-1; - RETURN; - } + if (pm->op_pmflags & PMf_KEEP && PM_GETRE(pm)) + goto finish; #endif re = PM_GETRE(pm); @@ -180,12 +177,27 @@ PP(pp_regcomp) } #endif - SP = args-1; - RETURN; +#if defined(USE_ITHREADS) + finish: +#endif + rpp_popfree_to(args - 1); + return NORMAL; } -PP(pp_substcont) +/* how many stack arguments a substcont op expects */ +#ifdef PERL_RC_STACK +STATIC I32 +S_substcont_argcount(pTHX) +{ + PERL_CONTEXT *cx = CX_CUR(); + /* the scalar result of the expression in s//expression/ is on the + * stack only on iterations 2+ */ + return cx->sb_iters ? 1 : 0; +} +#endif + +PP_wrapped(pp_substcont, S_substcont_argcount(aTHX), 0) { dSP; PERL_CONTEXT *cx = CX_CUR(); @@ -370,6 +382,7 @@ PP(pp_substcont) RETURNOP(pm->op_pmstashstartu.op_pmreplstart); } + void Perl_rxres_save(pTHX_ void **rsp, REGEXP *rx) { @@ -477,7 +490,7 @@ S_rxres_free(pTHX_ void **rsp) #define FORM_NUM_BLANK (1<<30) #define FORM_NUM_POINT (1<<29) -PP(pp_formline) +PP_wrapped(pp_formline, 0, 1) { dSP; dMARK; dORIGMARK; SV * const tmpForm = *++MARK; @@ -968,18 +981,24 @@ PP(pp_formline) /* also used for: pp_mapstart() */ PP(pp_grepstart) { - dSP; + /* See the code comments at the start of pp_grepwhile() and + * pp_mapwhile() for an explanation of how the stack is used + * during a grep or map. + */ SV *src; + SV **svp; - if (PL_stack_base + TOPMARK == SP) { + if (PL_stack_base + TOPMARK == PL_stack_sp) { (void)POPMARK; - if (GIMME_V == G_SCALAR) - XPUSHs(&PL_sv_zero); - RETURNOP(PL_op->op_next->op_next); + if (GIMME_V == G_SCALAR) { + rpp_extend(1); + *++PL_stack_sp = &PL_sv_zero; + } + return PL_op->op_next->op_next; } - PL_stack_sp = PL_stack_base + TOPMARK + 1; - Perl_pp_pushmark(aTHX); /* push dst */ - Perl_pp_pushmark(aTHX); /* push src */ + svp = PL_stack_base + TOPMARK + 1; + PUSHMARK(svp); /* push dst */ + PUSHMARK(svp); /* push src */ ENTER_with_name("grep"); /* enter outer scope */ SAVETMPS; @@ -989,15 +1008,20 @@ PP(pp_grepstart) src = PL_stack_base[TOPMARK]; if (SvPADTMP(src)) { - src = PL_stack_base[TOPMARK] = sv_mortalcopy(src); + SV *newsrc = sv_mortalcopy(src); PL_tmps_floor++; + PL_stack_base[TOPMARK] = newsrc; +#ifdef PERL_RC_STACK + SvREFCNT_inc_simple_void_NN(newsrc); + SvREFCNT_dec(src); +#endif + src = newsrc; } SvTEMP_off(src); DEFSV_set(src); - PUTBACK; if (PL_op->op_type == OP_MAPSTART) - Perl_pp_pushmark(aTHX); /* push top */ + PUSHMARK(PL_stack_sp); /* push top */ return cLOGOPx(PL_op->op_next)->op_other; } @@ -1005,14 +1029,81 @@ PP(pp_grepstart) PP(pp_mapwhile) { - dSP; + /* Understanding the stack during a map. + * + * 'map expr, args' is implemented in the form of + * + * grepstart; // which handles map too + * do { + * expr; + * mapwhile; + * } while (args); + * + * The stack examples below are in the form of 'perl -Ds' output, + * where any stack element indexed by PL_markstack_ptr[i] has a star + * just to the right of it. In addition, the corresponding i value + * is displayed under the indexed stack element. + * + * On entry to mapwhile, the stack looks like this: + * + * => * A1..An X1 * X2..Xn C * R1..Rn * E1..En + * [-3] [-2] [-1] [0] + * + * where: + * A1..An Accumulated results from all previous iterations of expr + * X1..Xn Random garbage + * C The current (just processed) arg, still aliased to $_. + * R1..Rn The args remaining to be processed. + * E1..En the (list) result of the just-executed map expression. + * + * Note that it is easiest to think of stack marks [-1] and [-2] as both + * being one too high, and so it would make more sense to have had the + * marks like this: + * + * => * A1..An * X1..Xn * C R1..Rn * E1..En + * [-3] [-2] [-1] [0] + * + * where the stack is divided neatly into 4 groups: + * - accumulated results + * - discards and/or holes proactively created for later result storage + * - being, or yet to be, processed, + * - results of last expr + * But off-by-one is the way it is currently, and it works as long as + * we keep it consistent and bear it in mind. + * + * pp_mapwhile() does the following: + * + * - If there isn't enough space in the X1..Xn zone to insert the + * expression results, grow the stack and shift up everything above C. + * - move E1..En to just above An + * - at the same time, manipulate the tmps stack so that temporaries + * from executing expr can be freed without prematurely freeing + * E1..En. + * - if on last iteration, pop all the marks, reset the stack pointer + * and update the return args based on caller context. + * - else alias $_ to the next arg. + * + */ + const U8 gimme = GIMME_V; - I32 items = (SP - PL_stack_base) - TOPMARK; /* how many new items */ + I32 items = (PL_stack_sp - PL_stack_base) - TOPMARK; /* how many new items */ I32 count; I32 shift; SV** src; SV** dst; +#ifdef PERL_RC_STACK + /* for ref-counted stack, we need to account for the currently-aliased + * stack element, as it might (or might not) get over-written when + * copying values from the expr to the end of the accumulated results + * section of the list. By RC--ing and zeroing out the stack entry, we + * ensure consistent handling. + */ + dst = PL_stack_base + PL_markstack_ptr[-1]; + SvREFCNT_dec_NN(*dst); + *dst = NULL; +#endif + /* first, move source pointer to the next item in the source list */ ++PL_markstack_ptr[-1]; @@ -1031,7 +1122,7 @@ PP(pp_mapwhile) shift = items - (PL_markstack_ptr[-1] - PL_markstack_ptr[-2]); /* items to shift up (accounting for the moved source pointer) */ - count = (SP - PL_stack_base) - (PL_markstack_ptr[-1] - 1); + count = (PL_stack_sp - PL_stack_base) - (PL_markstack_ptr[-1] - 1); /* This optimization is by Ben Tilly and it does * things differently from what Sarathy (gsar) @@ -1044,16 +1135,25 @@ PP(pp_mapwhile) if (shift < count) shift = count; /* Avoid shifting too often --Ben Tilly */ - EXTEND(SP,shift); - src = SP; - dst = (SP += shift); + rpp_extend(shift); + src = PL_stack_sp; + PL_stack_sp += shift; + dst = PL_stack_sp; PL_markstack_ptr[-1] += shift; *PL_markstack_ptr += shift; while (count--) *dst-- = *src--; +#ifdef PERL_RC_STACK + /* zero out the hole just created, so that on a + * reference-counted stack, so that the just-shifted SVs + * aren't counted twice. + */ + Zero(src+1, (dst-src), SV*); +#endif } /* copy the new items down to the destination list */ - dst = PL_stack_base + (PL_markstack_ptr[-2] += items) - 1; + PL_markstack_ptr[-2] += items; + dst = PL_stack_base + PL_markstack_ptr[-2] - 1; if (gimme == G_LIST) { /* add returned items to the collection (making mortal copies * if necessary), then clear the current temps stack frame @@ -1077,10 +1177,28 @@ PP(pp_mapwhile) PL_tmps_ix += items; while (i-- > 0) { - SV *sv = POPs; +#ifdef PERL_RC_STACK + SV *sv = *PL_stack_sp; + assert(!*dst); /* not overwriting ptrs to refcnted SVs */ + if (!SvTEMP(sv)) { + sv = sv_mortalcopy(sv); + /* NB - don't really need the mortalising above. + * A simple copy would suffice */ + *dst-- = sv; + SvREFCNT_inc_simple_void_NN(sv); + rpp_popfree_1(); + } + else { + *dst-- = sv; + PL_stack_sp--; + } + +#else + SV *sv = *PL_stack_sp--; if (!SvTEMP(sv)) sv = sv_mortalcopy(sv); *dst-- = sv; +#endif PL_tmps_stack[tmpsbase++] = SvREFCNT_inc_simple(sv); } /* clear the stack frame except for the items */ @@ -1095,14 +1213,16 @@ PP(pp_mapwhile) /* scalar context: we don't care about which values map returns * (we use undef here). And so we certainly don't want to do mortal * copies of meaningless values. */ - while (items-- > 0) { - (void)POPs; - *dst-- = &PL_sv_undef; - } + *(dst - items + 1) = &PL_sv_undef; + rpp_popfree_to(PL_stack_sp - items); FREETMPS; } } else { + if (items) { + assert(gimme == G_VOID); + rpp_popfree_to(PL_stack_sp - items); + } FREETMPS; } LEAVE_with_name("grep_item"); /* exit inner scope */ @@ -1115,14 +1235,17 @@ PP(pp_mapwhile) (void)POPMARK; /* pop src */ items = --*PL_markstack_ptr - PL_markstack_ptr[-1]; (void)POPMARK; /* pop dst */ - SP = PL_stack_base + POPMARK; /* pop original mark */ + SV **svp = PL_stack_base + POPMARK; /* pop original mark */ + if (gimme == G_LIST) + svp += items; + rpp_popfree_to(svp); if (gimme == G_SCALAR) { - dTARGET; - XPUSHi(items); + dTARGET; + TARGi(items, 1); + /* XXX is the extend necessary? */ + rpp_xpush_1(targ); } - else if (gimme == G_LIST) - SP += items; - RETURN; + return NORMAL; } else { SV *src; @@ -1132,13 +1255,22 @@ PP(pp_mapwhile) /* set $_ to the new source item */ src = PL_stack_base[PL_markstack_ptr[-1]]; + if (SvPADTMP(src)) { + SV *newsrc = sv_mortalcopy(src); + PL_stack_base[PL_markstack_ptr[-1]] = newsrc; +#ifdef PERL_RC_STACK + SvREFCNT_inc_simple_void_NN(newsrc); + SvREFCNT_dec(src); +#endif + src = newsrc; + } if (SvPADTMP(src)) { src = sv_mortalcopy(src); } SvTEMP_off(src); DEFSV_set(src); - RETURNOP(cLOGOP->op_other); + return cLOGOP->op_other; } } @@ -1156,7 +1288,8 @@ PP(pp_range) return NORMAL; } -PP(pp_flip) + +PP_wrapped(pp_flip,((GIMME_V == G_LIST) ? 0 : 1), 0) { dSP; @@ -1199,6 +1332,7 @@ PP(pp_flip) } } + /* This code tries to decide if "$left .. $right" should use the magical string increment, or if the range is numeric. Initially, an exception was made for *any* string beginning with "0" (see @@ -1214,7 +1348,8 @@ PP(pp_flip) && !(*SvPVX_const(left) == '0' && SvCUR(left)>1 ) )) \ && (!SvOK(right) || looks_like_number(right)))) -PP(pp_flop) + +PP_wrapped(pp_flop, (GIMME_V == G_LIST) ? 2 : 1, 0) { dSP; @@ -1308,6 +1443,7 @@ PP(pp_flop) RETURN; } + /* Control. */ static const char * const context_name[] = { @@ -1652,6 +1788,39 @@ Perl_dounwind(pTHX_ I32 cxix) } + +/* Like rpp_popfree_to(), but takes an offset rather than a pointer, + * and frees everything above ix appropriately, *regardless* of the + * refcountedness of the stack. If necessary it removes any split stack. + * Intended for use during exit() and die() and similar. +*/ +void +Perl_rpp_obliterate_stack_to(pTHX_ I32 ix) +{ +#ifdef PERL_RC_STACK + I32 nonrc_base = PL_curstackinfo->si_stack_nonrc_base; + assert(ix >= 0); + assert(ix <= PL_stack_sp - PL_stack_base); + assert(nonrc_base <= PL_stack_sp - PL_stack_base + 1); + + if (nonrc_base && nonrc_base > ix) { + /* abandon any non-refcounted stuff */ + PL_stack_sp = PL_stack_base + nonrc_base - 1; + /* and mark the stack as fully refcounted again */ + PL_curstackinfo->si_stack_nonrc_base = 0; + } + + if (rpp_stack_is_rc()) + rpp_popfree_to(PL_stack_base + ix); + else + PL_stack_sp = PL_stack_base + ix; +#else + PL_stack_sp = PL_stack_base + ix; +#endif + +} + + void Perl_qerror(pTHX_ SV *err) { @@ -1805,12 +1974,12 @@ Perl_die_unwind(pTHX_ SV *msv) && PL_curstackinfo->si_prev) { dounwind(-1); + rpp_obliterate_stack_to(0); POPSTACK; } if (cxix >= 0) { PERL_CONTEXT *cx; - SV **oldsp; U8 gimme; JMPENV *restartjmpenv; OP *restartop; @@ -1821,12 +1990,17 @@ Perl_die_unwind(pTHX_ SV *msv) cx = CX_CUR(); assert(CxTYPE(cx) == CXt_EVAL); + rpp_obliterate_stack_to(cx->blk_oldsp); + /* return false to the caller of eval */ - oldsp = PL_stack_base + cx->blk_oldsp; gimme = cx->blk_gimme; - if (gimme == G_SCALAR) - *++oldsp = &PL_sv_undef; - PL_stack_sp = oldsp; + if (gimme == G_SCALAR) { + rpp_extend(1); + if (rpp_stack_is_rc()) + rpp_push_1(&PL_sv_undef); + else + *++PL_stack_sp = &PL_sv_undef; + } restartjmpenv = cx->blk_eval.cur_top_env; restartop = cx->blk_eval.retop; @@ -1872,7 +2046,7 @@ Perl_die_unwind(pTHX_ SV *msv) NOT_REACHED; /* NOTREACHED */ } -PP(pp_xor) +PP_wrapped(pp_xor, 2, 0) { dSP; dPOPTOPssrl; if (SvTRUE_NN(left) != SvTRUE_NN(right)) @@ -1944,7 +2118,7 @@ Perl_caller_cx(pTHX_ I32 count, const PERL_CONTEXT **dbcxp) return cx; } -PP(pp_caller) +PP_wrapped(pp_caller, MAXARG, 0) { dSP; const PERL_CONTEXT *cx; @@ -2095,7 +2269,8 @@ PP(pp_caller) RETURN; } -PP(pp_reset) + +PP_wrapped(pp_reset, MAXARG, 0) { dSP; const char * tmps; @@ -2117,7 +2292,7 @@ PP(pp_dbstate) { PL_curcop = (COP*)PL_op; TAINT_NOT; /* Each statement is presumed innocent */ - PL_stack_sp = PL_stack_base + CX_CUR()->blk_oldsp; + rpp_popfree_to(PL_stack_base + CX_CUR()->blk_oldsp); FREETMPS; PERL_ASYNC_CHECK(); @@ -2125,7 +2300,6 @@ PP(pp_dbstate) if (PL_op->op_flags & OPf_SPECIAL /* breakpoint */ || PL_DBsingle_iv || PL_DBsignal_iv || PL_DBtrace_iv) { - dSP; PERL_CONTEXT *cx; const U8 gimme = G_LIST; GV * const gv = PL_DBgv; @@ -2145,16 +2319,42 @@ PP(pp_dbstate) ENTER; SAVEI32(PL_debug); PL_debug = 0; + /* I suspect that saving the stack position is no longer + * required. It was added in 5.001 by: + * + * NETaa13155: &DB::DB left trash on the stack. + * From: Thomas Koenig + * Files patched: lib/perl5db.pl pp_ctl.c + * The call by pp_dbstate() to &DB::DB left trash on the + * stack. It now calls DB in list context, and DB returns + * (). + * + * but the details of what bug it fixed are long lost to + * history. SAVESTACK_POS() doesn't work well with stacks + * which may be split into partly reference-counted and partly + * not halves, so skip it and hope it doesn't cause any + * problems. + */ +#ifndef PERL_RC_STACK SAVESTACK_POS(); +#endif SAVETMPS; - PUSHMARK(SP); - (void)(*CvXSUB(cv))(aTHX_ cv); + PUSHMARK(PL_stack_sp); +#ifdef PERL_RC_STACK + Perl_xs_wrap(aTHX_ CvXSUB(cv), cv); +#else + CvXSUB(cv)(aTHX_ cv); +#endif + FREETMPS; LEAVE; return NORMAL; } else { - cx = cx_pushblock(CXt_SUB, gimme, SP, PL_savestack_ix); +#ifdef PERL_RC_STACK + assert(!PL_curstackinfo->si_stack_nonrc_base); +#endif + cx = cx_pushblock(CXt_SUB, gimme, PL_stack_sp, PL_savestack_ix); cx_pushsub(cx, cv, PL_op->op_next, 0); /* OP_DBSTATE's op_private holds hint bits rather than * the lvalue-ish flags seen in OP_ENTERSUB. So cancel @@ -2163,12 +2363,15 @@ PP(pp_dbstate) SAVEI32(PL_debug); PL_debug = 0; + /* see comment above about SAVESTACK_POS */ +#ifndef PERL_RC_STACK SAVESTACK_POS(); +#endif CvDEPTH(cv)++; if (CvDEPTH(cv) >= 2) pad_push(CvPADLIST(cv), CvDEPTH(cv)); PAD_SET_CUR_NOSAVE(CvPADLIST(cv), CvDEPTH(cv)); - RETURNOP(CvSTART(cv)); + return CvSTART(cv); } } else @@ -2202,7 +2405,7 @@ PP(pp_leave) gimme = cx->blk_gimme; if (gimme == G_VOID) - PL_stack_sp = oldsp; + rpp_popfree_to(oldsp); else leave_adjust_stacks(oldsp, oldsp, gimme, PL_op->op_private & OPpLVALUE ? 3 : 1); @@ -2238,7 +2441,7 @@ S_outside_integer(pTHX_ SV *sv) PP(pp_enteriter) { - dSP; dMARK; + dMARK; PERL_CONTEXT *cx; const U8 gimme = GIMME_V; void *itervarp; /* GV or pad slot of the iteration variable */ @@ -2259,7 +2462,7 @@ PP(pp_enteriter) cxflags = CXp_FOR_PAD; } else { - SV * const sv = POPs; + SV * const sv = *PL_stack_sp; itervarp = (void *)sv; if (LIKELY(isGV(sv))) { /* symbol table variable */ itersave = GvSV(sv); @@ -2275,6 +2478,9 @@ PP(pp_enteriter) itersave = NULL; cxflags = CXp_FOR_LVREF; } + /* we transfer ownership of 1 ref count of itervarp from the stack + * to the CX entry, so no SvREFCNT_dec() needed */ + (void)rpp_pop_1_norc(); } /* OPpITER_DEF (implicit $_) should only occur with a GV iter var */ assert((cxflags & CXp_FOR_GV) || !(PL_op->op_private & OPpITER_DEF)); @@ -2291,10 +2497,10 @@ PP(pp_enteriter) /* OPf_STACKED implies either a single array: for(@), with a * single AV on the stack, or a range: for (1..5), with 1 and 5 on * the stack */ - SV *maybe_ary = POPs; + SV *maybe_ary = *PL_stack_sp; if (SvTYPE(maybe_ary) != SVt_PVAV) { /* range */ - dPOPss; + SV* sv = PL_stack_sp[-1]; SV * const right = maybe_ary; if (UNLIKELY(cxflags & CXp_FOR_LVREF)) DIE(aTHX_ "Assigned value is not a reference"); @@ -2307,12 +2513,18 @@ PP(pp_enteriter) DIE(aTHX_ "Range iterator outside integer range"); cx->blk_loop.state_u.lazyiv.cur = SvIV_nomg(sv); cx->blk_loop.state_u.lazyiv.end = SvIV_nomg(right); + rpp_popfree_2(); } else { cx->cx_type |= CXt_LOOP_LAZYSV; cx->blk_loop.state_u.lazysv.cur = newSVsv(sv); cx->blk_loop.state_u.lazysv.end = right; - SvREFCNT_inc_simple_void_NN(right); + + /* we transfer ownership of 1 ref count of right from the + * stack to the CX .end entry, so no SvREFCNT_dec() needed */ + (void)rpp_pop_1_norc(); + + rpp_popfree_1(); /* free the (now copied) start SV */ (void) SvPV_force_nolen(cx->blk_loop.state_u.lazysv.cur); /* This will do the upgrade to SVt_PV, and warn if the value is uninitialised. */ @@ -2329,17 +2541,19 @@ PP(pp_enteriter) /* for (@array) {} */ cx->cx_type |= CXt_LOOP_ARY; cx->blk_loop.state_u.ary.ary = MUTABLE_AV(maybe_ary); - SvREFCNT_inc_simple_void_NN(maybe_ary); + /* we transfer ownership of 1 ref count of the av from the + * stack to the CX .ary entry, so no SvREFCNT_dec() needed */ + (void)rpp_pop_1_norc(); cx->blk_loop.state_u.ary.ix = (PL_op->op_private & OPpITER_REVERSED) ? AvFILL(cx->blk_loop.state_u.ary.ary) + 1 : -1; } - /* EXTEND(SP, 1) not needed in this branch because we just did POPs */ + /* rpp_extend(1) not needed in this branch because we just did POPs */ } else { /* iterating over items on the stack */ cx->cx_type |= CXt_LOOP_LIST; - cx->blk_oldsp = SP - PL_stack_base; + cx->blk_oldsp = PL_stack_sp - PL_stack_base; cx->blk_loop.state_u.stack.basesp = MARK - PL_stack_base; cx->blk_loop.state_u.stack.ix = (PL_op->op_private & OPpITER_REVERSED) @@ -2347,10 +2561,10 @@ PP(pp_enteriter) : cx->blk_loop.state_u.stack.basesp; /* pre-extend stack so pp_iter doesn't have to check every time * it pushes yes/no */ - EXTEND(SP, 1); + rpp_extend(1); } - RETURN; + return NORMAL; } PP(pp_enterloop) @@ -2380,7 +2594,7 @@ PP(pp_leaveloop) gimme = cx->blk_gimme; if (gimme == G_VOID) - PL_stack_sp = base; + rpp_popfree_to(base); else leave_adjust_stacks(oldsp, base, gimme, PL_op->op_private & OPpLVALUE ? 3 : 1); @@ -2425,7 +2639,7 @@ PP(pp_leavesublv) oldsp = PL_stack_base + cx->blk_oldsp; /* last arg of previous frame */ if (gimme == G_VOID) - PL_stack_sp = oldsp; + rpp_popfree_to(oldsp); else { U8 lval = CxLVAL(cx); bool is_lval = (lval && !(lval & OPpENTERSUB_INARGS)); @@ -2457,12 +2671,11 @@ PP(pp_leavesublv) if (lval & OPpDEREF) { /* lval_sub()->{...} and similar */ - dSP; - SvGETMAGIC(TOPs); - if (!SvOK(TOPs)) { - TOPs = vivify_ref(TOPs, CxLVAL(cx) & OPpDEREF); + SvGETMAGIC(*PL_stack_sp); + if (!SvOK(*PL_stack_sp)) { + SV *sv = vivify_ref(*PL_stack_sp, CxLVAL(cx) & OPpDEREF); + rpp_replace_1_1(sv); } - PUTBACK; } } else { @@ -2511,7 +2724,7 @@ static const char *S_defer_blockname(PERL_CONTEXT *cx) PP(pp_return) { - dSP; dMARK; + dMARK; PERL_CONTEXT *cx; I32 cxix = dopopto_cursub(); @@ -2544,12 +2757,16 @@ PP(pp_return) /* See comment below about context popping. Since we know * we're scalar and not lvalue, we can preserve the return * value in a simpler fashion than there. */ - SV *sv = *SP; + SV *sv = *PL_stack_sp; assert(cxstack[0].blk_gimme == G_SCALAR); - if ( (sp != PL_stack_base) + if ( (PL_stack_sp != PL_stack_base) && !(SvFLAGS(sv) & (SVs_TEMP|SVs_PADTMP)) ) - *SP = sv_mortalcopy(sv); +#ifdef PERL_RC_STACK + rpp_replace_1_1(newSVsv(sv)); +#else + *PL_stack_sp = sv_mortalcopy(sv); +#endif dounwind(0); } /* caller responsible for popping cxstack[0] */ @@ -2571,13 +2788,11 @@ PP(pp_return) * isn't as inefficient as it sounds. */ cx = &cxstack[cxix]; - PUTBACK; if (cx->blk_gimme != G_VOID) leave_adjust_stacks(MARK, PL_stack_base + cx->blk_oldsp, cx->blk_gimme, CxTYPE(cx) == CXt_SUB && CvLVALUE(cx->blk_sub.cv) ? 3 : 0); - SPAGAIN; dounwind(cxix); cx = &cxstack[cxix]; /* CX stack may have been realloced */ } @@ -2591,23 +2806,33 @@ PP(pp_return) * context we can leave as-is (pp_leavesub will later return the * top stack element). But for an empty arg list, e.g. * for (1,2) { return } - * we need to set sp = oldsp so that pp_leavesub knows to push - * &PL_sv_undef onto the stack. + * we need to set PL_stack_sp = oldsp so that pp_leavesub knows to + * push &PL_sv_undef onto the stack. */ SV **oldsp; cx = &cxstack[cxix]; oldsp = PL_stack_base + cx->blk_oldsp; if (oldsp != MARK) { - SSize_t nargs = SP - MARK; + SSize_t nargs = PL_stack_sp - MARK; if (nargs) { if (cx->blk_gimme == G_LIST) { /* shift return args to base of call stack frame */ +#ifdef PERL_RC_STACK + /* free the items on the stack that will get + * overwritten */ + SV **p; + for (p = MARK; p > oldsp; p--) { + SV *sv = *p; + *p = NULL; + SvREFCNT_dec(sv); + } +#endif Move(MARK + 1, oldsp + 1, nargs, SV*); PL_stack_sp = oldsp + nargs; } } else - PL_stack_sp = oldsp; + rpp_popfree_to(oldsp); } } @@ -2642,29 +2867,38 @@ S_unwind_loop(pTHX) OP_NAME(PL_op)); } else { - dSP; STRLEN label_len; - const char * const label = - PL_op->op_flags & OPf_STACKED - ? SvPV(TOPs,label_len) - : (label_len = strlen(cPVOP->op_pv), cPVOP->op_pv); - const U32 label_flags = - PL_op->op_flags & OPf_STACKED - ? SvUTF8(POPs) - : (cPVOP->op_private & OPpPV_IS_UTF8) ? SVf_UTF8 : 0; - PUTBACK; + const char * label; + U32 label_flags; + SV *sv; + + if (PL_op->op_flags & OPf_STACKED) { + sv = *PL_stack_sp; + label = SvPV(sv, label_len); + label_flags = SvUTF8(sv); + } + else { + sv = NULL; /* not needed, but shuts up compiler warn */ + label = cPVOP->op_pv; + label_len = strlen(label); + label_flags = (cPVOP->op_private & OPpPV_IS_UTF8) ? SVf_UTF8 : 0; + } + cxix = dopoptolabel(label, label_len, label_flags); if (cxix < 0) /* diag_listed_as: Label not found for "last %s" */ Perl_croak(aTHX_ "Label not found for \"%s %" SVf "\"", OP_NAME(PL_op), SVfARG(PL_op->op_flags & OPf_STACKED - && !SvGMAGICAL(TOPp1s) - ? TOPp1s + && !SvGMAGICAL(sv) + ? sv : newSVpvn_flags(label, label_len, label_flags | SVs_TEMP))); + if (PL_op->op_flags & OPf_STACKED) + rpp_popfree_1(); } + if (cxix < cxstack_ix) { I32 i; /* Check for defer { last ... } etc */ @@ -2689,11 +2923,11 @@ PP(pp_last) cx = S_unwind_loop(aTHX); assert(CxTYPE_is_LOOP(cx)); - PL_stack_sp = PL_stack_base + rpp_popfree_to(PL_stack_base + (CxTYPE(cx) == CXt_LOOP_LIST ? cx->blk_loop.state_u.stack.basesp : cx->blk_oldsp - ); + )); TAINT_NOT; @@ -2862,7 +3096,6 @@ S_check_op_type(pTHX_ OP * const o) PP(pp_goto) { - dSP; OP *retop = NULL; I32 ix; PERL_CONTEXT *cx; @@ -2876,7 +3109,7 @@ PP(pp_goto) if (PL_op->op_flags & OPf_STACKED) { /* goto EXPR or goto &foo */ - SV * const sv = POPs; + SV * const sv = *PL_stack_sp; SvGETMAGIC(sv); if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVCV) { @@ -2935,13 +3168,14 @@ PP(pp_goto) /* First do some returnish stuff. */ SvREFCNT_inc_simple_void(cv); /* avoid premature free during unwind */ + rpp_popfree_1(); /* safe to free original sv now */ + FREETMPS; if (cxix < cxstack_ix) { dounwind(cxix); } cx = CX_CUR(); cx_topblock(cx); - SPAGAIN; /* protect @_ during save stack unwind. */ if (arg) @@ -2963,9 +3197,15 @@ PP(pp_goto) * unless pad[0] and @_ differ (e.g. if the old sub did * local *_ = []); in which case clear the old pad[0] * array in the usual way */ - if (av == arg || AvREAL(av)) + + if (av != arg && !SvMAGICAL(av) && SvREFCNT(av) == 1 +#ifndef PERL_RC_STACK + && !AvREAL(av) +#endif + ) + clear_defarray_simple(av); + else clear_defarray(av, av == arg); - else CLEAR_ARGARRAY(av); } /* don't restore PL_comppad here. It won't be needed if the @@ -3012,13 +3252,16 @@ PP(pp_goto) SAVEFREESV(old_cv); /* ditto, deferred freeing of old CV */ /* put GvAV(defgv) back onto stack */ - if (items) { - EXTEND(SP, items+1); /* @_ could have been extended. */ - } - mark = SP; + if (items) + rpp_extend(items + 1); /* @_ could have been extended. */ + mark = PL_stack_sp; if (items) { SSize_t index; +#ifdef PERL_RC_STACK + assert(AvREAL(arg)); +#else bool r = cBOOL(AvREAL(arg)); +#endif for (index=0; indexblk_oldcop; PAD_SET_CUR_NOSAVE(padlist, CvDEPTH(cv)); + if (CxHASARGS(cx)) { /* second half of donating @_ from the old sub to the @@ -3124,11 +3381,15 @@ PP(pp_goto) } } retop = CvSTART(cv); - goto putback_return; + goto finish; } } else { /* goto EXPR */ + /* avoid premature free of label before popping it off stack */ + SvREFCNT_inc_NN(sv); + sv_2mortal(sv); + rpp_popfree_1(); label = SvPV_nomg_const(sv, label_len); label_flags = SvUTF8(sv); } @@ -3289,14 +3550,12 @@ PP(pp_goto) PL_do_undump = FALSE; } - putback_return: - PL_stack_sp = sp; - _return: + finish: PERL_ASYNC_CHECK(); return retop; } -PP(pp_exit) +PP_wrapped(pp_exit, 1, 0) { dSP; I32 anum; @@ -3662,7 +3921,6 @@ S_try_run_unitcheck(pTHX_ OP* caller_op) STATIC bool S_doeval_compile(pTHX_ U8 gimme, CV* outside, U32 seq, HV *hh) { - dSP; OP * const saveop = PL_op; bool clear_hints = saveop->op_type != OP_ENTEREVAL; COP * const oldcurcop = PL_curcop; @@ -3676,7 +3934,7 @@ S_doeval_compile(pTHX_ U8 gimme, CV* outside, U32 seq, HV *hh) ((PL_op->op_private & OPpEVAL_RE_REPARSING) ? EVAL_RE_REPARSING : 0))); - PUSHMARK(SP); + PUSHMARK(PL_stack_sp); evalcv = MUTABLE_CV(newSV_type(SVt_PVCV)); CvEVAL_on(evalcv); @@ -3808,11 +4066,12 @@ S_doeval_compile(pTHX_ U8 gimme, CV* outside, U32 seq, HV *hh) op_free(PL_eval_root); PL_eval_root = NULL; } - SP = PL_stack_base + POPMARK; /* pop original mark */ + rpp_popfree_to(PL_stack_base + POPMARK); /* pop original mark */ cx = CX_CUR(); assert(CxTYPE(cx) == CXt_EVAL); /* pop the CXt_EVAL, and if was a require, croak */ S_pop_eval_context_maybe_croak(aTHX_ cx, ERRSV, 2); + } /* die_unwind() re-croaks when in require, having popped the @@ -3824,8 +4083,15 @@ S_doeval_compile(pTHX_ U8 gimme, CV* outside, U32 seq, HV *hh) if (!*(SvPV_nolen_const(errsv))) sv_setpvs(errsv, "Compilation error"); - if (gimme != G_LIST) PUSHs(&PL_sv_undef); - PUTBACK; + if (gimme == G_SCALAR) { + if (yystatus == 3) { + /* die_unwind already pushed undef in scalar context */ + assert(*PL_stack_sp == &PL_sv_undef); + } + else { + rpp_xpush_1(&PL_sv_undef); + } + } return FALSE; } @@ -3843,10 +4109,8 @@ S_doeval_compile(pTHX_ U8 gimme, CV* outside, U32 seq, HV *hh) if (PERLDB_INTER && saveop->op_type == OP_REQUIRE) { CV * const cv = get_cvs("DB::postponed", 0); if (cv) { - dSP; - PUSHMARK(SP); - XPUSHs(MUTABLE_SV(CopFILEGV(&PL_compiling))); - PUTBACK; + PUSHMARK(PL_stack_sp); + rpp_xpush_1(MUTABLE_SV(CopFILEGV(&PL_compiling))); call_sv(MUTABLE_SV(cv), G_DISCARD); } } @@ -3872,22 +4136,22 @@ S_doeval_compile(pTHX_ U8 gimme, CV* outside, U32 seq, HV *hh) sv_setpvs(errsv, "Unit check error"); } - if (gimme != G_LIST) PUSHs(&PL_sv_undef); - PUTBACK; + if (gimme != G_LIST) + rpp_xpush_1(&PL_sv_undef); return FALSE; } PL_eval_start = es; } CvDEPTH(evalcv) = 1; - SP = PL_stack_base + POPMARK; /* pop original mark */ + rpp_popfree_to(PL_stack_base + POPMARK); /* pop original mark */ PL_op = saveop; /* The caller may need it. */ PL_parser->lex_state = LEX_NOTPARSING; /* $^S needs this. */ - PUTBACK; return TRUE; } + /* Return NULL if the file doesn't exist or isn't a file; * else return PerlIO_openn(). */ @@ -4030,9 +4294,9 @@ S_path_is_searchable(const char *name) static OP * S_require_version(pTHX_ SV *sv) { - dSP; - sv = sv_2mortal(new_version(sv)); + rpp_popfree_1(); + if (!Perl_sv_derived_from_pvn(aTHX_ PL_patchlevel, STR_WITH_LEN("version"), 0)) upg_version(PL_patchlevel, TRUE); if (cUNOP->op_first->op_type == OP_CONST && cUNOP->op_first->op_private & OPpCONST_NOVER) { @@ -4086,18 +4350,19 @@ S_require_version(pTHX_ SV *sv) } } - RETPUSHYES; + *++PL_stack_sp = &PL_sv_yes; + return NORMAL; } + /* Handle C, C and C. * The first form will have already been converted at compile time to - * the second form */ + * the second form. + * sv is still on the stack at this point. */ static OP * S_require_file(pTHX_ SV *sv) { - dSP; - PERL_CONTEXT *cx; const char *name; STRLEN len; @@ -4139,14 +4404,20 @@ S_require_file(pTHX_ SV *sv) if (op_is_require) { /* can optimize to only perform one single lookup */ svp_cached = hv_fetch(GvHVn(PL_incgv), (char*) name, len, 0); - if ( svp_cached && (SvGETMAGIC(*svp_cached), SvOK(*svp_cached)) ) RETPUSHYES; + if (svp_cached && + (SvGETMAGIC(*svp_cached), SvOK(*svp_cached))) + { + rpp_replace_1_1(&PL_sv_yes); + return NORMAL; + } } #endif if (!IS_SAFE_PATHNAME(name, len, op_name)) { if (!op_is_require) { CLEAR_ERRSV(); - RETPUSHUNDEF; + rpp_replace_1_1(&PL_sv_undef); + return NORMAL; } DIE(aTHX_ "Can't locate %s: %s", pv_escape(newSVpvs_flags("",SVs_TEMP),name,len,len*2, @@ -4187,8 +4458,10 @@ S_require_file(pTHX_ SV *sv) /* we already did a get magic if this was cached */ if (!svp_cached) SvGETMAGIC(*svp); - if (SvOK(*svp)) - RETPUSHYES; + if (SvOK(*svp)) { + rpp_replace_1_1(&PL_sv_yes); + return NORMAL; + } else DIE(aTHX_ "Attempt to reload %s aborted.\n" "Compilation failed in require", unixname); @@ -4386,25 +4659,27 @@ S_require_file(pTHX_ SV *sv) ENTER_with_name("call_INC_hook"); SAVETMPS; - EXTEND(SP, 2 + ((method && (loader != dirsv)) ? 1 : 0)); - PUSHMARK(SP); - PUSHs(method ? loader : dirsv); /* always use the object for method calls */ - PUSHs(nsv); - if (method && (loader != dirsv)) /* add the args array for method calls */ - PUSHs(dirsv); - PUTBACK; + PUSHMARK(PL_stack_sp); + /* add the args array for method calls */ + bool add_dirsv = (method && (loader != dirsv)); + rpp_extend(2 + add_dirsv); + rpp_push_2( + /* always use the object for method calls */ + method ? loader : dirsv, + nsv + ); + if (add_dirsv) + rpp_push_1(dirsv); if (method) { count = call_method(method, G_LIST|G_EVAL); } else { count = call_sv(loader, G_LIST|G_EVAL); } - SPAGAIN; if (count > 0) { int i = 0; SV *arg; - - SP -= count - 1; + SV **base = PL_stack_sp - count + 1; if (is_incdir) { /* push the stringified returned items into the @@ -4415,7 +4690,7 @@ S_require_file(pTHX_ SV *sv) * stable. We speficially do *not* support returning * coderefs from an INCDIR call. */ while (count-->0) { - arg = SP[i++]; + arg = base[i++]; SvGETMAGIC(arg); if (!SvOK(arg)) continue; @@ -4447,14 +4722,14 @@ S_require_file(pTHX_ SV *sv) goto done_hook; } - arg = SP[i++]; + arg = base[i++]; if (SvROK(arg) && (SvTYPE(SvRV(arg)) <= SVt_PVLV) && !isGV_with_GP(SvRV(arg))) { filter_cache = SvRV(arg); if (i < count) { - arg = SP[i++]; + arg = base[i++]; } } @@ -4477,7 +4752,7 @@ S_require_file(pTHX_ SV *sv) } if (i < count) { - arg = SP[i++]; + arg = base[i++]; } } @@ -4486,7 +4761,7 @@ S_require_file(pTHX_ SV *sv) SvREFCNT_inc_simple_void_NN(filter_sub); if (i < count) { - filter_state = SP[i]; + filter_state = base[i]; SvREFCNT_inc_simple_void(filter_state); } } @@ -4495,8 +4770,8 @@ S_require_file(pTHX_ SV *sv) tryrsfp = PerlIO_open(BIT_BUCKET, PERL_SCRIPT_MODE); } - done_hook: - SP--; + done_hook: + rpp_popfree_to(base - 1); } else { SV *errsv= ERRSV; if (SvTRUE(errsv) && !SvROK(errsv)) { @@ -4541,7 +4816,6 @@ S_require_file(pTHX_ SV *sv) inc_idx_sv = GvSVn(PL_incgv); inc_idx = SvOK(inc_idx_sv) ? SvIV(inc_idx_sv) : -1; - PUTBACK; FREETMPS; LEAVE_with_name("call_INC_hook"); @@ -4770,12 +5044,15 @@ S_require_file(pTHX_ SV *sv) } #endif CLEAR_ERRSV(); - RETPUSHUNDEF; + rpp_replace_1_1(&PL_sv_undef); + return NORMAL; } } else SETERRNO(0, SS_NORMAL); + rpp_popfree_1(); /* finished with sv now */ + /* Update %INC. Assume success here to prevent recursive requirement. */ /* name is never assigned to again, so len is still strlen(name) */ /* Check whether a hook in @INC has already filled %INC */ @@ -4815,14 +5092,12 @@ S_require_file(pTHX_ SV *sv) /* switch to eval mode */ assert(!CATCH_GET); - cx = cx_pushblock(CXt_EVAL, gimme, SP, old_savestack_ix); + cx = cx_pushblock(CXt_EVAL, gimme, PL_stack_sp, old_savestack_ix); cx_pusheval(cx, PL_op->op_next, newSVpv(name, 0)); SAVECOPLINE(&PL_compiling); CopLINE_set(&PL_compiling, 0); - PUTBACK; - if (doeval_compile(gimme, NULL, PL_curcop->cop_seq, NULL)) op = PL_eval_start; else @@ -4849,10 +5124,10 @@ PP(pp_require) return docatch(Perl_pp_require); { - dSP; - SV *sv = POPs; + SV *sv = *PL_stack_sp; SvGETMAGIC(sv); - PUTBACK; + /* these tail-called subs are responsible for popping sv off the + * stack */ return ((SvNIOKp(sv) || SvVOK(sv)) && PL_op->op_type != OP_DOFILE) ? S_require_version(aTHX_ sv) : S_require_file(aTHX_ sv); @@ -4864,7 +5139,7 @@ PP(pp_require) pp_entereval. The hash can be modified by the code being eval'ed, so we return a copy instead. */ -PP(pp_hintseval) +PP_wrapped(pp_hintseval, 0, 0) { dSP; mXPUSHs(MUTABLE_SV(hv_copy_hints_hv(MUTABLE_HV(cSVOP_sv)))); @@ -4874,7 +5149,6 @@ PP(pp_hintseval) PP(pp_entereval) { - dSP; PERL_CONTEXT *cx; SV *sv; U8 gimme; @@ -4910,7 +5184,7 @@ PP(pp_entereval) bytes = PL_op->op_private & OPpEVAL_BYTES; if (PL_op->op_private & OPpEVAL_HAS_HH) { - saved_hh = MUTABLE_HV(SvREFCNT_inc(POPs)); + saved_hh = MUTABLE_HV(rpp_pop_1_norc()); } else if (PL_hints & HINT_LOCALIZE_HH || ( PL_op->op_private & OPpEVAL_COPHH @@ -4919,7 +5193,7 @@ PP(pp_entereval) saved_hh = cop_hints_2hv(PL_curcop, 0); hv_magic(saved_hh, NULL, PERL_MAGIC_hints); } - sv = POPs; + sv = *PL_stack_sp; if (!SvPOK(sv)) { /* make sure we've got a plain PV (no overload etc) before testing * for taint. Making a copy here is probably overkill, but better @@ -4953,6 +5227,8 @@ PP(pp_entereval) ) ); + rpp_popfree_1(); /* can free sv now */ + /* switch to eval mode */ if (PERLDB_NAMEEVAL && CopLINE(PL_curcop)) { @@ -4977,7 +5253,8 @@ PP(pp_entereval) runcv = find_runcv(&seq); assert(!CATCH_GET); - cx = cx_pushblock((CXt_EVAL|CXp_REAL), gimme, SP, old_savestack_ix); + cx = cx_pushblock((CXt_EVAL|CXp_REAL), + gimme, PL_stack_sp, old_savestack_ix); cx_pusheval(cx, PL_op->op_next, NULL); /* prepare to compile string */ @@ -4995,8 +5272,6 @@ PP(pp_entereval) saved_delete = TRUE; } - PUTBACK; - if (doeval_compile(gimme, runcv, seq, saved_hh)) { if (was != PL_breakable_sub_gen /* Some subs defined here. */ ? PERLDB_LINE_OR_SAVESRC @@ -5017,6 +5292,9 @@ PP(pp_entereval) } else if (!saved_delete) { (void)hv_delete(PL_defstash, tmpbuf, len, G_DISCARD); } + if (PL_op->op_private & OPpEVAL_EVALSV) + /* signal compiletime failure to our eval_sv() caller */ + *++PL_stack_sp = NULL; return PL_op->op_next; } } @@ -5099,7 +5377,7 @@ PP(pp_leaveeval) : PL_stack_sp > oldsp); if (gimme == G_VOID) { - PL_stack_sp = oldsp; + rpp_popfree_to(oldsp); /* free now to avoid late-called destructors clobbering $@ */ FREETMPS; } @@ -5223,13 +5501,15 @@ Perl_delete_eval_scope(pTHX) /* Common-ish code salvaged from Perl_call_sv and pp_entertry, because it was also needed by Perl_fold_constants. */ void -Perl_create_eval_scope(pTHX_ OP *retop, U32 flags) +Perl_create_eval_scope(pTHX_ OP *retop, SV **sp, U32 flags) { PERL_CONTEXT *cx; const U8 gimme = GIMME_V; + + PERL_ARGS_ASSERT_CREATE_EVAL_SCOPE; cx = cx_pushblock((CXt_EVAL|CXp_EVALBLOCK), gimme, - PL_stack_sp, PL_savestack_ix); + sp, PL_savestack_ix); cx_pusheval(cx, retop, NULL); PL_in_eval = EVAL_INEVAL; @@ -5258,7 +5538,7 @@ PP(pp_entertry) assert(!CATCH_GET); - create_eval_scope(retop, 0); + create_eval_scope(retop, PL_stack_sp, 0); return PL_op->op_next; } @@ -5281,7 +5561,7 @@ PP(pp_leavetry) gimme = cx->blk_gimme; if (gimme == G_VOID) { - PL_stack_sp = oldsp; + rpp_popfree_to(oldsp); /* free now to avoid late-called destructors clobbering $@ */ FREETMPS; } @@ -5299,19 +5579,17 @@ PP(pp_leavetry) PP(pp_entergiven) { - dSP; PERL_CONTEXT *cx; const U8 gimme = GIMME_V; SV *origsv = DEFSV; - SV *newsv = POPs; assert(!PL_op->op_targ); /* used to be set for lexical $_ */ - GvSV(PL_defgv) = SvREFCNT_inc(newsv); + GvSV(PL_defgv) = rpp_pop_1_norc(); - cx = cx_pushblock(CXt_GIVEN, gimme, SP, PL_savestack_ix); + cx = cx_pushblock(CXt_GIVEN, gimme, PL_stack_sp, PL_savestack_ix); cx_pushgiven(cx, origsv); - RETURN; + return NORMAL; } PP(pp_leavegiven) @@ -5327,7 +5605,7 @@ PP(pp_leavegiven) gimme = cx->blk_gimme; if (gimme == G_VOID) - PL_stack_sp = oldsp; + rpp_popfree_to(oldsp); else leave_adjust_stacks(oldsp, oldsp, gimme, 1); @@ -5385,7 +5663,7 @@ S_destroy_matcher(pTHX_ PMOP *matcher) } /* Do a smart match */ -PP(pp_smartmatch) +PP_wrapped(pp_smartmatch, 2, 0) { DEBUG_M(Perl_deb(aTHX_ "Starting smart match resolution\n")); return do_smartmatch(NULL, NULL, 0); @@ -5873,7 +6151,6 @@ S_do_smartmatch(pTHX_ HV *seen_this, HV *seen_other, const bool copied) PP(pp_enterwhen) { - dSP; PERL_CONTEXT *cx; const U8 gimme = GIMME_V; @@ -5881,18 +6158,21 @@ PP(pp_enterwhen) fails, we don't want to push a context and then pop it again right away, so we skip straight to the op that follows the leavewhen. - RETURNOP calls PUTBACK which restores the stack pointer after the POPs. */ - if (!(PL_op->op_flags & OPf_SPECIAL) && !SvTRUEx(POPs)) { - if (gimme == G_SCALAR) - PUSHs(&PL_sv_undef); - RETURNOP(cLOGOP->op_other->op_next); + if (!(PL_op->op_flags & OPf_SPECIAL)) { /* SPECIAL implies no condition */ + bool tr = SvTRUEx(*PL_stack_sp); + rpp_popfree_1(); + if (!tr) { + if (gimme == G_SCALAR) + *++PL_stack_sp = &PL_sv_undef; + return cLOGOP->op_other->op_next; + } } - cx = cx_pushblock(CXt_WHEN, gimme, SP, PL_savestack_ix); + cx = cx_pushblock(CXt_WHEN, gimme, PL_stack_sp, PL_savestack_ix); cx_pushwhen(cx); - RETURN; + return NORMAL; } PP(pp_leavewhen) @@ -5914,7 +6194,7 @@ PP(pp_leavewhen) oldsp = PL_stack_base + cx->blk_oldsp; if (gimme == G_VOID) - PL_stack_sp = oldsp; + rpp_popfree_to(oldsp); else leave_adjust_stacks(oldsp, oldsp, gimme, 1); @@ -5954,7 +6234,7 @@ PP(pp_continue) cx = CX_CUR(); assert(CxTYPE(cx) == CXt_WHEN); - PL_stack_sp = PL_stack_base + cx->blk_oldsp; + rpp_popfree_to(PL_stack_base + cx->blk_oldsp); CX_LEAVE_SCOPE(cx); cx_popwhen(cx); cx_popblock(cx); @@ -5982,7 +6262,7 @@ PP(pp_break) /* Restore the sp at the time we entered the given block */ cx = CX_CUR(); - PL_stack_sp = PL_stack_base + cx->blk_oldsp; + rpp_popfree_to(PL_stack_base + cx->blk_oldsp); return cx->blk_givwhen.leave_op; } @@ -6013,7 +6293,16 @@ _invoke_defer_block(pTHX_ U8 type, void *_arg) cx = CX_CUR(); assert(CxTYPE(cx) == CXt_DEFER); - PL_stack_sp = PL_stack_base + cx->blk_oldsp; + /* since we're called during a scope cleanup (including after + * a croak), theere's no guarantee thr stack is currently + * ref-counted */ +#ifdef PERL_RC_STACK + if (rpp_stack_is_rc()) + rpp_popfree_to(PL_stack_base + cx->blk_oldsp); + else +#endif + PL_stack_sp = PL_stack_base + cx->blk_oldsp; + CX_LEAVE_SCOPE(cx); cx_popblock(cx); diff --git a/pp_hot.c b/pp_hot.c index 0032937be7a8..8ea482376483 100644 --- a/pp_hot.c +++ b/pp_hot.c @@ -38,7 +38,148 @@ /* Hot code. */ -PP(pp_const) + +#ifdef PERL_RC_STACK + +/* common code for pp_wrap() and xs_wrap(): + * free any original arguments, and bump and shift down any return + * args + */ + +STATIC void +S_pp_xs_wrap_return(pTHX_ I32 nargs, I32 old_sp) +{ + I32 nret = (I32)(PL_stack_sp - PL_stack_base) - old_sp; + assert(nret >= 0); + + /* bump any returned values */ + if (nret) { + SV **svp = PL_stack_sp - nret + 1; + while (svp <= PL_stack_sp) { + SvREFCNT_inc(*svp); + svp++; + } + } + + PL_curstackinfo->si_stack_nonrc_base = 0; + + /* free the original args and shift the returned valued down */ + if (nargs) { + SV **svp = PL_stack_sp - nret; + I32 i = nargs; + while (i--) { + SvREFCNT_dec(*svp); + *svp = NULL; + svp--; + } + + if (nret) { + Move(PL_stack_sp - nret + 1, + PL_stack_sp - nret - nargs + 1, + nret, SV*); + } + PL_stack_sp -= nargs; + } +} + +/* pp_wrap(): + * wrapper function for pp() functions to turn them into functions + * that can operate on a reference-counted stack, by taking a non- + * reference-counted copy of the current stack frame, calling the real + * pp() function, then incrementing the reference count of any returned + * args. + * + * nargs or nlists indicate the number of stack arguments or the + * number of stack lists (delimited by MARKs) which the function expects. + */ +OP* +Perl_pp_wrap(pTHX_ Perl_ppaddr_t real_pp_fn, I32 nargs, int nlists) +{ + PERL_ARGS_ASSERT_PP_WRAP; + + if (!rpp_stack_is_rc()) + /* stack-already non-RC; nothing needing wrapping */ + return real_pp_fn(aTHX); + + OP *next_op; + I32 old_sp = (I32)(PL_stack_sp - PL_stack_base); + + assert(nargs >= 0); + assert(nlists >= 0); + assert(AvREAL(PL_curstack)); + + PL_curstackinfo->si_stack_nonrc_base = PL_stack_sp - PL_stack_base + 1; + + if (nlists) { + assert(nargs == 0); + I32 mark = PL_markstack_ptr[-nlists+1]; + nargs = (PL_stack_sp - PL_stack_base) - mark; + assert(nlists <= 2); /* if ever more, make below a loop */ + PL_markstack_ptr[0] += nargs; + if (nlists == 2) + PL_markstack_ptr[-1] += nargs; + } + + if (nargs) { + /* duplicate all the arg pointers further up the stack */ + rpp_extend(nargs); + Copy(PL_stack_sp - nargs + 1, PL_stack_sp + 1, nargs, SV*); + PL_stack_sp += nargs; + } + + next_op = real_pp_fn(aTHX); + + /* we should still be a split stack */ + assert(AvREAL(PL_curstack)); + assert(PL_curstackinfo->si_stack_nonrc_base); + + S_pp_xs_wrap_return(aTHX_ nargs, old_sp); + + return next_op; +} + + +/* xs_wrap(): + * similar in concept to pp_wrap: make a non-referenced-counted copy of + * a (not refcount aware) XS sub's args, call the XS subs, then bump any + * return values and free the original args */ + +void +Perl_xs_wrap(pTHX_ XSUBADDR_t xsub, CV *cv) +{ + PERL_ARGS_ASSERT_XS_WRAP; + + I32 old_sp = (I32)(PL_stack_sp - PL_stack_base); + I32 mark = PL_markstack_ptr[0]; + I32 nargs = (PL_stack_sp - PL_stack_base) - mark; + + /* we should be a fully refcounted stack */ + assert(AvREAL(PL_curstack)); + assert(!PL_curstackinfo->si_stack_nonrc_base); + + PL_curstackinfo->si_stack_nonrc_base = PL_stack_sp - PL_stack_base + 1; + + + if (nargs) { + /* duplicate all the arg pointers further up the stack */ + rpp_extend(nargs); + Copy(PL_stack_sp - nargs + 1, PL_stack_sp + 1, nargs, SV*); + PL_stack_sp += nargs; + PL_markstack_ptr[0] += nargs; + } + + xsub(aTHX_ cv); + + S_pp_xs_wrap_return(aTHX_ nargs, old_sp); +} + +#endif + + +/* ----------------------------------------------------------- */ + + +PP_wrapped(pp_const, 0, 0) { dSP; XPUSHs(cSVOP_sv); @@ -49,13 +190,13 @@ PP(pp_nextstate) { PL_curcop = (COP*)PL_op; TAINT_NOT; /* Each statement is presumed innocent */ - PL_stack_sp = PL_stack_base + CX_CUR()->blk_oldsp; + rpp_popfree_to(PL_stack_base + CX_CUR()->blk_oldsp); FREETMPS; PERL_ASYNC_CHECK(); return NORMAL; } -PP(pp_gvsv) +PP_wrapped(pp_gvsv, 0, 0) { dSP; assert(SvTYPE(cGVOP_gv) == SVt_PVGV); @@ -83,7 +224,7 @@ PP(pp_pushmark) return NORMAL; } -PP(pp_stringify) +PP_wrapped(pp_stringify, 1, 0) { dSP; dTARGET; SV * const sv = TOPs; @@ -94,7 +235,7 @@ PP(pp_stringify) return NORMAL; } -PP(pp_gv) +PP_wrapped(pp_gv, 0, 0) { dSP; /* cGVOP_gv might be a real GV or might be an RV to a CV */ @@ -107,7 +248,7 @@ PP(pp_gv) /* also used for: pp_andassign() */ -PP(pp_and) +PP_wrapped(pp_and, 2, 0) { PERL_ASYNC_CHECK(); { @@ -135,7 +276,7 @@ PP(pp_and) * (PL_op->op_private & OPpASSIGN_BACKWARDS) {or,and,dor}assign */ -PP(pp_padsv_store) +PP_wrapped(pp_padsv_store,1,0) { dSP; OP * const op = PL_op; @@ -171,7 +312,7 @@ PP(pp_padsv_store) /* A mashup of simplified AELEMFAST_LEX + SASSIGN OPs */ -PP(pp_aelemfastlex_store) +PP_wrapped(pp_aelemfastlex_store, 1, 0) { dSP; OP * const op = PL_op; @@ -219,7 +360,7 @@ PP(pp_aelemfastlex_store) RETURN; } -PP(pp_sassign) +PP_wrapped(pp_sassign, 2, 0) { dSP; /* sassign keeps its args in the optree traditionally backwards. @@ -314,7 +455,7 @@ PP(pp_sassign) } if ( - UNLIKELY(SvTEMP(left)) && !SvSMAGICAL(left) && SvREFCNT(left) == 1 && + rpp_is_lone(left) && !SvSMAGICAL(left) && (!isGV_with_GP(left) || SvFAKE(left)) && ckWARN(WARN_MISC) ) Perl_warner(aTHX_ @@ -325,7 +466,7 @@ PP(pp_sassign) RETURN; } -PP(pp_cond_expr) +PP_wrapped(pp_cond_expr, 1, 0) { dSP; SV *sv; @@ -341,7 +482,7 @@ PP(pp_unstack) PERL_ASYNC_CHECK(); TAINT_NOT; /* Each statement is presumed innocent */ cx = CX_CUR(); - PL_stack_sp = PL_stack_base + cx->blk_oldsp; + rpp_popfree_to(PL_stack_base + CX_CUR()->blk_oldsp); FREETMPS; if (!(PL_op->op_flags & OPf_SPECIAL)) { assert(CxTYPE(cx) == CXt_BLOCK || CxTYPE_is_LOOP(cx)); @@ -421,7 +562,7 @@ S_do_concat(pTHX_ SV *left, SV *right, SV *targ, U8 targmy) } -PP(pp_concat) +PP_wrapped(pp_concat, 2, 0) { dSP; dATARGET; tryAMAGICbin_MG(concat_amg, AMGf_assign); { @@ -492,7 +633,21 @@ have differing overloading behaviour. */ -PP(pp_multiconcat) + +/* how many stack arguments a multiconcat op expects */ +#ifdef PERL_RC_STACK +STATIC I32 +S_multiconcat_argcount(pTHX) +{ + UNOP_AUX_item *aux = cUNOP_AUXx(PL_op)->op_aux; + SSize_t nargs = aux[PERL_MULTICONCAT_IX_NARGS].ssize; + if (PL_op->op_flags & OPf_STACKED) + nargs++; + return nargs; +} +#endif + +PP_wrapped(pp_multiconcat, S_multiconcat_argcount(aTHX), 0) { dSP; SV *targ; /* The SV to be assigned or appended to */ @@ -1253,7 +1408,7 @@ S_pushav(pTHX_ AV* const av) /* ($lex1,@lex2,...) or my ($lex1,@lex2,...) */ -PP(pp_padrange) +PP_wrapped(pp_padrange, 0, 0) { dSP; PADOFFSET base = PL_op->op_targ; @@ -1298,7 +1453,7 @@ PP(pp_padrange) } -PP(pp_padsv) +PP_wrapped(pp_padsv, 0, 0) { dSP; EXTEND(SP, 1); @@ -1328,7 +1483,7 @@ PP(pp_padsv) } } -PP(pp_readline) +PP_wrapped(pp_readline, ((PL_op->op_flags & OPf_STACKED) ? 2 : 1), 0) { dSP; /* pp_coreargs pushes a NULL to indicate no args passed to @@ -1354,7 +1509,7 @@ PP(pp_readline) return do_readline(); } -PP(pp_eq) +PP_wrapped(pp_eq, 2, 0) { dSP; SV *left, *right; @@ -1379,7 +1534,7 @@ PP(pp_eq) /* also used for: pp_i_preinc() */ -PP(pp_preinc) +PP_wrapped(pp_preinc, 1, 0) { SV *sv = *PL_stack_sp; @@ -1400,7 +1555,7 @@ PP(pp_preinc) /* also used for: pp_i_predec() */ -PP(pp_predec) +PP_wrapped(pp_predec, 1, 0) { SV *sv = *PL_stack_sp; @@ -1421,7 +1576,7 @@ PP(pp_predec) /* also used for: pp_orassign() */ -PP(pp_or) +PP_wrapped(pp_or, 1, 0) { dSP; SV *sv; @@ -1439,7 +1594,7 @@ PP(pp_or) /* also used for: pp_dor() pp_dorassign() */ -PP(pp_defined) +PP_wrapped(pp_defined, 1, 0) { dSP; SV* sv = TOPs; @@ -1497,11 +1652,16 @@ PP(pp_defined) PP(pp_add) { - dSP; dATARGET; bool useleft; SV *svl, *svr; + bool useleft; SV *svl, *svr; + SV *targ = (PL_op->op_flags & OPf_STACKED) + ? PL_stack_sp[-1] + : PAD_SV(PL_op->op_targ); - tryAMAGICbin_MG(add_amg, AMGf_assign|AMGf_numeric); - svr = TOPs; - svl = TOPm1s; + if (rpp_try_AMAGIC_2(add_amg, AMGf_assign|AMGf_numeric)) + return NORMAL; + + svr = PL_stack_sp[0]; + svl = PL_stack_sp[-1]; #ifdef PERL_PRESERVE_IVUV @@ -1522,10 +1682,9 @@ PP(pp_add) * simple integer add: if the top of both numbers * are 00 or 11, then it's safe */ if (!( ((topl+1) | (topr+1)) & 2)) { - SP--; TARGi(il + ir, 0); /* args not GMG, so can't be tainted */ - SETs(TARG); - RETURN; + rpp_replace_2_1(targ); + return NORMAL; } goto generic; } @@ -1538,10 +1697,9 @@ PP(pp_add) /* nothing was lost by converting to IVs */ goto do_iv; } - SP--; TARGn(nl + nr, 0); /* args not GMG, so can't be tainted */ - SETs(TARG); - RETURN; + rpp_replace_2_1(targ); + return NORMAL; } } @@ -1606,8 +1764,9 @@ PP(pp_add) auv = 0; a_valid = auvok = 1; /* left operand is undef, treat as zero. + 0 is identity, - Could SETi or SETu right now, but space optimise by not adding - lots of code to speed up what is probably a rarish case. */ + Could TARGi or TARGu right now, but space optimise by not + adding lots of code to speed up what is probably a rare-ish + case. */ } else { /* Left operand is defined, so is it IV? */ if (SvIV_please_nomg(svl)) { @@ -1676,20 +1835,20 @@ PP(pp_add) result_good = 1; } if (result_good) { - SP--; if (auvok) - SETu( result ); + TARGu(result,1); else { /* Negate result */ if (result <= (UV)IV_MIN) - SETi(result == (UV)IV_MIN - ? IV_MIN : -(IV)result); + TARGi(result == (UV)IV_MIN + ? IV_MIN : -(IV)result, 1); else { /* result valid, but out of range for IV. */ - SETn( -(NV)result ); + TARGn(-(NV)result, 1); } } - RETURN; + rpp_replace_2_1(targ); + return NORMAL; } /* Overflow, drop through to NVs. */ } } @@ -1700,21 +1859,22 @@ PP(pp_add) { NV value = SvNV_nomg(svr); - (void)POPs; if (!useleft) { /* left operand is undef, treat as zero. + 0.0 is identity. */ - SETn(value); - RETURN; + TARGn(value, 1); } - SETn( value + SvNV_nomg(svl) ); - RETURN; + else { + TARGn(value + SvNV_nomg(svl), 1); + } + rpp_replace_2_1(targ); + return NORMAL; } } /* also used for: pp_aelemfast_lex() */ -PP(pp_aelemfast) +PP_wrapped(pp_aelemfast, 0, 0) { dSP; AV * const av = PL_op->op_type == OP_AELEMFAST_LEX @@ -1753,7 +1913,7 @@ PP(pp_aelemfast) RETURN; } -PP(pp_join) +PP_wrapped(pp_join, 0, 1) { dSP; dMARK; dTARGET; MARK++; @@ -1767,7 +1927,7 @@ PP(pp_join) /* also used for: pp_say() */ -PP(pp_print) +PP_wrapped(pp_print, 0, 1) { dSP; dMARK; dORIGMARK; PerlIO *fp; @@ -1961,7 +2121,7 @@ S_padhv_rv2hv_common(pTHX_ HV *hv, U8 gimme, bool is_keys, bool has_targ) /* This is also called directly by pp_lvavref. */ -PP(pp_padav) +PP_wrapped(pp_padav, 0, 0) { dSP; dTARGET; U8 gimme; @@ -2003,7 +2163,7 @@ PP(pp_padav) } -PP(pp_padhv) +PP_wrapped(pp_padhv, 0, 0) { dSP; dTARGET; U8 gimme; @@ -2041,7 +2201,7 @@ PP(pp_padhv) /* also used for: pp_rv2hv() */ /* also called directly by pp_lvavref */ -PP(pp_rv2av) +PP_wrapped(pp_rv2av, 1, 0) { dSP; dTOPss; const U8 gimme = GIMME_V; @@ -2311,7 +2471,7 @@ S_aassign_copy_common(pTHX_ SV **firstlelem, SV **lastlelem, -PP(pp_aassign) +PP_wrapped(pp_aassign, 0, 2) { dSP; SV **lastlelem = PL_stack_sp; @@ -2499,7 +2659,7 @@ PP(pp_aassign) for (svp = relem; svp <= lastrelem; svp++) { SV *rsv = *svp; - if (SvTEMP(rsv) && !SvGMAGICAL(rsv) && SvREFCNT(rsv) == 1) { + if (rpp_is_lone(rsv) && !SvGMAGICAL(rsv)) { /* can skip the copy */ SvREFCNT_inc_simple_void_NN(rsv); SvTEMP_off(rsv); @@ -2620,7 +2780,7 @@ PP(pp_aassign) for (svp = relem + 1; svp <= lastrelem; svp += 2) { SV *rsv = *svp; - if (SvTEMP(rsv) && !SvGMAGICAL(rsv) && SvREFCNT(rsv) == 1) { + if (rpp_is_lone(rsv) && !SvGMAGICAL(rsv)) { /* can skip the copy */ SvREFCNT_inc_simple_void_NN(rsv); SvTEMP_off(rsv); @@ -2783,7 +2943,7 @@ PP(pp_aassign) SV *ref; if (UNLIKELY( - SvTEMP(lsv) && !SvSMAGICAL(lsv) && SvREFCNT(lsv) == 1 && + rpp_is_lone(lsv) && !SvSMAGICAL(lsv) && (!isGV_with_GP(lsv) || SvFAKE(lsv)) && ckWARN(WARN_MISC) )) Perl_warner(aTHX_ @@ -2961,7 +3121,7 @@ PP(pp_aassign) RETURN; } -PP(pp_qr) +PP_wrapped(pp_qr, 0, 0) { dSP; PMOP * const pm = cPMOP; @@ -3043,7 +3203,7 @@ S_should_we_output_Debug_r(pTHX_ regexp *prog) return S_are_we_in_Debug_EXECUTE_r(aTHX); } -PP(pp_match) +PP_wrapped(pp_match, ((PL_op->op_flags & OPf_STACKED) ? 1 : 0), 0) { dSP; dTARG; PMOP *pm = cPMOP; @@ -3526,7 +3686,7 @@ Perl_do_readline(pTHX) } } -PP(pp_helem) +PP_wrapped(pp_helem, 2, 0) { dSP; HE* he; @@ -3626,6 +3786,28 @@ S_softref2xv_lite(pTHX_ SV *const sv, const char *const what, } +/* how many stack arguments a multideref op expects */ +#ifdef PERL_RC_STACK +STATIC I32 +S_multideref_argcount(pTHX) +{ + UNOP_AUX_item *items = cUNOP_AUXx(PL_op)->op_aux; + UV actions = items->uv; + I32 nargs; + + switch (actions & MDEREF_ACTION_MASK) { + case MDEREF_AV_pop_rv2av_aelem: /* expr->[...] */ + case MDEREF_HV_pop_rv2hv_helem: /* expr->{...} */ + nargs = 1; + break; + default: + nargs = 0; + } + return nargs; +} +#endif + + /* Handle one or more aggregate derefs and array/hash indexings, e.g. * $h->{foo} or $a[0]{$key}[$i] or f()->[1] * @@ -3636,7 +3818,7 @@ S_softref2xv_lite(pTHX_ SV *const sv, const char *const what, * one UV, and only reload when it becomes zero. */ -PP(pp_multideref) +PP_wrapped(pp_multideref, S_multideref_argcount(aTHX), 0) { SV *sv = NULL; /* init to avoid spurious 'may be used uninitialized' */ UNOP_AUX_item *items = cUNOP_AUXx(PL_op)->op_aux; @@ -4046,7 +4228,7 @@ PP(pp_multideref) } -PP(pp_iter) +PP_wrapped(pp_iter, 0, 0) { PERL_CONTEXT *cx = CX_CUR(); SV **itersvp = CxITERVAR(cx); @@ -4383,7 +4565,7 @@ pp_match is just a simpler version of the above. */ -PP(pp_subst) +PP_wrapped(pp_subst, ((PL_op->op_flags & OPf_STACKED) ? 2 : 1), 0) { dSP; dTARG; PMOP *pm = cPMOP; @@ -4780,19 +4962,84 @@ PP(pp_subst) RETURN; } + PP(pp_grepwhile) { - dSP; - dPOPss; + /* Understanding the stack during a grep. + * + * 'grep expr, args' is implemented in the form of + * grepstart; + * do { + * expr; + * grepwhile; + * } while (args); + * + * The stack examples below are in the form of 'perl -Ds' output, + * where any stack element indexed by PL_markstack_ptr[i] has a star + * just to the right of it. In addition, the corresponding i value + * is displayed under the indexed stack element. + * + * On entry to grepwhile, the stack looks like this: + * + * => * M1..Mn X1 * X2..Xn C * R1..Rn BOOL + * [-2] [-1] [0] + * + * where: + * M1..Mn Accumulated args which have been matched so far. + * X1..Xn Random discardable elements from previous iterations. + * C The current (just processed) arg, still aliased to $_. + * R1..Rn The args remaining to be processed. + * BOOL the result of the just-executed grep expression. + * + * Note that it is easiest to think of the top two stack marks as both + * being one too high, and so it would make more sense to have had the + * marks like this: + * + * => * M1..Mn * X1..Xn * C R1..Rn BOOL + * [-2] [-1] [0] + * + * where the stack is divided neatly into 3 groups: + * - matched, + * - discarded, + * - being, or yet to be, processed. + * But off-by-one is the way it is currently, and it works as long as + * we keep it consistent and bear it in mind. + * + * pp_grepwhile() does the following: + * + * - for a match, replace the X1 pointer with a pointer to C and bump + * PL_markstack_ptr[-1] + * - if more args to process, bump PL_markstack_ptr[0] and update the + * $_ alias, else + * - remove top 3 MARKs and return M1..Mn, or a scalar, + * or void as appropriate. + * + */ + + bool match = SvTRUE_NN(*PL_stack_sp); + rpp_popfree_1(); + + if (match) { + SV **from_p = PL_stack_base + PL_markstack_ptr[0]; + SV **to_p = PL_stack_base + PL_markstack_ptr[-1]++; + SV *from = *from_p; + SV *to = *to_p; + + if (from != to) { + *to_p = from; +#ifdef PERL_RC_STACK + SvREFCNT_inc_simple_void_NN(from); + SvREFCNT_dec(to); +#endif + } + } - if (SvTRUE_NN(sv)) - PL_stack_base[PL_markstack_ptr[-1]++] = PL_stack_base[*PL_markstack_ptr]; ++*PL_markstack_ptr; FREETMPS; LEAVE_with_name("grep_item"); /* exit inner scope */ /* All done yet? */ - if (UNLIKELY(PL_stack_base + *PL_markstack_ptr > SP)) { + if (UNLIKELY(PL_stack_base + *PL_markstack_ptr > PL_stack_sp)) { I32 items; const U8 gimme = GIMME_V; @@ -4800,18 +5047,24 @@ PP(pp_grepwhile) (void)POPMARK; /* pop src */ items = --*PL_markstack_ptr - PL_markstack_ptr[-1]; (void)POPMARK; /* pop dst */ - SP = PL_stack_base + POPMARK; /* pop original mark */ - if (gimme == G_SCALAR) { - if (PL_op->op_private & OPpTRUEBOOL) - PUSHs(items ? &PL_sv_yes : &PL_sv_zero); - else { - dTARGET; - PUSHi(items); + SV **base = PL_stack_base + POPMARK; /* pop original mark */ + + if (gimme == G_LIST) + rpp_popfree_to(base + items); + else { + rpp_popfree_to(base); + if (gimme == G_SCALAR) { + if (PL_op->op_private & OPpTRUEBOOL) + rpp_push_1(items ? &PL_sv_yes : &PL_sv_zero); + else { + dTARGET; + TARGi(items,1); + rpp_push_1(TARG); + } } } - else if (gimme == G_LIST) - SP += items; - RETURN; + + return NORMAL; } else { SV *src; @@ -4821,16 +5074,23 @@ PP(pp_grepwhile) src = PL_stack_base[TOPMARK]; if (SvPADTMP(src)) { - src = PL_stack_base[TOPMARK] = sv_mortalcopy(src); + SV *newsrc = sv_mortalcopy(src); + PL_stack_base[TOPMARK] = newsrc; +#ifdef PERL_RC_STACK + SvREFCNT_inc_simple_void_NN(newsrc); + SvREFCNT_dec(src); +#endif + src = newsrc; PL_tmps_floor++; } SvTEMP_off(src); DEFSV_set(src); - RETURNOP(cLOGOP->op_other); + return cLOGOP->op_other; } } + /* leave_adjust_stacks(): * * Process a scope's return args (in the range from_sp+1 .. PL_stack_sp), @@ -4894,7 +5154,6 @@ PP(pp_grepwhile) void Perl_leave_adjust_stacks(pTHX_ SV **from_sp, SV **to_sp, U8 gimme, int pass) { - dSP; SSize_t tmps_base; /* lowest index into tmps stack that needs freeing now */ SSize_t nargs; @@ -4903,27 +5162,38 @@ Perl_leave_adjust_stacks(pTHX_ SV **from_sp, SV **to_sp, U8 gimme, int pass) TAINT_NOT; if (gimme == G_LIST) { - nargs = SP - from_sp; + nargs = PL_stack_sp - from_sp; from_sp++; } else { assert(gimme == G_SCALAR); - if (UNLIKELY(from_sp >= SP)) { + if (UNLIKELY(from_sp >= PL_stack_sp)) { /* no return args */ - assert(from_sp == SP); - EXTEND(SP, 1); - *++SP = &PL_sv_undef; - to_sp = SP; - nargs = 0; - } - else { - from_sp = SP; - nargs = 1; + assert(from_sp == PL_stack_sp); + rpp_extend(1); + *++PL_stack_sp = &PL_sv_undef; } + from_sp = PL_stack_sp; + nargs = 1; } /* common code for G_SCALAR and G_LIST */ +#ifdef PERL_RC_STACK + { + /* free any items from the stack which are about to get + * over-written */ + SV **p = from_sp - 1; + assert(p >= to_sp); + while (p > to_sp) { + SV *sv = *p; + *p-- = NULL; + SvREFCNT_dec(sv); + } + } +#endif + + tmps_base = PL_tmps_floor + 1; assert(nargs >= 0); @@ -4972,13 +5242,17 @@ Perl_leave_adjust_stacks(pTHX_ SV **from_sp, SV **to_sp, U8 gimme, int pass) #endif if ( - pass == 0 ? (SvTEMP(sv) && !SvMAGICAL(sv) && SvREFCNT(sv) == 1) + pass == 0 ? (rpp_is_lone(sv) && !SvMAGICAL(sv)) : pass == 1 ? ((SvTEMP(sv) || SvPADTMP(sv)) && !SvMAGICAL(sv) && SvREFCNT(sv) == 1) : pass == 2 ? (!SvPADTMP(sv)) : 1) { /* pass through: skip copy for logic or optimisation * reasons; instead mortalise it, except that ... */ + +#ifdef PERL_RC_STACK + from_sp[-1] = NULL; +#endif *++to_sp = sv; if (SvTEMP(sv)) { @@ -5063,7 +5337,6 @@ Perl_leave_adjust_stacks(pTHX_ SV **from_sp, SV **to_sp, U8 gimme, int pass) PL_tmps_stack[++PL_tmps_ix] = *tmps_basep; /* put it on the tmps stack early so it gets freed if we die */ *tmps_basep++ = newsv; - *++to_sp = newsv; if (SvTYPE(sv) <= SVt_IV) { /* arg must be one of undef, IV/UV, or RV: skip @@ -5117,6 +5390,17 @@ Perl_leave_adjust_stacks(pTHX_ SV **from_sp, SV **to_sp, U8 gimme, int pass) TAINT_NOT; /* Each item is independent */ } + +#ifdef PERL_RC_STACK + from_sp[-1] = NULL; + SvREFCNT_dec_NN(sv); + assert(!to_sp[1]); + *++to_sp = newsv; + SvREFCNT_inc_simple_void_NN(newsv); +#else + *++to_sp = newsv; +#endif + } } while (--nargs); @@ -5183,7 +5467,7 @@ PP(pp_leavesub) oldsp = PL_stack_base + cx->blk_oldsp; /* last arg of previous frame */ if (gimme == G_VOID) - PL_stack_sp = oldsp; + rpp_popfree_to(oldsp); else leave_adjust_stacks(oldsp, oldsp, gimme, 0); @@ -5205,16 +5489,25 @@ Perl_clear_defarray(pTHX_ AV* av, bool abandon) { PERL_ARGS_ASSERT_CLEAR_DEFARRAY; - if (LIKELY(!abandon && SvREFCNT(av) == 1 && !SvMAGICAL(av))) { - av_clear(av); + if (LIKELY(!abandon && SvREFCNT(av) == 1 && !SvMAGICAL(av)) +#ifndef PERL_RC_STACK + && !AvREAL(av) +#endif + ) { + clear_defarray_simple(av); +#ifndef PERL_RC_STACK AvREIFY_only(av); +#endif } else { + /* abandon */ const SSize_t size = AvFILLp(av) + 1; /* The ternary gives consistency with av_extend() */ AV *newav = newAV_alloc_x(size < PERL_ARRAY_NEW_MIN_KEY ? PERL_ARRAY_NEW_MIN_KEY : size); +#ifndef PERL_RC_STACK AvREIFY_only(newav); +#endif PAD_SVl(0) = MUTABLE_SV(newav); SvREFCNT_dec_NN(av); } @@ -5223,11 +5516,11 @@ Perl_clear_defarray(pTHX_ AV* av, bool abandon) PP(pp_entersub) { - dSP; dPOPss; GV *gv; CV *cv; PERL_CONTEXT *cx; I32 old_savestack_ix; + SV *sv = *PL_stack_sp; if (UNLIKELY(!sv)) goto do_die; @@ -5275,7 +5568,6 @@ PP(pp_entersub) do_ref: if (UNLIKELY(SvAMAGIC(sv))) { sv = amagic_deref_call(sv, to_cv_amg); - /* Don't SPAGAIN here. */ } } else { @@ -5369,6 +5661,8 @@ PP(pp_entersub) DIE(aTHX_ "No DB::sub routine defined"); } + rpp_popfree_1(); /* finished with sv now */ + if (!(CvISXSUB(cv))) { /* This path taken at least 75% of the time */ dMARK; @@ -5384,12 +5678,20 @@ PP(pp_entersub) */ { SV **svp = MARK; - while (svp < SP) { + while (svp < PL_stack_sp) { SV *sv = *++svp; if (!sv) continue; - if (SvPADTMP(sv)) - *svp = sv = sv_mortalcopy(sv); + if (SvPADTMP(sv)) { + SV *newsv = sv_mortalcopy(sv); + *svp = newsv; +#ifdef PERL_RC_STACK + /* should just skip the mortalisation instead */ + SvREFCNT_inc_simple_void_NN(newsv); + SvREFCNT_dec_NN(sv); +#endif + sv = newsv; + } SvTEMP_off(sv); } } @@ -5415,9 +5717,15 @@ PP(pp_entersub) /* it's the responsibility of whoever leaves a sub to ensure * that a clean, empty AV is left in pad[0]. This is normally * done by cx_popsub() */ - assert(!AvREAL(av) && AvFILLp(av) == -1); - items = SP - MARK; +#ifdef PERL_RC_STACK + assert(AvREAL(av)); +#else + assert(!AvREAL(av)); +#endif + assert(AvFILLp(av) == -1); + + items = PL_stack_sp - MARK; if (UNLIKELY(items - 1 > AvMAX(av))) { SV **ary = AvALLOC(av); Renew(ary, items, SV*); @@ -5429,6 +5737,10 @@ PP(pp_entersub) if (items) Copy(MARK+1,AvARRAY(av),items,SV*); AvFILLp(av) = items - 1; +#ifdef PERL_RC_STACK + /* transfer ownership of the arguments' refcounts to av */ + PL_stack_sp = MARK; +#endif } if (UNLIKELY((cx->blk_u16 & OPpENTERSUB_LVAL_MASK) == OPpLVAL_INTRO && !CvLVALUE(cv))) @@ -5442,7 +5754,7 @@ PP(pp_entersub) && ckWARN(WARN_RECURSION) && !(PERLDB_SUB && cv == GvCV(PL_DBsub)))) sub_crush_depth(cv); - RETURNOP(CvSTART(cv)); + return CvSTART(cv); } else { SSize_t markix = TOPMARK; @@ -5453,7 +5765,6 @@ PP(pp_entersub) PL_scopestack[PL_scopestack_ix - 1] = old_savestack_ix; SAVETMPS; - PUTBACK; if (UNLIKELY(((PL_op->op_private & CX_PUSHSUB_GET_LVALUE_MASK(Perl_is_lvalue_sub) @@ -5473,7 +5784,7 @@ PP(pp_entersub) SSize_t i = 0; const bool m = cBOOL(SvRMAGICAL(av)); /* Mark is at the end of the stack. */ - EXTEND(SP, items); + rpp_extend(items); for (; i < items; ++i) { SV *sv; @@ -5481,26 +5792,31 @@ PP(pp_entersub) SV ** const svp = av_fetch(av, i, 0); sv = svp ? *svp : NULL; } - else sv = AvARRAY(av)[i]; - if (sv) SP[i+1] = sv; - else { - SP[i+1] = av_nonelem(av, i); - } + else + sv = AvARRAY(av)[i]; + + rpp_push_1(sv ? sv : av_nonelem(av, i)); } - SP += items; - PUTBACK ; } } else { SV **mark = PL_stack_base + markix; - SSize_t items = SP - mark; + SSize_t items = PL_stack_sp - mark; while (items--) { mark++; if (*mark && SvPADTMP(*mark)) { - *mark = sv_mortalcopy(*mark); + SV *oldsv = *mark; + SV *newsv = sv_mortalcopy(oldsv); + *mark = newsv; +#ifdef PERL_RC_STACK + /* should just skip the mortalisation instead */ + SvREFCNT_inc_simple_void_NN(newsv); + SvREFCNT_dec_NN(oldsv); +#endif } } } + /* We assume first XSUB in &DB::sub is the called one. */ if (UNLIKELY(PL_curcopdb)) { SAVEVPTR(PL_curcop); @@ -5515,7 +5831,12 @@ PP(pp_entersub) /* CvXSUB(cv) must not be NULL because newXS() refuses NULL xsub address */ assert(CvXSUB(cv)); + +#ifdef PERL_RC_STACK + Perl_xs_wrap(aTHX_ CvXSUB(cv), cv); +#else CvXSUB(cv)(aTHX_ cv); +#endif #if defined DEBUGGING && !defined DEBUGGING_RE_ONLY /* This duplicates the check done in runops_debug(), but provides more @@ -5536,8 +5857,21 @@ PP(pp_entersub) if (is_scalar) { SV **svp = PL_stack_base + markix + 1; if (svp != PL_stack_sp) { +#ifdef PERL_RC_STACK + if (svp < PL_stack_sp) { + /* move return value to bottom of stack frame + * and free everything else */ + SV* retsv = *PL_stack_sp; + *PL_stack_sp = *svp; + *svp = retsv; + rpp_popfree_to(svp); + } + else + *++PL_stack_sp = &PL_sv_undef; +#else *svp = svp > PL_stack_sp ? &PL_sv_undef : *PL_stack_sp; PL_stack_sp = svp; +#endif } } LEAVE; @@ -5580,7 +5914,7 @@ Perl_croak_caller(const char *pat, ...) } -PP(pp_aelem) +PP_wrapped(pp_aelem, 2, 0) { dSP; SV** svp; @@ -5736,7 +6070,19 @@ S_opmethod_stash(pTHX_ SV* meth) ob = LvTARG(ob); assert(ob); } - *(PL_stack_base + TOPMARK + 1) = sv_2mortal(newRV(ob)); + /* Replace the object at the base of the stack frame. + * This is "below" whatever pp_wrap has wrapped, so needs freeing. + */ + SV *newsv = sv_2mortal(newRV(ob)); + SV **svp = (PL_stack_base + TOPMARK + 1); +#ifdef PERL_RC_STACK + SV *oldsv = *svp; +#endif + *svp = newsv; +#ifdef PERL_RC_STACK + SvREFCNT_inc_simple_void_NN(newsv); + SvREFCNT_dec_NN(oldsv); +#endif } else { /* this isn't a reference */ @@ -5764,8 +6110,20 @@ S_opmethod_stash(pTHX_ SV* meth) if (stash) return stash; else return MUTABLE_HV(sv); } - /* it _is_ a filehandle name -- replace with a reference */ - *(PL_stack_base + TOPMARK + 1) = sv_2mortal(newRV(MUTABLE_SV(iogv))); + /* it _is_ a filehandle name -- replace with a reference. + * Replace the object at the base of the stack frame. + * This is "below" whatever pp_wrap has wrapped, so needs freeing. + */ + SV *newsv = sv_2mortal(newRV(MUTABLE_SV(iogv))); + SV **svp = (PL_stack_base + TOPMARK + 1); +#ifdef PERL_RC_STACK + SV *oldsv = *svp; +#endif + *svp = newsv; +#ifdef PERL_RC_STACK + SvREFCNT_inc_simple_void_NN(newsv); + SvREFCNT_dec_NN(oldsv); +#endif } /* if we got here, ob should be an object or a glob */ @@ -5783,7 +6141,7 @@ S_opmethod_stash(pTHX_ SV* meth) return SvSTASH(ob); } -PP(pp_method) +PP_wrapped(pp_method, 1, 0) { dSP; GV* gv; @@ -5819,7 +6177,7 @@ PP(pp_method) } \ } \ -PP(pp_method_named) +PP_wrapped(pp_method_named, 0, 0) { dSP; GV* gv; @@ -5837,7 +6195,7 @@ PP(pp_method_named) RETURN; } -PP(pp_method_super) +PP_wrapped(pp_method_super, 0, 0) { dSP; GV* gv; @@ -5860,7 +6218,7 @@ PP(pp_method_super) RETURN; } -PP(pp_method_redir) +PP_wrapped(pp_method_redir, 0, 0) { dSP; GV* gv; @@ -5878,7 +6236,7 @@ PP(pp_method_redir) RETURN; } -PP(pp_method_redir_super) +PP_wrapped(pp_method_redir_super, 0, 0) { dSP; GV* gv; diff --git a/pp_pack.c b/pp_pack.c index 4442287ae40f..f436b45d2bb7 100644 --- a/pp_pack.c +++ b/pp_pack.c @@ -1849,7 +1849,7 @@ S_unpack_rec(pTHX_ tempsym_t* symptr, const char *s, const char *strbeg, const c return SP - PL_stack_base - start_sp_offset; } -PP(pp_unpack) +PP_wrapped(pp_unpack, 2, 0) { dSP; dPOPPOPssrl; @@ -3064,7 +3064,13 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist ) * of pack() (and all copies of the result) are * gone. */ - if (((SvTEMP(fromstr) && SvREFCNT(fromstr) == 1) + if (((SvTEMP(fromstr) && SvREFCNT(fromstr) <= +#ifdef PERL_RC_STACK + 2 +#else + 1 +#endif + ) || (SvPADTMP(fromstr) && !SvREADONLY(fromstr)))) { Perl_ck_warner(aTHX_ packWARN(WARN_PACK), @@ -3137,7 +3143,7 @@ S_pack_rec(pTHX_ SV *cat, tempsym_t* symptr, SV **beglist, SV **endlist ) #undef NEXTFROM -PP(pp_pack) +PP_wrapped(pp_pack, 0, 1) { dSP; dMARK; dORIGMARK; dTARGET; SV *cat = TARG; diff --git a/pp_sort.c b/pp_sort.c index 8cc90a1ade7f..d74e5bcea6cd 100644 --- a/pp_sort.c +++ b/pp_sort.c @@ -688,7 +688,7 @@ Perl_sortsv(pTHX_ SV **array, size_t nmemb, SVCOMPARE_t cmp) PP(pp_sort) { - dSP; dMARK; dORIGMARK; + dMARK; dORIGMARK; SV **p1 = ORIGMARK+1, **p2; SSize_t max, i; AV* av = NULL; @@ -697,7 +697,7 @@ PP(pp_sort) U8 gimme = GIMME_V; OP* const nextop = PL_op->op_next; I32 overloading = 0; - bool hasargs = FALSE; + bool hasargs = FALSE; /* the sort sub has proto($$)? */ bool copytmps; I32 is_xsub = 0; const U8 priv = PL_op->op_private; @@ -709,13 +709,31 @@ PP(pp_sort) descending = 1; if (gimme != G_LIST) { - SP = MARK; - EXTEND(SP,1); - RETPUSHUNDEF; + rpp_popfree_to(mark); + rpp_xpush_1(&PL_sv_undef); + return NORMAL; } ENTER; SAVEVPTR(PL_sortcop); + + /* Important flag meanings: + * + * OPf_STACKED sort args + * + * (OPf_STACKED + * |OPf_SPECIAL) sort { } args + * + * ---- standard block; e.g. sort { $a <=> $b } args + * + * + * OPpSORT_NUMERIC { $a <=> $b } (as opposed to $a cmp $b) + * OPpSORT_INTEGER ditto in scope of 'use integer' + * OPpSORT_DESCEND { $b <=> $a } + * OPpSORT_REVERSE @a= reverse sort ....; + * OPpSORT_INPLACE @a = sort @a; + */ + if (flags & OPf_STACKED) { if (flags & OPf_SPECIAL) { OP *nullop = OpSIBLING(cLISTOP->op_first); /* pass pushmark */ @@ -723,9 +741,23 @@ PP(pp_sort) PL_sortcop = nullop->op_next; } else { + /* sort list */ GV *autogv = NULL; HV *stash; - cv = sv_2cv(*++MARK, &stash, &gv, GV_ADD); + SV *fn = *++MARK; + cv = sv_2cv(fn, &stash, &gv, GV_ADD); + + /* want to remove the function name from the stack, + * but mustn't trigger cv being freed at the same time. + * Normally the name is a PV while cv is CV (duh!) but + * for lexical subs, fn can already be the CV (but is kept + * alive by a reference from the pad */ +#ifdef PERL_RC_STACK + assert(fn != (SV*)cv || SvREFCNT(fn) > 1); + SvREFCNT_dec(fn); +#endif + *MARK = NULL; + check_cv: if (cv && SvPOK(cv)) { const char * const proto = SvPV_nolen_const(MUTABLE_SV(cv)); @@ -778,69 +810,103 @@ PP(pp_sort) * push (@a) onto stack, then assign result back to @a at the end of * this function */ if (priv & OPpSORT_INPLACE) { - assert( MARK+1 == SP && *SP && SvTYPE(*SP) == SVt_PVAV); + assert( MARK+1 == PL_stack_sp + && *PL_stack_sp + && SvTYPE(*PL_stack_sp) == SVt_PVAV); (void)POPMARK; /* remove mark associated with ex-OP_AASSIGN */ - av = MUTABLE_AV((*SP)); + av = MUTABLE_AV((*PL_stack_sp)); if (SvREADONLY(av)) Perl_croak_no_modify(); max = AvFILL(av) + 1; - MEXTEND(SP, max); + + I32 oldmark = MARK - PL_stack_base; + rpp_extend(max); + MARK = PL_stack_base + oldmark; + if (SvMAGICAL(av)) { for (i=0; i < max; i++) { SV **svp = av_fetch(av, i, FALSE); - *SP++ = (svp) ? *svp : NULL; + SV *sv; + if (svp) { + sv = *svp; +#ifdef PERL_RC_STACK + SvREFCNT_inc_simple_void_NN(sv); +#endif + } + else + sv = NULL; + *++PL_stack_sp = sv; } } else { SV **svp = AvARRAY(av); assert(svp || max == 0); - for (i = 0; i < max; i++) - *SP++ = *svp++; + for (i = 0; i < max; i++) { + SV *sv = *svp++; +#ifdef PERL_RC_STACK + SvREFCNT_inc_simple_void(sv); +#endif + *++PL_stack_sp = sv; + } } - SP--; - p1 = p2 = SP - (max-1); + p1 = p2 = PL_stack_sp - (max-1); + /* we've kept av on the stacck (just below the pushed contents) so + * that a reference-counted stack keeps a reference to it for now + */ + assert((SV*)av == p1[-1]); } else { p2 = MARK+1; - max = SP - MARK; + max = PL_stack_sp - MARK; } /* shuffle stack down, removing optional initial cv (p1!=p2), plus * any nulls; also stringify or converting to integer or number as * required any args */ + + /* no ref-counted SVs at base to be overwritten */ + assert(p1 == p2 || (p1+1 == p2 && !*p1)); + copytmps = cBOOL(PL_sortcop); for (i=max; i > 0 ; i--) { - if ((*p1 = *p2++)) { /* Weed out nulls. */ - if (copytmps && SvPADTMP(*p1)) { - *p1 = sv_mortalcopy(*p1); + SV *sv = *p2++; + if (sv) { /* Weed out nulls. */ + if (copytmps && SvPADTMP(sv)) { + SV *nsv = sv_mortalcopy(sv); +#ifdef PERL_RC_STACK + SvREFCNT_dec_NN(sv); + SvREFCNT_inc_simple_void_NN(nsv); +#endif + sv = nsv; } - SvTEMP_off(*p1); + SvTEMP_off(sv); if (!PL_sortcop) { if (priv & OPpSORT_NUMERIC) { if (priv & OPpSORT_INTEGER) { - if (!SvIOK(*p1)) - (void)sv_2iv_flags(*p1, SV_GMAGIC|SV_SKIP_OVERLOAD); + if (!SvIOK(sv)) + (void)sv_2iv_flags(sv, SV_GMAGIC|SV_SKIP_OVERLOAD); } else { - if (!SvNSIOK(*p1)) - (void)sv_2nv_flags(*p1, SV_GMAGIC|SV_SKIP_OVERLOAD); - if (all_SIVs && !SvSIOK(*p1)) + if (!SvNSIOK(sv)) + (void)sv_2nv_flags(sv, SV_GMAGIC|SV_SKIP_OVERLOAD); + if (all_SIVs && !SvSIOK(sv)) all_SIVs = 0; } } else { - if (!SvPOK(*p1)) - (void)sv_2pv_flags(*p1, 0, + if (!SvPOK(sv)) + (void)sv_2pv_flags(sv, 0, SV_GMAGIC|SV_CONST_RETURN|SV_SKIP_OVERLOAD); } - if (SvAMAGIC(*p1)) + if (SvAMAGIC(sv)) overloading = 1; } - p1++; + *p1++ = sv; } else max--; } + if (max > 1) { SV **start; if (PL_sortcop) { @@ -851,8 +917,10 @@ PP(pp_sort) SAVEOP(); CATCH_SET(TRUE); - PUSHSTACKi(PERLSI_SORT); + push_stackinfo(PERLSI_SORT, 1); + if (!hasargs && !is_xsub) { + /* standard perl sub with values passed as $a and $b */ SAVEGENERICSV(PL_firstgv); SAVEGENERICSV(PL_secondgv); PL_firstgv = MUTABLE_GV(SvREFCNT_inc( @@ -888,10 +956,10 @@ PP(pp_sort) if (hasargs) { /* This is mostly copied from pp_entersub */ - AV * const av = MUTABLE_AV(PAD_SVl(0)); + AV * const av0 = MUTABLE_AV(PAD_SVl(0)); cx->blk_sub.savearray = GvAV(PL_defgv); - GvAV(PL_defgv) = MUTABLE_AV(SvREFCNT_inc_simple(av)); + GvAV(PL_defgv) = MUTABLE_AV(SvREFCNT_inc_simple(av0)); } } @@ -905,7 +973,10 @@ PP(pp_sort) /* Reset cx, in case the context stack has been reallocated. */ cx = CX_CUR(); - PL_stack_sp = PL_stack_base + cx->blk_oldsp; + /* the code used to think this could be > 0 */ + assert(cx->blk_oldsp == 0); + + rpp_popfree_to(PL_stack_base); CX_LEAVE_SCOPE(cx); if (!(flags & OPf_SPECIAL)) { @@ -918,12 +989,17 @@ PP(pp_sort) cx_popblock(cx); CX_POP(cx); - POPSTACK; + pop_stackinfo(); CATCH_SET(oldcatch); } else { - MEXTEND(SP, 20); /* Can't afford stack realloc on signal. */ - start = ORIGMARK+1; + /* call one of the built-in sort functions */ + + /* XXX this extend has been here since perl5.000. With safe + * signals, I don't think it's needed any more - DAPM. + MEXTEND(SP, 20); Can't afford stack realloc on signal. + */ + start = p1 - max; if (priv & OPpSORT_NUMERIC) { if ((priv & OPpSORT_INTEGER) || all_SIVs) { if (overloading) @@ -987,13 +1063,29 @@ PP(pp_sort) } } - if (av) { - /* copy back result to the array */ - SV** const base = MARK+1; + if (!av) { + LEAVE; + PL_stack_sp = ORIGMARK + max; + return nextop; + } + + /* OPpSORT_INPLACE: copy back result to the array */ + { + SV** const base = MARK+2; SSize_t max_minus_one = max - 1; /* attempt to work around mingw bug */ + + /* we left the AV there so on a refcounted stack it wouldn't be + * prematurely freed */ + assert(base[-1] == (SV*)av); + if (SvMAGICAL(av)) { - for (i = 0; i <= max_minus_one; i++) - base[i] = newSVsv(base[i]); + for (i = 0; i <= max_minus_one; i++) { + SV *sv = base[i]; + base[i] = newSVsv(sv); +#ifdef PERL_RC_STACK + SvREFCNT_dec_NN(sv); +#endif + } av_clear(av); if (max_minus_one >= 0) av_extend(av, max_minus_one); @@ -1002,24 +1094,45 @@ PP(pp_sort) SV ** const didstore = av_store(av, i, sv); if (SvSMAGICAL(sv)) mg_set(sv); +#ifdef PERL_RC_STACK + if (didstore) + SvREFCNT_inc_simple_void_NN(sv); +#else if (!didstore) sv_2mortal(sv); +#endif } } else { /* the elements of av are likely to be the same as the * (non-refcounted) elements on the stack, just in a different * order. However, its possible that someone's messed with av - * in the meantime. So bump and unbump the relevant refcounts - * first. + * in the meantime. + * So to avoid freeing most/all the stack elements when + * doing av_clear(), first bump the count on each element. + * In addition, normally a *copy* of each sv should be + * assigned to each array element; but if the only reference + * to that sv was from the array, then we can skip the copy. + * + * For a refcounted stack, it's not necessary to bump the + * refcounts initially, as the stack itself keeps the + * elements alive during av_clear(). + * */ for (i = 0; i <= max_minus_one; i++) { SV *sv = base[i]; assert(sv); +#ifdef PERL_RC_STACK + if (SvREFCNT(sv) > 2) { + base[i] = newSVsv(sv); + SvREFCNT_dec_NN(sv); + } +#else if (SvREFCNT(sv) > 1) base[i] = newSVsv(sv); else SvREFCNT_inc_simple_void_NN(sv); +#endif } av_clear(av); if (max_minus_one >= 0) { @@ -1029,13 +1142,31 @@ PP(pp_sort) AvFILLp(av) = max_minus_one; AvREIFY_off(av); AvREAL_on(av); +#ifdef PERL_RC_STACK + /* the AV now contributes 1 refcnt to each element */ + for (i = 0; i <= max_minus_one; i++) + SvREFCNT_inc_void_NN(base[i]); +#endif } + /* sort is only ever optimised with OPpSORT_INPLACE when the + * (@a = sort @a) is in void context. (As an aside: the context + * flag aught to be copied to the sort op: then we could assert + * here that it's void). + * Thus we can simply discard the stack elements now: their + * reference counts have already claimed by av. + */ + PL_stack_sp = ORIGMARK; +#ifdef PERL_RC_STACK + SvREFCNT_dec_NN(av); +#endif + LEAVE; + return nextop; } - LEAVE; - PL_stack_sp = ORIGMARK + max; - return nextop; } + +/* call a traditional perl compare function, setting $a and $b */ + static I32 S_sortcv(pTHX_ SV *const a, SV *const b) { @@ -1047,13 +1178,17 @@ S_sortcv(pTHX_ SV *const a, SV *const b) PERL_ARGS_ASSERT_SORTCV; +#ifdef PERL_RC_STACK + assert(rpp_stack_is_rc()); +#endif + olda = GvSV(PL_firstgv); GvSV(PL_firstgv) = SvREFCNT_inc_simple_NN(a); SvREFCNT_dec(olda); oldb = GvSV(PL_secondgv); GvSV(PL_secondgv) = SvREFCNT_inc_simple_NN(b); SvREFCNT_dec(oldb); - PL_stack_sp = PL_stack_base; + assert(PL_stack_sp == PL_stack_base); PL_op = PL_sortcop; CALLRUNOPS(aTHX); PL_curcop = cop; @@ -1061,12 +1196,16 @@ S_sortcv(pTHX_ SV *const a, SV *const b) * simplifies converting a '()' return into undef in scalar context */ assert(PL_stack_sp > PL_stack_base || *PL_stack_base == &PL_sv_undef); result = SvIV(*PL_stack_sp); + rpp_popfree_to(PL_stack_base); LEAVE_SCOPE(oldsaveix); PL_curpm = pm; return result; } + +/* call a perl compare function that has a ($$) prototype, setting @_ */ + static I32 S_sortcv_stacked(pTHX_ SV *const a, SV *const b) { @@ -1078,11 +1217,21 @@ S_sortcv_stacked(pTHX_ SV *const a, SV *const b) PERL_ARGS_ASSERT_SORTCV_STACKED; +#ifdef PERL_RC_STACK + assert(rpp_stack_is_rc()); +#endif + +#ifdef PERL_RC_STACK + assert(AvREAL(av)); + av_clear(av); +#else if (AvREAL(av)) { av_clear(av); AvREAL_off(av); AvREIFY_on(av); } +#endif + if (AvMAX(av) < 1) { SV **ary = AvALLOC(av); if (AvARRAY(av) != ary) { @@ -1100,7 +1249,11 @@ S_sortcv_stacked(pTHX_ SV *const a, SV *const b) AvARRAY(av)[0] = a; AvARRAY(av)[1] = b; - PL_stack_sp = PL_stack_base; +#ifdef PERL_RC_STACK + SvREFCNT_inc_simple_void_NN(a); + SvREFCNT_inc_simple_void_NN(b); +#endif + assert(PL_stack_sp == PL_stack_base); PL_op = PL_sortcop; CALLRUNOPS(aTHX); PL_curcop = cop; @@ -1108,16 +1261,20 @@ S_sortcv_stacked(pTHX_ SV *const a, SV *const b) * simplifies converting a '()' return into undef in scalar context */ assert(PL_stack_sp > PL_stack_base || *PL_stack_base == &PL_sv_undef); result = SvIV(*PL_stack_sp); + rpp_popfree_to(PL_stack_base); LEAVE_SCOPE(oldsaveix); PL_curpm = pm; return result; } + +/* call an XS compare function. (The two args are always passed on the + * stack, regardless of whether it has a ($$) prototype or not.) */ + static I32 S_sortcv_xsub(pTHX_ SV *const a, SV *const b) { - dSP; const I32 oldsaveix = PL_savestack_ix; CV * const cv=MUTABLE_CV(PL_sortcop); I32 result; @@ -1125,17 +1282,25 @@ S_sortcv_xsub(pTHX_ SV *const a, SV *const b) PERL_ARGS_ASSERT_SORTCV_XSUB; - SP = PL_stack_base; - PUSHMARK(SP); - EXTEND(SP, 2); - *++SP = a; - *++SP = b; - PUTBACK; +#ifdef PERL_RC_STACK + assert(rpp_stack_is_rc()); +#endif + + assert(PL_stack_sp == PL_stack_base); + PUSHMARK(PL_stack_sp); + rpp_xpush_2(a, b); + +#ifdef PERL_RC_STACK + Perl_xs_wrap(aTHX_ CvXSUB(cv), cv); +#else (void)(*CvXSUB(cv))(aTHX_ cv); +#endif + /* entry zero of a stack is always PL_sv_undef, which * simplifies converting a '()' return into undef in scalar context */ assert(PL_stack_sp > PL_stack_base || *PL_stack_base == &PL_sv_undef); result = SvIV(*PL_stack_sp); + rpp_popfree_to(PL_stack_base); LEAVE_SCOPE(oldsaveix); PL_curpm = pm; diff --git a/pp_sys.c b/pp_sys.c index 26a9dda00604..64c6c3a18764 100644 --- a/pp_sys.c +++ b/pp_sys.c @@ -274,7 +274,7 @@ S_emulate_eaccess(pTHX_ const char* path, Mode_t mode) # define PERL_EFF_ACCESS(p,f) (S_emulate_eaccess(aTHX_ (p), (f))) #endif -PP(pp_backtick) +PP_wrapped(pp_backtick, 1, 0) { dSP; dTARGET; PerlIO *fp; @@ -335,7 +335,7 @@ PP(pp_backtick) RETURN; } -PP(pp_glob) +PP_wrapped(pp_glob, 1 + !(PL_op->op_flags & OPf_SPECIAL), 0) { OP *result; dSP; @@ -394,13 +394,13 @@ PP(pp_glob) return result; } -PP(pp_rcatline) +PP_wrapped(pp_rcatline, 1, 0) { PL_last_in_gv = cGVOP_gv; return do_readline(); } -PP(pp_warn) +PP_wrapped(pp_warn, 0, 1) { dSP; dMARK; SV *exsv; @@ -449,7 +449,7 @@ PP(pp_warn) RETSETYES; } -PP(pp_die) +PP_wrapped(pp_die, 0, 1) { dSP; dMARK; SV *exsv; @@ -590,7 +590,7 @@ Perl_tied_method(pTHX_ SV *methname, SV **sp, SV *const sv, #define tied_method2(a,b,c,d,e,f) \ Perl_tied_method(aTHX_ a,b,c,d,G_SCALAR,2,e,f) -PP(pp_open) +PP_wrapped(pp_open, 0, 1) { dSP; dMARK; dORIGMARK; @@ -643,7 +643,7 @@ PP(pp_open) RETURN; } -PP(pp_close) +PP_wrapped(pp_close, MAXARG, 0) { dSP; /* pp_coreargs pushes a NULL to indicate no args passed to @@ -667,7 +667,7 @@ PP(pp_close) RETURN; } -PP(pp_pipe_op) +PP_wrapped(pp_pipe_op, 2, 0) { #ifdef HAS_PIPE dSP; @@ -716,7 +716,7 @@ PP(pp_pipe_op) #endif } -PP(pp_fileno) +PP_wrapped(pp_fileno, MAXARG, 0) { dSP; dTARGET; GV *gv; @@ -764,7 +764,7 @@ PP(pp_fileno) RETURN; } -PP(pp_umask) +PP_wrapped(pp_umask, MAXARG, 0) { dSP; #ifdef HAS_UMASK @@ -794,7 +794,7 @@ PP(pp_umask) RETURN; } -PP(pp_binmode) +PP_wrapped(pp_binmode, MAXARG, 0) { dSP; GV *gv; @@ -855,7 +855,7 @@ PP(pp_binmode) } } -PP(pp_tie) +PP_wrapped(pp_tie, 0, 1) { dSP; dMARK; HV* stash; @@ -996,7 +996,7 @@ PP(pp_tie) /* also used for: pp_dbmclose() */ -PP(pp_untie) +PP_wrapped(pp_untie, 1, 0) { dSP; MAGIC *mg; @@ -1048,7 +1048,7 @@ PP(pp_untie) RETPUSHYES; } -PP(pp_tied) +PP_wrapped(pp_tied, 1, 0) { dSP; const MAGIC *mg; @@ -1071,7 +1071,7 @@ PP(pp_tied) return NORMAL; } -PP(pp_dbmopen) +PP_wrapped(pp_dbmopen, 3, 0) { dSP; dPOPPOPssrl; @@ -1129,7 +1129,7 @@ PP(pp_dbmopen) RETURN; } -PP(pp_sselect) +PP_wrapped(pp_sselect, 4, 0) { #ifdef HAS_SELECT dSP; dTARGET; @@ -1337,7 +1337,7 @@ Perl_setdefout(pTHX_ GV *gv) SvREFCNT_dec(oldgv); } -PP(pp_select) +PP_wrapped(pp_select, MAXARG, 0) { dSP; dTARGET; HV *hv; @@ -1368,7 +1368,7 @@ PP(pp_select) RETURN; } -PP(pp_getc) +PP_wrapped(pp_getc, MAXARG, 0) { dSP; dTARGET; /* pp_coreargs pushes a NULL to indicate no args passed to @@ -1437,26 +1437,35 @@ S_doform(pTHX_ CV *cv, GV *gv, OP *retop) return CvSTART(cv); } + PP(pp_enterwrite) { - dSP; GV *gv; IO *io; GV *fgv; CV *cv = NULL; if (MAXARG == 0) { - EXTEND(SP, 1); + rpp_extend(1); gv = PL_defoutgv; } else { - gv = MUTABLE_GV(POPs); + gv = MUTABLE_GV(*PL_stack_sp); + /* NB: in principle, decrementing gv's ref count could free it, + * and we aught to make the gv field of the struct block_format + * reference counted to compensate; in practice, since formats + * invariably use named GVs in the source which link to the GV, + * it's almost impossible to free a GV during format processing. + */ + rpp_popfree_1(); if (!gv) gv = PL_defoutgv; } io = GvIO(gv); if (!io) { - RETPUSHNO; + *++PL_stack_sp = &PL_sv_no; + return NORMAL; + } if (IoFMT_GV(io)) fgv = IoFMT_GV(io); @@ -1472,12 +1481,12 @@ PP(pp_enterwrite) DIE(aTHX_ "Undefined format \"%" SVf "\" called", SVfARG(tmpsv)); } IoFLAGS(io) &= ~IOf_DIDTOP; - RETURNOP(doform(cv,gv,PL_op->op_next)); + return doform(cv,gv,PL_op->op_next); } + PP(pp_leavewrite) { - dSP; GV * const gv = CX_CUR()->blk_format.gv; IO * const io = GvIOp(gv); PerlIO *ofp; @@ -1561,48 +1570,49 @@ PP(pp_leavewrite) forget_top: cx = CX_CUR(); assert(CxTYPE(cx) == CXt_FORMAT); - SP = PL_stack_base + cx->blk_oldsp; /* ignore retval of formline */ + rpp_popfree_to(PL_stack_base + cx->blk_oldsp); /* ignore retval of formline */ CX_LEAVE_SCOPE(cx); cx_popformat(cx); cx_popblock(cx); retop = cx->blk_sub.retop; CX_POP(cx); - EXTEND(SP, 1); + rpp_extend(1); if (is_return) /* XXX the semantics of doing 'return' in a format aren't documented. * Currently we ignore any args to 'return' and just return * a single undef in both scalar and list contexts */ - PUSHs(&PL_sv_undef); + *++PL_stack_sp = &PL_sv_undef; else if (!io || !(fp = IoOFP(io))) { if (io && IoIFP(io)) report_wrongway_fh(gv, '<'); else report_evil_fh(gv); - PUSHs(&PL_sv_no); + *++PL_stack_sp = &PL_sv_no; } else { if ((IoLINES_LEFT(io) -= FmLINES(PL_formtarget)) < 0) { Perl_ck_warner(aTHX_ packWARN(WARN_IO), "page overflow"); } if (!do_print(PL_formtarget, fp)) - PUSHs(&PL_sv_no); + *++PL_stack_sp = &PL_sv_no; else { FmLINES(PL_formtarget) = 0; SvCUR_set(PL_formtarget, 0); *SvEND(PL_formtarget) = '\0'; if (IoFLAGS(io) & IOf_FLUSH) (void)PerlIO_flush(fp); - PUSHs(&PL_sv_yes); + *++PL_stack_sp = &PL_sv_yes; } } PL_formtarget = PL_bodytarget; - RETURNOP(retop); + return retop; } -PP(pp_prtf) + +PP_wrapped(pp_prtf, 0, 1) { dSP; dMARK; dORIGMARK; PerlIO *fp; @@ -1663,7 +1673,7 @@ PP(pp_prtf) RETURN; } -PP(pp_sysopen) +PP_wrapped(pp_sysopen, MAXARG, 0) { dSP; const int perm = (MAXARG > 3 && (TOPs || POPs)) ? POPi : 0666; @@ -1687,7 +1697,7 @@ PP(pp_sysopen) /* also used for: pp_read() and pp_recv() (where supported) */ -PP(pp_sysread) +PP_wrapped(pp_sysread, 0, 1) { dSP; dMARK; dORIGMARK; dTARGET; SSize_t offset; @@ -1950,7 +1960,7 @@ PP(pp_sysread) /* also used for: pp_send() where defined */ -PP(pp_syswrite) +PP_wrapped(pp_syswrite, 0, 1) { dSP; dMARK; dORIGMARK; dTARGET; SV *bufsv; @@ -2104,7 +2114,7 @@ PP(pp_syswrite) RETPUSHUNDEF; } -PP(pp_eof) +PP_wrapped(pp_eof, MAXARG, 0) { dSP; GV *gv; @@ -2171,7 +2181,7 @@ PP(pp_eof) RETURN; } -PP(pp_tell) +PP_wrapped(pp_tell, MAXARG, 0) { dSP; dTARGET; GV *gv; @@ -2208,7 +2218,7 @@ PP(pp_tell) /* also used for: pp_seek() */ -PP(pp_sysseek) +PP_wrapped(pp_sysseek, 3, 0) { dSP; const int whence = POPi; @@ -2255,7 +2265,7 @@ PP(pp_sysseek) RETURN; } -PP(pp_truncate) +PP_wrapped(pp_truncate, 2, 0) { dSP; /* There seems to be no consensus on the length type of truncate() @@ -2365,7 +2375,7 @@ PP(pp_truncate) /* also used for: pp_fcntl() */ -PP(pp_ioctl) +PP_wrapped(pp_ioctl, 3, 0) { dSP; dTARGET; SV * const argsv = POPs; @@ -2438,7 +2448,7 @@ PP(pp_ioctl) RETURN; } -PP(pp_flock) +PP_wrapped(pp_flock, 2, 0) { #ifdef FLOCK dSP; dTARGET; @@ -2469,7 +2479,7 @@ PP(pp_flock) #ifdef HAS_SOCKET -PP(pp_socket) +PP_wrapped(pp_socket, 4, 0) { dSP; const int protocol = POPi; @@ -2501,7 +2511,7 @@ PP(pp_socket) } #endif -PP(pp_sockpair) +PP_wrapped(pp_sockpair, 5, 0) { #if defined (HAS_SOCKETPAIR) || (defined (HAS_SOCKET) && defined(SOCK_DGRAM) && defined(AF_INET) && defined(PF_INET)) dSP; @@ -2549,7 +2559,7 @@ PP(pp_sockpair) /* also used for: pp_connect() */ -PP(pp_bind) +PP_wrapped(pp_bind, 2, 0) { dSP; SV * const addrsv = POPs; @@ -2584,7 +2594,7 @@ PP(pp_bind) RETPUSHUNDEF; } -PP(pp_listen) +PP_wrapped(pp_listen, 2, 0) { dSP; const int backlog = POPi; @@ -2605,7 +2615,7 @@ PP(pp_listen) RETPUSHUNDEF; } -PP(pp_accept) +PP_wrapped(pp_accept, 2, 0) { dSP; dTARGET; IO *nstio; @@ -2666,7 +2676,7 @@ PP(pp_accept) } -PP(pp_shutdown) +PP_wrapped(pp_shutdown, 2, 0) { dSP; dTARGET; const int how = POPi; @@ -2688,7 +2698,7 @@ PP(pp_shutdown) /* also used for: pp_gsockopt() */ -PP(pp_ssockopt) +PP_wrapped(pp_ssockopt,(PL_op->op_type == OP_GSOCKOPT) ? 3 : 4 , 0) { dSP; const int optype = PL_op->op_type; @@ -2758,7 +2768,7 @@ PP(pp_ssockopt) /* also used for: pp_getsockname() */ -PP(pp_getpeername) +PP_wrapped(pp_getpeername, 1, 0) { dSP; const int optype = PL_op->op_type; @@ -2828,7 +2838,7 @@ PP(pp_getpeername) /* also used for: pp_lstat() */ -PP(pp_stat) +PP_wrapped(pp_stat, !(PL_op->op_flags & OPf_REF), 0) { dSP; GV *gv = NULL; @@ -3065,9 +3075,7 @@ PP(pp_stat) /* All filetest ops avoid manipulating the perl stack pointer in their main bodies (since commit d2c4d2d1e22d3125), and return using either S_ft_return_false() or S_ft_return_true(). These two helper functions are - the only two which manipulate the perl stack. To ensure that no stack - manipulation macros are used, the filetest ops avoid defining a local copy - of the stack pointer with dSP. */ + the only two which manipulate the perl stack. */ /* If the next filetest is stacked up with this one (PL_op->op_private & OPpFT_STACKING), we leave @@ -3079,11 +3087,12 @@ PP(pp_stat) static OP * S_ft_return_false(pTHX_ SV *ret) { OP *next = NORMAL; - dSP; - if (PL_op->op_flags & OPf_REF) XPUSHs(ret); - else SETs(ret); - PUTBACK; + if (PL_op->op_flags & OPf_REF) { + rpp_xpush_1(ret); + } + else + rpp_replace_1_1(ret); if (PL_op->op_private & OPpFT_STACKING) { while (next && OP_IS_FILETEST(next->op_type) @@ -3095,12 +3104,12 @@ S_ft_return_false(pTHX_ SV *ret) { PERL_STATIC_INLINE OP * S_ft_return_true(pTHX_ SV *ret) { - dSP; - if (PL_op->op_flags & OPf_REF) - XPUSHs(PL_op->op_private & OPpFT_STACKING ? (SV *)cGVOP_gv : (ret)); + if (PL_op->op_flags & OPf_REF) { + rpp_xpush_1((PL_op->op_private & OPpFT_STACKING) + ? (SV*)cGVOP_gv : ret); + } else if (!(PL_op->op_private & OPpFT_STACKING)) - SETs(ret); - PUTBACK; + rpp_replace_1_1(ret); return NORMAL; } @@ -3108,9 +3117,11 @@ S_ft_return_true(pTHX_ SV *ret) { #define FT_RETURNUNDEF return S_ft_return_false(aTHX_ &PL_sv_undef) #define FT_RETURNYES return S_ft_return_true(aTHX_ &PL_sv_yes) +/* NB: OPf_REF implies '-X _' and thus no arg on the stack */ #define tryAMAGICftest_MG(chr) STMT_START { \ - if ( (SvFLAGS(*PL_stack_sp) & (SVf_ROK|SVs_GMG)) \ - && PL_op->op_flags & OPf_KIDS) { \ + if ( !(PL_op->op_flags & OPf_REF) \ + && (SvFLAGS(*PL_stack_sp) & (SVf_ROK|SVs_GMG))) \ + { \ OP *next = S_try_amagic_ftest(aTHX_ chr); \ if (next) return next; \ } \ @@ -3658,7 +3669,7 @@ PP(pp_fttext) /* File calls. */ -PP(pp_chdir) +PP_wrapped(pp_chdir, MAXARG, 0) { dSP; dTARGET; const char *tmps = NULL; @@ -3750,7 +3761,7 @@ PP(pp_chdir) /* also used for: pp_chmod() pp_kill() pp_unlink() pp_utime() */ -PP(pp_chown) +PP_wrapped(pp_chown, 0, 1) { dSP; dMARK; dTARGET; const I32 value = (I32)apply(PL_op->op_type, MARK, SP); @@ -3760,7 +3771,7 @@ PP(pp_chown) RETURN; } -PP(pp_chroot) +PP_wrapped(pp_chroot, 1, 0) { #ifdef HAS_CHROOT dSP; dTARGET; @@ -3773,7 +3784,7 @@ PP(pp_chroot) #endif } -PP(pp_rename) +PP_wrapped(pp_rename, 2, 0) { dSP; dTARGET; int anum; @@ -3805,7 +3816,7 @@ PP(pp_rename) /* also used for: pp_symlink() */ #if defined(HAS_LINK) || defined(HAS_SYMLINK) -PP(pp_link) +PP_wrapped(pp_link, 2, 0) { dSP; dTARGET; const int op_type = PL_op->op_type; @@ -3852,7 +3863,7 @@ PP(pp_link) } #endif -PP(pp_readlink) +PP_wrapped(pp_readlink, 1, 0) { dSP; #ifdef HAS_SYMLINK @@ -3983,7 +3994,7 @@ S_dooneliner(pTHX_ const char *cmd, const char *filename) (copy) = TRUE; \ } -PP(pp_mkdir) +PP_wrapped(pp_mkdir, MAXARG, 0) { dSP; dTARGET; STRLEN len; @@ -4010,7 +4021,7 @@ PP(pp_mkdir) RETURN; } -PP(pp_rmdir) +PP_wrapped(pp_rmdir, 1, 0) { dSP; dTARGET; STRLEN len; @@ -4031,7 +4042,7 @@ PP(pp_rmdir) /* Directory calls. */ -PP(pp_open_dir) +PP_wrapped(pp_open_dir, 2, 0) { #if defined(Direntry_t) && defined(HAS_READDIR) dSP; @@ -4057,7 +4068,7 @@ PP(pp_open_dir) #endif } -PP(pp_readdir) +PP_wrapped(pp_readdir, 1, 0) { #if !defined(Direntry_t) || !defined(HAS_READDIR) DIE(aTHX_ PL_no_dir_func, "readdir"); @@ -4109,7 +4120,7 @@ PP(pp_readdir) #endif } -PP(pp_telldir) +PP_wrapped(pp_telldir, 1, 0) { #if defined(HAS_TELLDIR) || defined(telldir) dSP; dTARGET; @@ -4141,7 +4152,7 @@ PP(pp_telldir) #endif } -PP(pp_seekdir) +PP_wrapped(pp_seekdir, 2, 0) { #if defined(HAS_SEEKDIR) || defined(seekdir) dSP; @@ -4167,7 +4178,7 @@ PP(pp_seekdir) #endif } -PP(pp_rewinddir) +PP_wrapped(pp_rewinddir, 1, 0) { #if defined(HAS_REWINDDIR) || defined(rewinddir) dSP; @@ -4191,7 +4202,7 @@ PP(pp_rewinddir) #endif } -PP(pp_closedir) +PP_wrapped(pp_closedir, 1, 0) { #if defined(Direntry_t) && defined(HAS_READDIR) dSP; @@ -4226,7 +4237,7 @@ PP(pp_closedir) /* Process control. */ -PP(pp_fork) +PP_wrapped(pp_fork, 0, 0) { #ifdef HAS_FORK dSP; dTARGET; @@ -4298,7 +4309,7 @@ PP(pp_fork) #endif } -PP(pp_wait) +PP_wrapped(pp_wait, 0, 0) { #if (!defined(DOSISH) || defined(OS2) || defined(WIN32)) && !defined(__LIBCATAMOUNT__) dSP; dTARGET; @@ -4326,7 +4337,7 @@ PP(pp_wait) #endif } -PP(pp_waitpid) +PP_wrapped(pp_waitpid, 2, 0) { #if (!defined(DOSISH) || defined(OS2) || defined(WIN32)) && !defined(__LIBCATAMOUNT__) dSP; dTARGET; @@ -4363,7 +4374,7 @@ PP(pp_waitpid) #endif } -PP(pp_system) +PP_wrapped(pp_system, 0, 1) { dSP; dMARK; dORIGMARK; dTARGET; #if defined(__LIBCATAMOUNT__) @@ -4577,7 +4588,7 @@ PP(pp_system) RETURN; } -PP(pp_exec) +PP_wrapped(pp_exec, 0, 1) { dSP; dMARK; dORIGMARK; dTARGET; I32 value; @@ -4616,7 +4627,7 @@ PP(pp_exec) RETURN; } -PP(pp_getppid) +PP_wrapped(pp_getppid, 0, 0) { #ifdef HAS_GETPPID dSP; dTARGET; @@ -4627,7 +4638,7 @@ PP(pp_getppid) #endif } -PP(pp_getpgrp) +PP_wrapped(pp_getpgrp, MAXARG, 0) { #ifdef HAS_GETPGRP dSP; dTARGET; @@ -4649,7 +4660,7 @@ PP(pp_getpgrp) #endif } -PP(pp_setpgrp) +PP_wrapped(pp_setpgrp, MAXARG, 0) { #ifdef HAS_SETPGRP dSP; dTARGET; @@ -4691,7 +4702,7 @@ PP(pp_setpgrp) # define PRIORITY_WHICH_T(which) which #endif -PP(pp_getpriority) +PP_wrapped(pp_getpriority, 2, 0) { #ifdef HAS_GETPRIORITY dSP; dTARGET; @@ -4704,7 +4715,7 @@ PP(pp_getpriority) #endif } -PP(pp_setpriority) +PP_wrapped(pp_setpriority, 3, 0) { #ifdef HAS_SETPRIORITY dSP; dTARGET; @@ -4723,7 +4734,7 @@ PP(pp_setpriority) /* Time calls. */ -PP(pp_time) +PP_wrapped(pp_time, 0, 0) { dSP; dTARGET; #ifdef BIG_TIME @@ -4734,7 +4745,7 @@ PP(pp_time) RETURN; } -PP(pp_tms) +PP_wrapped(pp_tms, 0, 0) { #ifdef HAS_TIMES dSP; @@ -4777,7 +4788,7 @@ PP(pp_tms) /* also used for: pp_localtime() */ -PP(pp_gmtime) +PP_wrapped(pp_gmtime, MAXARG, 0) { dSP; Time64_T when; @@ -4873,7 +4884,7 @@ PP(pp_gmtime) RETURN; } -PP(pp_alarm) +PP_wrapped(pp_alarm, 1, 0) { #ifdef HAS_ALARM dSP; dTARGET; @@ -4906,7 +4917,7 @@ PP(pp_alarm) #endif } -PP(pp_sleep) +PP_wrapped(pp_sleep, MAXARG, 0) { dSP; dTARGET; Time_t lasttime; @@ -4938,7 +4949,7 @@ PP(pp_sleep) /* also used for: pp_msgrcv() pp_msgsnd() pp_semop() pp_shmread() */ -PP(pp_shmwrite) +PP_wrapped(pp_shmwrite, 0, 1) { #if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM) dSP; dMARK; dTARGET; @@ -4972,7 +4983,7 @@ PP(pp_shmwrite) /* also used for: pp_msgget() pp_shmget() */ -PP(pp_semget) +PP_wrapped(pp_semget, 0, 1) { #if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM) dSP; dMARK; dTARGET; @@ -4989,7 +5000,7 @@ PP(pp_semget) /* also used for: pp_msgctl() pp_shmctl() */ -PP(pp_semctl) +PP_wrapped(pp_semctl, 0, 1) { #if defined(HAS_MSG) || defined(HAS_SEM) || defined(HAS_SHM) dSP; dMARK; dTARGET; @@ -5034,7 +5045,10 @@ S_space_join_names_mortal(pTHX_ char *const *array) /* also used for: pp_ghbyaddr() pp_ghbyname() */ -PP(pp_ghostent) +PP_wrapped(pp_ghostent, + ((PL_op->op_type == OP_GHBYNAME) ? 1 : + (PL_op->op_type == OP_GHBYADDR) ? 2 : 0), + 0) { #if defined(HAS_GETHOSTBYNAME) || defined(HAS_GETHOSTBYADDR) || defined(HAS_GETHOSTENT) dSP; @@ -5128,7 +5142,10 @@ PP(pp_ghostent) /* also used for: pp_gnbyaddr() pp_gnbyname() */ -PP(pp_gnetent) +PP_wrapped(pp_gnetent, + ((PL_op->op_type == OP_GNBYNAME) ? 1 : + (PL_op->op_type == OP_GNBYADDR) ? 2 : 0), + 0) { #if defined(HAS_GETNETBYNAME) || defined(HAS_GETNETBYADDR) || defined(HAS_GETNETENT) dSP; @@ -5204,7 +5221,10 @@ PP(pp_gnetent) /* also used for: pp_gpbyname() pp_gpbynumber() */ -PP(pp_gprotoent) +PP_wrapped(pp_gprotoent, + ((PL_op->op_type == OP_GPBYNAME) ? 1 : + (PL_op->op_type == OP_GPBYNUMBER) ? 1 : 0), + 0) { #if defined(HAS_GETPROTOBYNAME) || defined(HAS_GETPROTOBYNUMBER) || defined(HAS_GETPROTOENT) dSP; @@ -5267,7 +5287,10 @@ PP(pp_gprotoent) /* also used for: pp_gsbyname() pp_gsbyport() */ -PP(pp_gservent) +PP_wrapped(pp_gservent, + ((PL_op->op_type == OP_GSBYNAME) ? 2 : + (PL_op->op_type == OP_GSBYPORT) ? 2 : 0), + 0) { #if defined(HAS_GETSERVBYNAME) || defined(HAS_GETSERVBYPORT) || defined(HAS_GETSERVENT) dSP; @@ -5335,7 +5358,7 @@ PP(pp_gservent) /* also used for: pp_snetent() pp_sprotoent() pp_sservent() */ -PP(pp_shostent) +PP_wrapped(pp_shostent, 1, 0) { dSP; const int stayopen = TOPi; @@ -5376,7 +5399,7 @@ PP(pp_shostent) /* also used for: pp_egrent() pp_enetent() pp_eprotoent() pp_epwent() * pp_eservent() pp_sgrent() pp_spwent() */ -PP(pp_ehostent) +PP_wrapped(pp_ehostent, 0, 0) { dSP; switch(PL_op->op_type) { @@ -5444,7 +5467,10 @@ PP(pp_ehostent) /* also used for: pp_gpwnam() pp_gpwuid() */ -PP(pp_gpwent) +PP_wrapped(pp_gpwent, + ((PL_op->op_type == OP_GPWNAM) ? 1 : + (PL_op->op_type == OP_GPWUID) ? 1 : 0), + 0) { #ifdef HAS_PASSWD dSP; @@ -5671,7 +5697,10 @@ PP(pp_gpwent) /* also used for: pp_ggrgid() pp_ggrnam() */ -PP(pp_ggrent) +PP_wrapped(pp_ggrent, + ((PL_op->op_type == OP_GGRNAM) ? 1 : + (PL_op->op_type == OP_GGRGID) ? 1 : 0), + 0) { #ifdef HAS_GROUP dSP; @@ -5743,7 +5772,7 @@ PP(pp_ggrent) #endif } -PP(pp_getlogin) +PP_wrapped(pp_getlogin, 0, 0) { #ifdef HAS_GETLOGIN dSP; dTARGET; @@ -5761,7 +5790,7 @@ PP(pp_getlogin) /* Miscellaneous. */ -PP(pp_syscall) +PP_wrapped(pp_syscall, 0, 1) { #ifdef HAS_SYSCALL dSP; dMARK; dORIGMARK; dTARGET; diff --git a/proto.h b/proto.h index 518422ed328c..608f6b53ed3c 100644 --- a/proto.h +++ b/proto.h @@ -600,9 +600,10 @@ Perl_coresub_op(pTHX_ SV * const coreargssv, const int code, const int opnum) assert(coreargssv) PERL_CALLCONV void -Perl_create_eval_scope(pTHX_ OP *retop, U32 flags) +Perl_create_eval_scope(pTHX_ OP *retop, SV **sp, U32 flags) __attribute__visibility__("hidden"); -#define PERL_ARGS_ASSERT_CREATE_EVAL_SCOPE +#define PERL_ARGS_ASSERT_CREATE_EVAL_SCOPE \ + assert(sp) PERL_CALLCONV_NO_RET void Perl_croak(pTHX_ const char *pat, ...) @@ -3105,6 +3106,11 @@ Perl_new_stackinfo(pTHX_ I32 stitems, I32 cxitems) __attribute__warn_unused_result__; #define PERL_ARGS_ASSERT_NEW_STACKINFO +PERL_CALLCONV PERL_SI * +Perl_new_stackinfo_flags(pTHX_ I32 stitems, I32 cxitems, UV flags) + __attribute__warn_unused_result__; +#define PERL_ARGS_ASSERT_NEW_STACKINFO_FLAGS + PERL_CALLCONV SV * Perl_new_version(pTHX_ SV *ver); #define PERL_ARGS_ASSERT_NEW_VERSION \ @@ -3817,6 +3823,10 @@ Perl_rpeep(pTHX_ OP *o) __attribute__visibility__("hidden"); #define PERL_ARGS_ASSERT_RPEEP +PERL_CALLCONV void +Perl_rpp_obliterate_stack_to(pTHX_ I32 ix); +#define PERL_ARGS_ASSERT_RPP_OBLITERATE_STACK_TO + PERL_CALLCONV Sighandler_t Perl_rsignal(pTHX_ int i, Sighandler_t t); #define PERL_ARGS_ASSERT_RSIGNAL @@ -3844,6 +3854,10 @@ PERL_CALLCONV int Perl_runops_standard(pTHX); #define PERL_ARGS_ASSERT_RUNOPS_STANDARD +PERL_CALLCONV int +Perl_runops_wrap(pTHX); +#define PERL_ARGS_ASSERT_RUNOPS_WRAP + PERL_CALLCONV CV * Perl_rv2cv_op_cv(pTHX_ OP *cvop, U32 flags); #define PERL_ARGS_ASSERT_RV2CV_OP_CV \ @@ -6290,7 +6304,7 @@ Perl_croak_kw_unless_class(pTHX_ const char *kw); defined(PERL_IN_PERLY_C) || defined(PERL_IN_TOKE_C) */ #if defined(PERL_IN_DEB_C) STATIC void -S_deb_stack_n(pTHX_ SV **stack_base, I32 stack_min, I32 stack_max, I32 mark_min, I32 mark_max); +S_deb_stack_n(pTHX_ SV **stack_base, I32 stack_min, I32 stack_max, I32 mark_min, I32 mark_max, I32 nonrc_base); # define PERL_ARGS_ASSERT_DEB_STACK_N \ assert(stack_base) @@ -9738,11 +9752,21 @@ Perl_av_push_simple(pTHX_ AV *av, SV *val); # define PERL_ARGS_ASSERT_AV_PUSH_SIMPLE \ assert(av); assert(val) +PERL_STATIC_INLINE void +Perl_av_remove_offset(pTHX_ AV *av); +# define PERL_ARGS_ASSERT_AV_REMOVE_OFFSET \ + assert(av) + PERL_STATIC_INLINE SV ** Perl_av_store_simple(pTHX_ AV *av, SSize_t key, SV *val); # define PERL_ARGS_ASSERT_AV_STORE_SIMPLE \ assert(av) +PERL_STATIC_INLINE void +Perl_clear_defarray_simple(pTHX_ AV *av); +# define PERL_ARGS_ASSERT_CLEAR_DEFARRAY_SIMPLE \ + assert(av) + PERL_STATIC_INLINE I32 Perl_foldEQ(pTHX_ const char *a, const char *b, I32 len); # define PERL_ARGS_ASSERT_FOLDEQ \ @@ -9869,6 +9893,87 @@ Perl_newSV_type_mortal(pTHX_ const svtype type) __attribute__always_inline__; # define PERL_ARGS_ASSERT_NEWSV_TYPE_MORTAL +PERL_STATIC_INLINE void +Perl_pop_stackinfo(pTHX); +# define PERL_ARGS_ASSERT_POP_STACKINFO + +PERL_STATIC_INLINE void +Perl_push_stackinfo(pTHX_ I32 type, UV flags); +# define PERL_ARGS_ASSERT_PUSH_STACKINFO + +PERL_STATIC_INLINE void +Perl_rpp_extend(pTHX_ SSize_t n); +# define PERL_ARGS_ASSERT_RPP_EXTEND + +PERL_STATIC_INLINE bool +Perl_rpp_is_lone(pTHX_ SV *sv); +# define PERL_ARGS_ASSERT_RPP_IS_LONE \ + assert(sv) + +PERL_STATIC_INLINE SV * +Perl_rpp_pop_1_norc(pTHX); +# define PERL_ARGS_ASSERT_RPP_POP_1_NORC + +PERL_STATIC_INLINE void +Perl_rpp_popfree_1(pTHX); +# define PERL_ARGS_ASSERT_RPP_POPFREE_1 + +PERL_STATIC_INLINE void +Perl_rpp_popfree_2(pTHX); +# define PERL_ARGS_ASSERT_RPP_POPFREE_2 + +PERL_STATIC_INLINE void +Perl_rpp_popfree_to(pTHX_ SV **sp); +# define PERL_ARGS_ASSERT_RPP_POPFREE_TO \ + assert(sp) + +PERL_STATIC_INLINE void +Perl_rpp_push_1(pTHX_ SV *sv); +# define PERL_ARGS_ASSERT_RPP_PUSH_1 \ + assert(sv) + +PERL_STATIC_INLINE void +Perl_rpp_push_1_norc(pTHX_ SV *sv); +# define PERL_ARGS_ASSERT_RPP_PUSH_1_NORC \ + assert(sv) + +PERL_STATIC_INLINE void +Perl_rpp_push_2(pTHX_ SV *sv1, SV *sv2); +# define PERL_ARGS_ASSERT_RPP_PUSH_2 \ + assert(sv1); assert(sv2) + +PERL_STATIC_INLINE void +Perl_rpp_replace_1_1(pTHX_ SV *sv); +# define PERL_ARGS_ASSERT_RPP_REPLACE_1_1 \ + assert(sv) + +PERL_STATIC_INLINE void +Perl_rpp_replace_2_1(pTHX_ SV *sv); +# define PERL_ARGS_ASSERT_RPP_REPLACE_2_1 \ + assert(sv) + +PERL_STATIC_INLINE bool +Perl_rpp_stack_is_rc(pTHX); +# define PERL_ARGS_ASSERT_RPP_STACK_IS_RC + +PERL_STATIC_INLINE bool +Perl_rpp_try_AMAGIC_1(pTHX_ int method, int flags); +# define PERL_ARGS_ASSERT_RPP_TRY_AMAGIC_1 + +PERL_STATIC_INLINE bool +Perl_rpp_try_AMAGIC_2(pTHX_ int method, int flags); +# define PERL_ARGS_ASSERT_RPP_TRY_AMAGIC_2 + +PERL_STATIC_INLINE void +Perl_rpp_xpush_1(pTHX_ SV *sv); +# define PERL_ARGS_ASSERT_RPP_XPUSH_1 \ + assert(sv) + +PERL_STATIC_INLINE void +Perl_rpp_xpush_2(pTHX_ SV *sv1, SV *sv2); +# define PERL_ARGS_ASSERT_RPP_XPUSH_2 \ + assert(sv1); assert(sv2) + PERL_STATIC_INLINE unsigned Perl_single_1bit_pos32(U32 word) __attribute__warn_unused_result__; @@ -9894,6 +9999,11 @@ Perl_sv_setpv_freshbuf(pTHX_ SV * const sv); # define PERL_ARGS_ASSERT_SV_SETPV_FRESHBUF \ assert(sv) +PERL_STATIC_INLINE void +Perl_switch_argstack(pTHX_ AV *to); +# define PERL_ARGS_ASSERT_SWITCH_ARGSTACK \ + assert(to) + PERL_STATIC_INLINE IV Perl_utf8_distance(pTHX_ const U8 *a, const U8 *b) __attribute__warn_unused_result__; @@ -10094,6 +10204,18 @@ S_PerlEnv_putenv(pTHX_ char *str); # endif /* !defined(PERL_IMPLICIT_SYS) */ # endif /* defined(USE_ITHREADS) */ #endif /* !defined(PERL_NO_INLINE_FUNCTIONS) */ +#if defined(PERL_RC_STACK) +PERL_CALLCONV OP * +Perl_pp_wrap(pTHX_ Perl_ppaddr_t real_pp_fn, I32 nargs, int nlists); +# define PERL_ARGS_ASSERT_PP_WRAP \ + assert(real_pp_fn) + +PERL_CALLCONV void +Perl_xs_wrap(pTHX_ XSUBADDR_t xsub, CV *cv); +# define PERL_ARGS_ASSERT_XS_WRAP \ + assert(xsub); assert(cv) + +#endif /* defined(PERL_RC_STACK) */ #if defined(PERL_USE_3ARG_SIGHANDLER) PERL_CALLCONV Signal_t Perl_csighandler(int sig, Siginfo_t *info, void *uap); diff --git a/regen/op_private b/regen/op_private index 64effe496bf4..547879b62615 100644 --- a/regen/op_private +++ b/regen/op_private @@ -739,6 +739,8 @@ addbits('entereval', 3 => qw(OPpEVAL_BYTES BYTES ), 4 => qw(OPpEVAL_COPHH COPHH ), # Construct %^H from COP hints 5 => qw(OPpEVAL_RE_REPARSING REPARSE), # eval_sv(..., G_RE_REPARSING) + 6 => qw(OPpEVAL_EVALSV EVALSV ), # called from eval_sv() + ); diff --git a/run.c b/run.c index 352bfbc18ce1..7d11ae8b484b 100644 --- a/run.c +++ b/run.c @@ -47,6 +47,119 @@ Perl_runops_standard(pTHX) return 0; } + +#ifdef PERL_RC_STACK + +/* this is a wrapper for all runops-style functions. It temporarily + * reifies the stack if necessary, then calls the real runops function + */ +int +Perl_runops_wrap(pTHX) +{ + /* runops loops assume a ref-counted stack. If we have been called via a + * wrapper (pp_wrap or xs_wrap) with the top half of the stack not + * reference-counted, or with a non-real stack, temporarily convert it + * to reference-counted. This is because the si_stack_nonrc_base + * mechanism only allows a single split in the stack, not multiple + * stripes. + * At the end, we revert the stack (or part thereof) to non-refcounted + * to keep whoever our caller is happy. + * + * If what we call croaks, catch it, revert, then rethrow. + */ + + I32 cut; /* the cut point between refcnted and non-refcnted */ + bool was_real = cBOOL(AvREAL(PL_curstack)); + I32 old_base = PL_curstackinfo->si_stack_nonrc_base; + + if (was_real && !old_base) { + PL_runops(aTHX); /* call the real loop */ + return 0; + } + + if (was_real) { + cut = old_base; + assert(PL_stack_base + cut <= PL_stack_sp + 1); + PL_curstackinfo->si_stack_nonrc_base = 0; + } + else { + assert(!old_base); + assert(!AvREIFY(PL_curstack)); + AvREAL_on(PL_curstack); + /* skip the PL_sv_undef guard at PL_stack_base[0] but still + * signal adjusting may be needed on return by setting to a + * non-zero value - even if stack is empty */ + cut = 1; + } + + if (cut) { + SV **svp = PL_stack_base + cut; + while (svp <= PL_stack_sp) { + SvREFCNT_inc_simple_void(*svp); + svp++; + } + } + + AV * old_curstack = PL_curstack; + + /* run the real loop while catching exceptions */ + dJMPENV; + int ret; + JMPENV_PUSH(ret); + switch (ret) { + case 0: /* normal return from JMPENV_PUSH */ + cur_env.je_mustcatch = cur_env.je_prev->je_mustcatch; + PL_runops(aTHX); /* call the real loop */ + + revert: + /* revert stack back its non-ref-counted state */ + assert(AvREAL(PL_curstack)); + + if (cut) { + /* undo the stack reification that took place at the beginning of + * this function */ + if (UNLIKELY(!was_real)) + AvREAL_off(PL_curstack); + + SSize_t n = PL_stack_sp - (PL_stack_base + cut) + 1; + if (n > 0) { + /* we need to decrement the refcount of every SV from cut + * upwards; but this may prematurely free them, so + * mortalise them instead */ + EXTEND_MORTAL(n); + Copy(PL_stack_base + cut, PL_tmps_stack + PL_tmps_ix + 1, n, SV*); + PL_tmps_ix += n; + } + I32 sp1 = PL_stack_sp - PL_stack_base + 1; + PL_curstackinfo->si_stack_nonrc_base = + old_base > sp1 ? sp1 : old_base; + } + break; + + case 3: /* exception trapped by eval - stack only partially unwound */ + + /* if the exception has already unwound to before the current + * stack, no need to fix it up */ + if (old_curstack == PL_curstack) + goto revert; + break; + + default: + break; + } + + JMPENV_POP; + + if (ret) { + JMPENV_JUMP(ret); /* re-throw the exception */ + NOT_REACHED; /* NOTREACHED */ + } + + return 0; +} + +#endif + /* * ex: set ts=8 sts=4 sw=4 et: */ diff --git a/scope.c b/scope.c index 6cb1433d1c75..0d58bbfe7542 100644 --- a/scope.c +++ b/scope.c @@ -69,16 +69,31 @@ Perl_stack_grow(pTHX_ SV **sp, SV **p, SSize_t n) #define GROW(old) ((old) + 1) #endif +/* for backcomp */ PERL_SI * Perl_new_stackinfo(pTHX_ I32 stitems, I32 cxitems) +{ + return new_stackinfo_flags(stitems, cxitems, 0); +} + +/* current flag meanings: + * 1 make the new arg stack AvREAL + */ + +PERL_SI * +Perl_new_stackinfo_flags(pTHX_ I32 stitems, I32 cxitems, UV flags) { PERL_SI *si; Newx(si, 1, PERL_SI); si->si_stack = newAV(); - AvREAL_off(si->si_stack); + if (!(flags & 1)) + AvREAL_off(si->si_stack); av_extend(si->si_stack, stitems > 0 ? stitems-1 : 0); AvALLOC(si->si_stack)[0] = &PL_sv_undef; AvFILLp(si->si_stack) = 0; +#ifdef PERL_RC_STACK + si->si_stack_nonrc_base = 0; +#endif si->si_prev = 0; si->si_next = 0; si->si_cxmax = cxitems - 1; @@ -1481,6 +1496,14 @@ Perl_leave_scope(pTHX_ I32 base) break; case SAVEt_STACK_POS: /* Position on Perl stack */ +#ifdef PERL_RC_STACK + /* DAPM Jan 2023. I don't think this save type is used any + * more, but if some XS code uses it, fail it for now, as + * it's not clear to me what perl should be doing to stack ref + * counts when arbitrarily resetting the stack pointer. + */ + assert(0); +#endif a0 = ap[0]; PL_stack_sp = PL_stack_base + a0.any_i32; break; diff --git a/sv.c b/sv.c index a4290d92b3e4..2abc70eb218e 100644 --- a/sv.c +++ b/sv.c @@ -231,7 +231,10 @@ Public API: #ifdef DEBUG_LEAKING_SCALARS # define FREE_SV_DEBUG_FILE(sv) STMT_START { \ - if ((sv)->sv_debug_file) PerlMemShared_free((sv)->sv_debug_file); \ + if ((sv)->sv_debug_file) { \ + PerlMemShared_free((sv)->sv_debug_file); \ + sv->sv_debug_file = NULL; \ + } \ } STMT_END # define DEBUG_SV_SERIAL(sv) \ DEBUG_m(PerlIO_printf(Perl_debug_log, "0x%" UVxf ": (%05ld) del_SV\n", \ @@ -15073,6 +15076,9 @@ Perl_si_dup(pTHX_ PERL_SI *si, CLONE_PARAMS* param) nsi->si_prev = si_dup(si->si_prev, param); nsi->si_next = si_dup(si->si_next, param); nsi->si_markoff = si->si_markoff; +#ifdef PERL_RC_STACK + nsi->si_stack_nonrc_base = si->si_stack_nonrc_base; +#endif #if defined DEBUGGING && !defined DEBUGGING_RE_ONLY nsi->si_stack_hwm = 0; #endif @@ -16254,12 +16260,14 @@ perl_clone_using(PerlInterpreter *proto_perl, UV flags, HV* const stash = MUTABLE_HV(av_shift(param->stashes)); GV* const cloner = gv_fetchmethod_autoload(stash, "CLONE", 0); if (cloner && GvCV(cloner)) { - dSP; ENTER; SAVETMPS; - PUSHMARK(SP); - mXPUSHs(newSVhek(HvNAME_HEK(stash))); - PUTBACK; + PUSHMARK(PL_stack_sp); + rpp_extend(1); + SV *newsv = newSVhek(HvNAME_HEK(stash)); + *++PL_stack_sp = newsv; + if (!rpp_stack_is_rc()) + sv_2mortal(newsv); call_sv(MUTABLE_SV(GvCV(cloner)), G_DISCARD); FREETMPS; LEAVE; diff --git a/t/cmd/for.t b/t/cmd/for.t index a1ce6762cfc4..001d728782ec 100644 --- a/t/cmd/for.t +++ b/t/cmd/for.t @@ -73,9 +73,15 @@ for ("-3" .. "0") { print $loop_count == 4 ? "ok" : "not ok", " 12\n"; # modifying arrays in loops is a no-no +# - unless the stack is reference-counted @a = (3,4); eval { @a = () for (1,2,@a) }; -print $@ =~ /Use of freed value in iteration/ ? "ok" : "not ok", " 13\n"; +print $@ =~ ((Internals::stack_refcounted() & 1) + ? qr/^$/ + : qr/Use of freed value in iteration/ + ) + ? "ok" : "not ok", + " 13 - freed value in iteration\n"; # [perl #30061] double destory when same iterator variable (eg $_) used in # DESTROY as used in for loop that triggered the destroy diff --git a/t/op/for.t b/t/op/for.t index a4d80664d8dc..b931a66a6c0a 100644 --- a/t/op/for.t +++ b/t/op/for.t @@ -756,4 +756,20 @@ is(fscope(), 1, 'return via loop in sub'); } } +# the GV of the loop variable didn't have its refcount bumped while being +# used by the loop, so it was possible to free it mid-loop. This used to +# assert/SEGV + +{ + my $f = "a_low_refcnt_package_var"; + my $i = 0; + no strict 'refs'; + for ${*$f} (5,11,33) { + delete $main::{$f}; + $i++; + } + is($i, 3, "deleting glob is safe"); +} + + done_testing(); diff --git a/t/op/grep.t b/t/op/grep.t index 765fd6bf3815..8ab9a8aa4d87 100644 --- a/t/op/grep.t +++ b/t/op/grep.t @@ -10,7 +10,7 @@ BEGIN { set_up_inc( qw(. ../lib) ); } -plan( tests => 67 ); +plan( tests => 76 ); { my @lol = ([qw(a b c)], [], [qw(1 2 3)]); @@ -238,3 +238,43 @@ pass 'no double frees with grep/map { undef *_ }'; my @a = map { 1; "$_" } 1,2; is("@a", "1 2", "PADTMP"); } + + +package FOO { + my $count; + sub DESTROY { $count++ } + my @a; + + # check all grep arguments are immediately released + + $count = 0; + @a = (bless([]), bless([]), bless([])); + grep 1, @a; + ::is ($count, 0, "grep void pre"); + @a = (); + ::is ($count, 3, "grep void post"); + + $count = 0; + @a = (bless([]), bless([]), bless([])); + my $x = grep 1, @a; + ::is ($count, 0, "grep scalar pre"); + @a = (); + ::is ($count, 3, "grep scalar post"); + + $count = 0; + @a = (bless([]), bless([]), bless([])); + () = grep 1, @a; + ::is ($count, 0, "grep list pre"); + @a = (); + ::is ($count, 3, "grep list post"); + + # check check map expression results are immediately released + # in void context + + $count = 1; + map { + ::is ($count, 1, "block map void $_"); + $count = 0; + bless[]; + } 1,2,3; +} diff --git a/t/op/sort.t b/t/op/sort.t index 1a429f13c7b5..bffa6f6a6353 100644 --- a/t/op/sort.t +++ b/t/op/sort.t @@ -7,7 +7,7 @@ BEGIN { set_up_inc('../lib'); } use warnings; -plan(tests => 203); +plan(tests => 204); use Tie::Array; # we need to test sorting tied arrays # these shouldn't hang @@ -1231,3 +1231,8 @@ SKIP: eval 'my @s = (sort); 1'; like($@, qr/Not enough arguments for sort/, 'empty (sort); not allowed'); } + +# check that lexical sort subs are ok + +my sub lexcmp { $a <=> $b } +is join('', sort lexcmp 3,4,1,2), "1234", "lexical sort sub" ; diff --git a/t/op/sub.t b/t/op/sub.t index 09f5609b499a..aab5916e7854 100644 --- a/t/op/sub.t +++ b/t/op/sub.t @@ -6,7 +6,7 @@ BEGIN { set_up_inc('../lib'); } -plan(tests => 63); +plan(tests => 64); sub empty_sub {} @@ -433,3 +433,15 @@ fresh_perl_like( {}, "GH Issue #16944 - Syntax error with sub and shift causes segfault" ); + +# Bug 20010515.004 (#6998) +# freeing array used as args to sub + +fresh_perl_like( + q{my @h = 1 .. 10; bad(@h); sub bad { undef @h; warn "O\n"; print for @_; warn "K\n";}}, + (Internals::stack_refcounted() & 1) + ? qr/^O\nK/ + : qr/Use of freed value in iteration/, + {}, + "#6998 freeing array used as args to sub", +); diff --git a/t/run/fresh_perl.t b/t/run/fresh_perl.t index df7cebb80c74..e317b8a2f695 100644 --- a/t/run/fresh_perl.t +++ b/t/run/fresh_perl.t @@ -619,19 +619,6 @@ EXPECT -w "x" =~ /(\G?x)?/; ######## -# Bug 20010515.004 (#6998) -my @h = 1 .. 10; -bad(@h); -sub bad { - undef @h; - warn "O\n"; - print for @_; - warn "K\n"; -} -EXPECT -O -Use of freed value in iteration at - line 7. -######## # Bug 20010506.041 (#6952) "abcd\x{1234}" =~ /(a)(b[c])(d+)?/i and print "ok\n"; EXPECT diff --git a/toke.c b/toke.c index a4454fe010c8..550760e93297 100644 --- a/toke.c +++ b/toke.c @@ -4919,7 +4919,32 @@ Perl_filter_read(pTHX_ int idx, SV *buf_sv, int maxlen) /* Return: <0:error, =0:eof, >0:not eof */ ENTER; save_scalar(PL_errgv); + + /* although this calls out to a random C function, there's a good + * chance that that function will call back into perl (e.g. using + * Filter::Util::Call). So downgrade the stack to + * non-reference-counted for backwards compatibility - i.e. do the + * equivalent of xs_wrap(), but this time we know there are no + * args to be passed or returned on the stack, simplifying it. + */ +#ifdef PERL_RC_STACK + assert(AvREAL(PL_curstack)); + I32 oldbase = PL_curstackinfo->si_stack_nonrc_base; + I32 oldsp = PL_stack_sp - PL_stack_base; + if (!oldbase) + PL_curstackinfo->si_stack_nonrc_base = oldsp + 1; +#endif + ret = (*funcp)(aTHX_ idx, buf_sv, correct_length); + +#ifdef PERL_RC_STACK + assert(oldsp == PL_stack_sp - PL_stack_base); + assert(AvREAL(PL_curstack)); + assert(PL_curstackinfo->si_stack_nonrc_base == + oldbase ? oldbase : oldsp + 1); + PL_curstackinfo->si_stack_nonrc_base = oldbase; +#endif + LEAVE; return ret; } diff --git a/universal.c b/universal.c index 20a36fae8517..cd615af82a86 100644 --- a/universal.c +++ b/universal.c @@ -753,6 +753,20 @@ XS(XS_Internals_hv_clear_placehold) } } +XS(XS_Internals_stack_refcounted); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Internals_stack_refcounted) +{ + dXSARGS; + UV val = 0; + + if (items != 0) + croak_xs_usage(cv, ""); +#ifdef PERL_RC_STACK + val |= 1; +#endif + XSRETURN_UV(val); +} + XS(XS_PerlIO_get_layers); /* prototype to pass -Wmissing-prototypes */ XS(XS_PerlIO_get_layers) { @@ -1301,6 +1315,7 @@ static const struct xsub_details these_details[] = { {"Internals::SvREADONLY", XS_Internals_SvREADONLY, "\\[$%@];$", 0 }, {"Internals::SvREFCNT", XS_Internals_SvREFCNT, "\\[$%@];$", 0 }, {"Internals::hv_clear_placeholders", XS_Internals_hv_clear_placehold, "\\%", 0 }, + {"Internals::stack_refcounted", XS_Internals_stack_refcounted, NULL, 0 }, {"constant::_make_const", XS_constant__make_const, "\\[$@]", 0 }, {"PerlIO::get_layers", XS_PerlIO_get_layers, "*;@", 0 }, {"re::is_regexp", XS_re_is_regexp, "$", 0 },