X-Git-Url: http://git.vpit.fr/?p=perl%2Fmodules%2Fautovivification.git;a=blobdiff_plain;f=autovivification.xs;h=9884389bd99370dd398e8a812bfd88e6172d4bcf;hp=43922752149c3457518c1b0f65f8fc8c211e9c81;hb=aae04e46de5be0dc7e9abfc02035b6bb09aea6a7;hpb=aebbab9a783906dacf7815fec6b1da3f17ed3aca diff --git a/autovivification.xs b/autovivification.xs index 4392275..9884389 100644 --- a/autovivification.xs +++ b/autovivification.xs @@ -29,6 +29,10 @@ # define A_HAS_RPEEP A_HAS_PERL(5, 13, 5) #endif +#ifndef A_HAS_MULTIDEREF +# define A_HAS_MULTIDEREF A_HAS_PERL(5, 21, 7) +#endif + #ifndef OpSIBLING # ifdef OP_SIBLING # define OpSIBLING(O) OP_SIBLING(O) @@ -37,6 +41,49 @@ # endif #endif +/* ... Our vivify_ref() .................................................... */ + +/* Perl_vivify_ref() is not exported, so we have to reimplement it. */ + +#if A_HAS_MULTIDEREF + +static SV *a_vivify_ref(pTHX_ SV *sv, int to_hash) { +#define a_vivify_ref(S, TH) a_vivify_ref(aTHX_ (S), (TH)) + SvGETMAGIC(sv); + + if (!SvOK(sv)) { + SV *val; + + if (SvREADONLY(sv)) + Perl_croak_no_modify(); + + /* Inlined prepare_SV_for_RV() */ + if (SvTYPE(sv) < SVt_PV && SvTYPE(sv) != SVt_IV) { + sv_upgrade(sv, SVt_IV); + } else if (SvTYPE(sv) >= SVt_PV) { + SvPV_free(sv); + SvLEN_set(sv, 0); + SvCUR_set(sv, 0); + } + + val = to_hash ? MUTABLE_SV(newHV()) : MUTABLE_SV(newAV()); + SvRV_set(sv, val); + SvROK_on(sv); + SvSETMAGIC(sv); + SvGETMAGIC(sv); + } + + if (SvGMAGICAL(sv)) { + SV *msv = sv_newmortal(); + sv_setsv_nomg(msv, sv); + return msv; + } + + return sv; +} + +#endif /* A_HAS_MULTIDEREF */ + /* ... Thread safety and multiplicity ...................................... */ /* Always safe when the workaround isn't needed */ @@ -49,12 +96,17 @@ #endif #ifndef A_MULTIPLICITY -# if defined(MULTIPLICITY) || defined(PERL_IMPLICIT_CONTEXT) +# if defined(MULTIPLICITY) # define A_MULTIPLICITY 1 # else # define A_MULTIPLICITY 0 # endif #endif +#if A_MULTIPLICITY +# ifndef PERL_IMPLICIT_CONTEXT +# error MULTIPLICITY builds must set PERL_IMPLICIT_CONTEXT +# endif +#endif #ifndef tTHX # define tTHX PerlInterpreter* @@ -83,12 +135,33 @@ # define MY_CXT_CLONE NOOP #endif +#if A_THREADSAFE +/* We must use preexistent global mutexes or we will never be able to destroy + * them. */ +# if A_HAS_PERL(5, 9, 3) +# define A_LOADED_LOCK MUTEX_LOCK(&PL_my_ctx_mutex) +# define A_LOADED_UNLOCK MUTEX_UNLOCK(&PL_my_ctx_mutex) +# else +# define A_LOADED_LOCK OP_REFCNT_LOCK +# define A_LOADED_UNLOCK OP_REFCNT_UNLOCK +# endif +#else +# define A_LOADED_LOCK NOOP +# define A_LOADED_UNLOCK NOOP +#endif + #if defined(OP_CHECK_MUTEX_LOCK) && defined(OP_CHECK_MUTEX_UNLOCK) -# define A_CHECK_MUTEX_LOCK OP_CHECK_MUTEX_LOCK -# define A_CHECK_MUTEX_UNLOCK OP_CHECK_MUTEX_UNLOCK +# define A_CHECK_LOCK OP_CHECK_MUTEX_LOCK +# define A_CHECK_UNLOCK OP_CHECK_MUTEX_UNLOCK +#elif A_HAS_PERL(5, 9, 3) +# define A_CHECK_LOCK OP_REFCNT_LOCK +# define A_CHECK_UNLOCK OP_REFCNT_UNLOCK #else -# define A_CHECK_MUTEX_LOCK OP_REFCNT_LOCK -# define A_CHECK_MUTEX_UNLOCK OP_REFCNT_UNLOCK +/* Before perl 5.9.3, indirect_ck_*() calls are already protected by the + * A_LOADED mutex, which falls back to the OP_REFCNT mutex. Make sure we don't + * lock it twice. */ +# define A_CHECK_LOCK NOOP +# define A_CHECK_UNLOCK NOOP #endif typedef OP *(*a_ck_t)(pTHX_ OP *); @@ -101,28 +174,102 @@ typedef OP *(*a_ck_t)(pTHX_ OP *); static void a_ck_replace(pTHX_ OPCODE type, a_ck_t new_ck, a_ck_t *old_ck_p) { #define a_ck_replace(T, NC, OCP) a_ck_replace(aTHX_ (T), (NC), (OCP)) - A_CHECK_MUTEX_LOCK; + A_CHECK_LOCK; if (!*old_ck_p) { *old_ck_p = PL_check[type]; PL_check[type] = new_ck; } - A_CHECK_MUTEX_UNLOCK; + A_CHECK_UNLOCK; } #endif static void a_ck_restore(pTHX_ OPCODE type, a_ck_t *old_ck_p) { #define a_ck_restore(T, OCP) a_ck_restore(aTHX_ (T), (OCP)) - A_CHECK_MUTEX_LOCK; + A_CHECK_LOCK; if (*old_ck_p) { PL_check[type] = *old_ck_p; *old_ck_p = 0; } - A_CHECK_MUTEX_UNLOCK; + A_CHECK_UNLOCK; } /* --- Helpers ------------------------------------------------------------- */ +/* ... Check if the module is loaded ....................................... */ + +static I32 a_loaded = 0; + +#if A_THREADSAFE + +#define PTABLE_NAME ptable_loaded +#define PTABLE_NEED_DELETE 1 +#define PTABLE_NEED_WALK 0 + +#include "ptable.h" + +#define ptable_loaded_store(T, K, V) ptable_loaded_store(aPTBLMS_ (T), (K), (V)) +#define ptable_loaded_delete(T, K) ptable_loaded_delete(aPTBLMS_ (T), (K)) +#define ptable_loaded_free(T) ptable_loaded_free(aPTBLMS_ (T)) + +static ptable *a_loaded_cxts = NULL; + +static int a_is_loaded(pTHX_ void *cxt) { +#define a_is_loaded(C) a_is_loaded(aTHX_ (C)) + int res = 0; + + A_LOADED_LOCK; + if (a_loaded_cxts && ptable_fetch(a_loaded_cxts, cxt)) + res = 1; + A_LOADED_UNLOCK; + + return res; +} + +static int a_set_loaded_locked(pTHX_ void *cxt) { +#define a_set_loaded_locked(C) a_set_loaded_locked(aTHX_ (C)) + int global_setup = 0; + + if (a_loaded <= 0) { + assert(a_loaded == 0); + assert(!a_loaded_cxts); + a_loaded_cxts = ptable_new(); + global_setup = 1; + } + ++a_loaded; + assert(a_loaded_cxts); + ptable_loaded_store(a_loaded_cxts, cxt, cxt); + + return global_setup; +} + +static int a_clear_loaded_locked(pTHX_ void *cxt) { +#define a_clear_loaded_locked(C) a_clear_loaded_locked(aTHX_ (C)) + int global_teardown = 0; + + if (a_loaded > 1) { + assert(a_loaded_cxts); + ptable_loaded_delete(a_loaded_cxts, cxt); + --a_loaded; + } else if (a_loaded_cxts) { + assert(a_loaded == 1); + ptable_loaded_free(a_loaded_cxts); + a_loaded_cxts = NULL; + a_loaded = 0; + global_teardown = 1; + } + + return global_teardown; +} + +#else + +#define a_is_loaded(C) (a_loaded > 0) +#define a_set_loaded_locked(C) ((a_loaded++ <= 0) ? 1 : 0) +#define a_clear_loaded_locked(C) ((--a_loaded <= 0) ? 1 : 0) + +#endif + /* ... Thread-safe hints ................................................... */ #if A_WORKAROUND_REQUIRE_PROPAGATION @@ -138,6 +285,8 @@ typedef struct { #define PTABLE_NAME ptable_hints #define PTABLE_VAL_FREE(V) A_HINT_FREE(V) +#define PTABLE_NEED_DELETE 0 +#define PTABLE_NEED_WALK 1 #define pPTBL pTHX #define pPTBL_ pTHX_ @@ -154,7 +303,8 @@ typedef struct { #endif /* A_WORKAROUND_REQUIRE_PROPAGATION */ #define PTABLE_NAME ptable_seen -#define PTABLE_VAL_FREE(V) NOOP +#define PTABLE_NEED_DELETE 0 +#define PTABLE_NEED_WALK 0 #include "ptable.h" @@ -166,19 +316,20 @@ typedef struct { #define MY_CXT_KEY __PACKAGE__ "::_guts" XS_VERSION typedef struct { + peep_t old_peep; /* This is actually the rpeep past 5.13.5 */ + ptable *seen; /* It really is a ptable_seen */ #if A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION - ptable *tbl; /* It really is a ptable_hints */ + ptable *tbl; /* It really is a ptable_hints */ tTHX owner; #endif /* A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION */ - ptable *seen; /* It really is a ptable_seen */ } my_cxt_t; START_MY_CXT -#if A_THREADSAFE - #if A_WORKAROUND_REQUIRE_PROPAGATION +#if A_THREADSAFE + typedef struct { ptable *tbl; #if A_HAS_PERL(5, 13, 2) @@ -216,46 +367,8 @@ static void a_ptable_clone(pTHX_ ptable_ent *ent, void *ud_) { ptable_hints_store(ud->tbl, ent->key, h2); } -#endif /* A_WORKAROUND_REQUIRE_PROPAGATION */ - -static void a_thread_cleanup(pTHX_ void *ud) { - dMY_CXT; - -#if A_WORKAROUND_REQUIRE_PROPAGATION - ptable_hints_free(MY_CXT.tbl); - MY_CXT.tbl = NULL; -#endif /* A_WORKAROUND_REQUIRE_PROPAGATION */ - ptable_seen_free(MY_CXT.seen); - MY_CXT.seen = NULL; -} - -static int a_endav_free(pTHX_ SV *sv, MAGIC *mg) { - SAVEDESTRUCTOR_X(a_thread_cleanup, NULL); - - return 0; -} - -static MGVTBL a_endav_vtbl = { - 0, - 0, - 0, - 0, - a_endav_free -#if MGf_COPY - , 0 -#endif -#if MGf_DUP - , 0 -#endif -#if MGf_LOCAL - , 0 -#endif -}; - #endif /* A_THREADSAFE */ -#if A_WORKAROUND_REQUIRE_PROPAGATION - static IV a_require_tag(pTHX) { #define a_require_tag() a_require_tag(aTHX) const CV *cv, *outside; @@ -381,7 +494,7 @@ static UV a_detag(pTHX_ const SV *hint) { #define A_HINT_ROOT 64 #define A_HINT_DEREF 128 -static U32 a_hash = 0; +static VOL U32 a_hash = 0; static UV a_hint(pTHX) { #define a_hint() a_hint(aTHX) @@ -413,12 +526,15 @@ typedef struct { #define PTABLE_NAME ptable_map #define PTABLE_VAL_FREE(V) PerlMemShared_free(V) +#define PTABLE_NEED_DELETE 1 +#define PTABLE_NEED_WALK 0 #include "ptable.h" /* PerlMemShared_free() needs the [ap]PTBLMS_? default values */ #define ptable_map_store(T, K, V) ptable_map_store(aPTBLMS_ (T), (K), (V)) #define ptable_map_delete(T, K) ptable_map_delete(aPTBLMS_ (T), (K)) +#define ptable_map_free(T) ptable_map_free(aPTBLMS_ (T)) static ptable *a_op_map = NULL; @@ -710,6 +826,16 @@ static OP *a_pp_rv2hv(pTHX) { /* ... pp_deref (aelem,helem,rv2sv,padsv) .................................. */ +static void a_cannot_vivify(pTHX_ UV flags) { +#define a_cannot_vivify(F) a_cannot_vivify(aTHX_ (F)) + if (flags & A_HINT_STRICT) + croak("Reference vivification forbidden"); + else if (flags & A_HINT_WARN) + warn("Reference was vivified"); + else /* A_HINT_STORE */ + croak("Can't vivify reference"); +} + static OP *a_pp_deref(pTHX) { dA_MAP_THX; const a_op_info *oi; @@ -726,14 +852,8 @@ static OP *a_pp_deref(pTHX) { if (flags & (A_HINT_NOTIFY|A_HINT_STORE)) { SPAGAIN; - if (a_undef(TOPs)) { - if (flags & A_HINT_STRICT) - croak("Reference vivification forbidden"); - else if (flags & A_HINT_WARN) - warn("Reference was vivified"); - else /* A_HINT_STORE */ - croak("Can't vivify reference"); - } + if (a_undef(TOPs)) + a_cannot_vivify(flags); } return o; @@ -783,6 +903,315 @@ static OP *a_pp_root_binop(pTHX) { } } +#if A_HAS_MULTIDEREF + +/* ... pp_multideref ....................................................... */ + +/* This pp replacement is actually only called for topmost exists/delete ops, + * because we hijack the [ah]elem check functions and this disables the + * optimization for lvalue and rvalue dereferencing. In particular, the + * OPf_MOD branches should never be covered. In the future, the multideref + * optimization might also be disabled for custom exists/delete check functions, + * which will make this section unnecessary. However, the code tries to be as + * general as possible in case I think of a way to reenable the multideref + * optimization even when this module is in use. */ + +static UV a_do_multideref(const OP *o, UV flags) { + UV isexdel, other_flags; + + assert(o->op_type == OP_MULTIDEREF); + + other_flags = flags & ~A_HINT_DO; + + isexdel = o->op_private & (OPpMULTIDEREF_EXISTS|OPpMULTIDEREF_DELETE); + if (isexdel) { + if (isexdel & OPpMULTIDEREF_EXISTS) { + flags &= A_HINT_EXISTS; + } else { + flags &= A_HINT_DELETE; + } + } else { + if (o->op_flags & OPf_MOD) { + flags &= A_HINT_STORE; + } else { + flags &= A_HINT_FETCH; + } + } + + return flags ? (flags | other_flags) : 0; +} + +static SV *a_do_fake_pp(pTHX_ OP *op) { +#define a_do_fake_pp(O) a_do_fake_pp(aTHX_ (O)) + { + OP *o = PL_op; + ENTER; + SAVEOP(); + PL_op = op; + PL_op->op_ppaddr(aTHX); + PL_op = o; + LEAVE; + } + + { + SV *ret; + dSP; + ret = POPs; + PUTBACK; + return ret; + } +} + +static void a_do_fake_pp_unop_init(pTHX_ UNOP *unop, U32 type, U32 flags) { +#define a_do_fake_pp_unop_init(O, T, F) a_do_fake_pp_unop_init(aTHX_ (O), (T), (F)) + unop->op_type = type; + unop->op_flags = OPf_WANT_SCALAR | (~OPf_WANT & flags); + unop->op_private = 0; + unop->op_first = NULL; + unop->op_ppaddr = PL_ppaddr[type]; +} + +static SV *a_do_fake_pp_unop_arg1(pTHX_ U32 type, U32 flags, SV *arg) { +#define a_do_fake_pp_unop_arg1(T, F, A) a_do_fake_pp_unop_arg1(aTHX_ (T), (F), (A)) + UNOP unop; + dSP; + + a_do_fake_pp_unop_init(&unop, type, flags); + + EXTEND(SP, 1); + PUSHs(arg); + PUTBACK; + + return a_do_fake_pp((OP *) &unop); +} + +static SV *a_do_fake_pp_unop_arg2(pTHX_ U32 type, U32 flags, SV *arg1, SV *arg2) { +#define a_do_fake_pp_unop_arg2(T, F, A1, A2) a_do_fake_pp_unop_arg2(aTHX_ (T), (F), (A1), (A2)) + UNOP unop; + dSP; + + a_do_fake_pp_unop_init(&unop, type, flags); + + EXTEND(SP, 2); + PUSHs(arg1); + PUSHs(arg2); + PUTBACK; + + return a_do_fake_pp((OP *) &unop); +} + +#define a_do_pp_rv2av(R) a_do_fake_pp_unop_arg1(OP_RV2AV, OPf_REF, (R)) +#define a_do_pp_afetch(A, I) a_do_fake_pp_unop_arg2(OP_AELEM, 0, (A), (I)) +#define a_do_pp_afetch_lv(A, I) a_do_fake_pp_unop_arg2(OP_AELEM, OPf_MOD, (A), (I)) +#define a_do_pp_aexists(A, I) a_do_fake_pp_unop_arg2(OP_EXISTS, OPf_SPECIAL, (A), (I)) +#define a_do_pp_adelete(A, I) a_do_fake_pp_unop_arg2(OP_DELETE, OPf_SPECIAL, (A), (I)) + +#define a_do_pp_rv2hv(R) a_do_fake_pp_unop_arg1(OP_RV2HV, OPf_REF, (R)) +#define a_do_pp_hfetch(H, K) a_do_fake_pp_unop_arg2(OP_HELEM, 0, (H), (K)) +#define a_do_pp_hfetch_lv(H, K) a_do_fake_pp_unop_arg2(OP_HELEM, OPf_MOD, (H), (K)) +#define a_do_pp_hexists(H, K) a_do_fake_pp_unop_arg2(OP_EXISTS, 0, (H), (K)) +#define a_do_pp_hdelete(H, K) a_do_fake_pp_unop_arg2(OP_DELETE, 0, (H), (K)) + +static OP *a_pp_multideref(pTHX) { + UNOP_AUX_item *items; + UV actions; + UV flags = 0; + SV *sv = NULL; + dSP; + + { + dA_MAP_THX; + const a_op_info *oi = a_map_fetch(PL_op); + assert(oi); + flags = a_do_multideref(PL_op, oi->flags); + if (!flags) + return oi->old_pp(aTHX); + } + + items = cUNOP_AUXx(PL_op)->op_aux; + actions = items->uv; + + PL_multideref_pc = items; + + while (1) { + switch (actions & MDEREF_ACTION_MASK) { + case MDEREF_reload: + actions = (++items)->uv; + continue; + case MDEREF_AV_padav_aelem: /* $lex[...] */ + sv = PAD_SVl((++items)->pad_offset); + if (a_undef(sv)) + goto ret_undef; + goto do_AV_aelem; + case MDEREF_AV_gvav_aelem: /* $pkg[...] */ + sv = UNOP_AUX_item_sv(++items); + assert(isGV_with_GP(sv)); + sv = (SV *) GvAVn((GV *) sv); + if (a_undef(sv)) + goto ret_undef; + goto do_AV_aelem; + case MDEREF_AV_pop_rv2av_aelem: /* expr->[...] */ + sv = POPs; + if (a_undef(sv)) + goto ret_undef; + goto do_AV_rv2av_aelem; + case MDEREF_AV_gvsv_vivify_rv2av_aelem: /* $pkg->[...] */ + sv = UNOP_AUX_item_sv(++items); + assert(isGV_with_GP(sv)); + sv = GvSVn((GV *) sv); + if (a_undef(sv)) + goto ret_undef; + goto do_AV_vivify_rv2av_aelem; + case MDEREF_AV_padsv_vivify_rv2av_aelem: /* $lex->[...] */ + sv = PAD_SVl((++items)->pad_offset); + /* FALLTHROUGH */ + case MDEREF_AV_vivify_rv2av_aelem: /* vivify, ->[...] */ + if (a_undef(sv)) + goto ret_undef; +do_AV_vivify_rv2av_aelem: + sv = a_vivify_ref(sv, 0); +do_AV_rv2av_aelem: + sv = a_do_pp_rv2av(sv); +do_AV_aelem: + { + SV *esv; + assert(SvTYPE(sv) == SVt_PVAV); + switch (actions & MDEREF_INDEX_MASK) { + case MDEREF_INDEX_none: + goto finish; + case MDEREF_INDEX_const: + esv = sv_2mortal(newSViv((++items)->iv)); + break; + case MDEREF_INDEX_padsv: + esv = PAD_SVl((++items)->pad_offset); + goto check_elem; + case MDEREF_INDEX_gvsv: + esv = UNOP_AUX_item_sv(++items); + assert(isGV_with_GP(esv)); + esv = GvSVn((GV *) esv); +check_elem: + if (UNLIKELY(SvROK(esv) && !SvGAMAGIC(esv) && ckWARN(WARN_MISC))) + Perl_warner(aTHX_ packWARN(WARN_MISC), + "Use of reference \"%"SVf"\" as array index", + SVfARG(esv)); + break; + } + PL_multideref_pc = items; + if (actions & MDEREF_FLAG_last) { + switch (flags & A_HINT_DO) { + case A_HINT_FETCH: + sv = a_do_pp_afetch(sv, esv); + break; + case A_HINT_STORE: + sv = a_do_pp_afetch_lv(sv, esv); + break; + case A_HINT_EXISTS: + sv = a_do_pp_aexists(sv, esv); + break; + case A_HINT_DELETE: + sv = a_do_pp_adelete(sv, esv); + break; + } + goto finish; + } + sv = a_do_pp_afetch(sv, esv); + break; + } + case MDEREF_HV_padhv_helem: /* $lex{...} */ + sv = PAD_SVl((++items)->pad_offset); + if (a_undef(sv)) + goto ret_undef; + goto do_HV_helem; + case MDEREF_HV_gvhv_helem: /* $pkg{...} */ + sv = UNOP_AUX_item_sv(++items); + assert(isGV_with_GP(sv)); + sv = (SV *) GvHVn((GV *) sv); + if (a_undef(sv)) + goto ret_undef; + goto do_HV_helem; + case MDEREF_HV_pop_rv2hv_helem: /* expr->{...} */ + sv = POPs; + if (a_undef(sv)) + goto ret_undef; + goto do_HV_rv2hv_helem; + case MDEREF_HV_gvsv_vivify_rv2hv_helem: /* $pkg->{...} */ + sv = UNOP_AUX_item_sv(++items); + assert(isGV_with_GP(sv)); + sv = GvSVn((GV *) sv); + if (a_undef(sv)) + goto ret_undef; + goto do_HV_vivify_rv2hv_helem; + case MDEREF_HV_padsv_vivify_rv2hv_helem: /* $lex->{...} */ + sv = PAD_SVl((++items)->pad_offset); + /* FALLTHROUGH */ + case MDEREF_HV_vivify_rv2hv_helem: /* vivify, ->{...} */ + if (a_undef(sv)) + goto ret_undef; +do_HV_vivify_rv2hv_helem: + sv = a_vivify_ref(sv, 1); +do_HV_rv2hv_helem: + sv = a_do_pp_rv2hv(sv); +do_HV_helem: + { + SV *key; + assert(SvTYPE(sv) == SVt_PVHV); + switch (actions & MDEREF_INDEX_MASK) { + case MDEREF_INDEX_none: + goto finish; + case MDEREF_INDEX_const: + key = UNOP_AUX_item_sv(++items); + break; + case MDEREF_INDEX_padsv: + key = PAD_SVl((++items)->pad_offset); + break; + case MDEREF_INDEX_gvsv: + key = UNOP_AUX_item_sv(++items); + assert(isGV_with_GP(key)); + key = GvSVn((GV *) key); + break; + } + PL_multideref_pc = items; + if (actions & MDEREF_FLAG_last) { + switch (flags & A_HINT_DO) { + case A_HINT_FETCH: + sv = a_do_pp_hfetch(sv, key); + break; + case A_HINT_STORE: + sv = a_do_pp_hfetch_lv(sv, key); + break; + case A_HINT_EXISTS: + sv = a_do_pp_hexists(sv, key); + break; + case A_HINT_DELETE: + sv = a_do_pp_hdelete(sv, key); + break; + default: + break; + } + goto finish; + } + sv = a_do_pp_hfetch(sv, key); + break; + } + } + + actions >>= MDEREF_SHIFT; + } + +ret_undef: + if (flags & (A_HINT_NOTIFY|A_HINT_STORE)) + a_cannot_vivify(flags); + if (flags & A_HINT_EXISTS) + sv = &PL_sv_no; + else + sv = &PL_sv_undef; +finish: + XPUSHs(sv); + RETURN; +} + +#endif /* A_HAS_MULTIDEREF */ + /* --- Check functions ----------------------------------------------------- */ static void a_recheck_rv2xv(pTHX_ OP *o, OPCODE type, OP *(*new_pp)(pTHX)) { @@ -873,6 +1302,18 @@ static OP *a_ck_deref(pTHX_ OP *o) { } o = old_ck(aTHX_ o); +#if A_HAS_MULTIDEREF + if (old_ck == a_old_ck_rv2sv && o->op_flags & OPf_KIDS) { + OP *kid = cUNOPo->op_first; + if (kid && kid->op_type == OP_GV) { + if (hint & A_HINT_DO) + a_map_store(kid, kid->op_ppaddr, NULL, hint); + else + a_map_delete(kid); + } + } +#endif + if (hint & A_HINT_DO) { a_map_store_root(o, o->op_ppaddr, hint); o->op_ppaddr = a_pp_deref; @@ -1006,8 +1447,6 @@ static OP *a_ck_root(pTHX_ OP *o) { /* ... Our peephole optimizer .............................................. */ -static peep_t a_old_peep = 0; /* This is actually the rpeep past 5.13.5 */ - static void a_peep_rec(pTHX_ OP *o, ptable *seen); static void a_peep_rec(pTHX_ OP *o, ptable *seen) { @@ -1076,6 +1515,20 @@ static void a_peep_rec(pTHX_ OP *o, ptable *seen) { if (!(oi->flags & A_HINT_DEREF)) o->op_ppaddr = oi->old_pp; break; +#if A_HAS_MULTIDEREF + case OP_MULTIDEREF: + if (o->op_ppaddr != a_pp_multideref) { + oi = a_map_fetch(cUNOPo->op_first); + if (!oi) + break; + flags = oi->flags; + if (a_do_multideref(o, flags)) { + a_map_store_root(o, o->op_ppaddr, flags & ~A_HINT_DEREF); + o->op_ppaddr = a_pp_multideref; + } + } + break; +#endif #if !A_HAS_RPEEP case OP_MAPWHILE: case OP_GREPWHILE: @@ -1117,11 +1570,14 @@ static void a_peep_rec(pTHX_ OP *o, ptable *seen) { } static void a_peep(pTHX_ OP *o) { + ptable *seen; dMY_CXT; - ptable *seen = MY_CXT.seen; - a_old_peep(aTHX_ o); + assert(a_is_loaded(&MY_CXT)); + + MY_CXT.old_peep(aTHX_ o); + seen = MY_CXT.seen; if (seen) { ptable_seen_clear(seen); a_peep_rec(o); @@ -1129,127 +1585,101 @@ static void a_peep(pTHX_ OP *o) { } } -/* --- Interpreter setup/teardown ------------------------------------------ */ - -static U32 a_initialized = 0; +/* --- Module setup/teardown ----------------------------------------------- */ static void a_teardown(pTHX_ void *root) { + dMY_CXT; - if (!a_initialized) - return; + A_LOADED_LOCK; -#if A_MULTIPLICITY - if (aTHX != root) - return; -#endif + if (a_clear_loaded_locked(&MY_CXT)) { + a_ck_restore(OP_PADANY, &a_old_ck_padany); + a_ck_restore(OP_PADSV, &a_old_ck_padsv); - { - dMY_CXT; -# if A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION - ptable_hints_free(MY_CXT.tbl); - MY_CXT.tbl = NULL; -# endif /* A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION */ - ptable_seen_free(MY_CXT.seen); - MY_CXT.seen = NULL; - } + a_ck_restore(OP_AELEM, &a_old_ck_aelem); + a_ck_restore(OP_HELEM, &a_old_ck_helem); + a_ck_restore(OP_RV2SV, &a_old_ck_rv2sv); - a_ck_restore(OP_PADANY, &a_old_ck_padany); - a_ck_restore(OP_PADSV, &a_old_ck_padsv); + a_ck_restore(OP_RV2AV, &a_old_ck_rv2av); + a_ck_restore(OP_RV2HV, &a_old_ck_rv2hv); - a_ck_restore(OP_AELEM, &a_old_ck_aelem); - a_ck_restore(OP_HELEM, &a_old_ck_helem); - a_ck_restore(OP_RV2SV, &a_old_ck_rv2sv); + a_ck_restore(OP_ASLICE, &a_old_ck_aslice); + a_ck_restore(OP_HSLICE, &a_old_ck_hslice); - a_ck_restore(OP_RV2AV, &a_old_ck_rv2av); - a_ck_restore(OP_RV2HV, &a_old_ck_rv2hv); + a_ck_restore(OP_EXISTS, &a_old_ck_exists); + a_ck_restore(OP_DELETE, &a_old_ck_delete); + a_ck_restore(OP_KEYS, &a_old_ck_keys); + a_ck_restore(OP_VALUES, &a_old_ck_values); - a_ck_restore(OP_ASLICE, &a_old_ck_aslice); - a_ck_restore(OP_HSLICE, &a_old_ck_hslice); + ptable_map_free(a_op_map); + a_op_map = NULL; + +#ifdef USE_ITHREADS + MUTEX_DESTROY(&a_op_map_mutex); +#endif + } - a_ck_restore(OP_EXISTS, &a_old_ck_exists); - a_ck_restore(OP_DELETE, &a_old_ck_delete); - a_ck_restore(OP_KEYS, &a_old_ck_keys); - a_ck_restore(OP_VALUES, &a_old_ck_values); + A_LOADED_UNLOCK; + if (MY_CXT.old_peep) { #if A_HAS_RPEEP - PL_rpeepp = a_old_peep; + PL_rpeepp = MY_CXT.old_peep; #else - PL_peepp = a_old_peep; + PL_peepp = MY_CXT.old_peep; #endif - a_old_peep = 0; + MY_CXT.old_peep = 0; + } + + ptable_seen_free(MY_CXT.seen); + MY_CXT.seen = NULL; - a_initialized = 0; +#if A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION + ptable_hints_free(MY_CXT.tbl); + MY_CXT.tbl = NULL; +#endif /* A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION */ + + return; } static void a_setup(pTHX) { #define a_setup() a_setup(aTHX) - if (a_initialized) - return; + MY_CXT_INIT; /* Takes/release PL_my_ctx_mutex */ - { - MY_CXT_INIT; -# if A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION - MY_CXT.tbl = ptable_new(); - MY_CXT.owner = aTHX; -# endif /* A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION */ - MY_CXT.seen = ptable_new(); - } + A_LOADED_LOCK; - a_ck_replace(OP_PADANY, a_ck_padany, &a_old_ck_padany); - a_ck_replace(OP_PADSV, a_ck_padsv, &a_old_ck_padsv); - - a_ck_replace(OP_AELEM, a_ck_deref, &a_old_ck_aelem); - a_ck_replace(OP_HELEM, a_ck_deref, &a_old_ck_helem); - a_ck_replace(OP_RV2SV, a_ck_deref, &a_old_ck_rv2sv); - - a_ck_replace(OP_RV2AV, a_ck_rv2xv, &a_old_ck_rv2av); - a_ck_replace(OP_RV2HV, a_ck_rv2xv, &a_old_ck_rv2hv); - - a_ck_replace(OP_ASLICE, a_ck_xslice, &a_old_ck_aslice); - a_ck_replace(OP_HSLICE, a_ck_xslice, &a_old_ck_hslice); + if (a_set_loaded_locked(&MY_CXT)) { + PERL_HASH(a_hash, __PACKAGE__, __PACKAGE_LEN__); - a_ck_replace(OP_EXISTS, a_ck_root, &a_old_ck_exists); - a_ck_replace(OP_DELETE, a_ck_root, &a_old_ck_delete); - a_ck_replace(OP_KEYS, a_ck_root, &a_old_ck_keys); - a_ck_replace(OP_VALUES, a_ck_root, &a_old_ck_values); + a_op_map = ptable_new(); -#if A_HAS_RPEEP - a_old_peep = PL_rpeepp; - PL_rpeepp = a_peep; -#else - a_old_peep = PL_peepp; - PL_peepp = a_peep; +#ifdef USE_ITHREADS + MUTEX_INIT(&a_op_map_mutex); #endif -#if A_MULTIPLICITY - call_atexit(a_teardown, aTHX); -#else - call_atexit(a_teardown, NULL); -#endif + a_ck_replace(OP_PADANY, a_ck_padany, &a_old_ck_padany); + a_ck_replace(OP_PADSV, a_ck_padsv, &a_old_ck_padsv); - a_initialized = 1; -} + a_ck_replace(OP_AELEM, a_ck_deref, &a_old_ck_aelem); + a_ck_replace(OP_HELEM, a_ck_deref, &a_old_ck_helem); + a_ck_replace(OP_RV2SV, a_ck_deref, &a_old_ck_rv2sv); -static U32 a_booted = 0; + a_ck_replace(OP_RV2AV, a_ck_rv2xv, &a_old_ck_rv2av); + a_ck_replace(OP_RV2HV, a_ck_rv2xv, &a_old_ck_rv2hv); -/* --- XS ------------------------------------------------------------------ */ + a_ck_replace(OP_ASLICE, a_ck_xslice, &a_old_ck_aslice); + a_ck_replace(OP_HSLICE, a_ck_xslice, &a_old_ck_hslice); -MODULE = autovivification PACKAGE = autovivification + a_ck_replace(OP_EXISTS, a_ck_root, &a_old_ck_exists); + a_ck_replace(OP_DELETE, a_ck_root, &a_old_ck_delete); + a_ck_replace(OP_KEYS, a_ck_root, &a_old_ck_keys); + a_ck_replace(OP_VALUES, a_ck_root, &a_old_ck_values); + } -PROTOTYPES: ENABLE + A_LOADED_UNLOCK; -BOOT: -{ - if (!a_booted++) { + { HV *stash; - a_op_map = ptable_new(); -#ifdef USE_ITHREADS - MUTEX_INIT(&a_op_map_mutex); -#endif - - PERL_HASH(a_hash, __PACKAGE__, __PACKAGE_LEN__); - stash = gv_stashpvn(__PACKAGE__, __PACKAGE_LEN__, 1); newCONSTSUB(stash, "A_HINT_STRICT", newSVuv(A_HINT_STRICT)); newCONSTSUB(stash, "A_HINT_WARN", newSVuv(A_HINT_WARN)); @@ -1262,6 +1692,41 @@ BOOT: newCONSTSUB(stash, "A_FORKSAFE", newSVuv(A_FORKSAFE)); } +#if A_HAS_RPEEP + if (PL_rpeepp != a_peep) { + MY_CXT.old_peep = PL_rpeepp; + PL_rpeepp = a_peep; + } +#else + if (PL_peepp != a_peep) { + MY_CXT.old_peep = PL_peepp; + PL_peepp = a_peep; + } +#endif + else { + MY_CXT.old_peep = 0; + } + + MY_CXT.seen = ptable_new(); + +#if A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION + MY_CXT.tbl = ptable_new(); + MY_CXT.owner = aTHX; +#endif /* A_THREADSAFE && A_WORKAROUND_REQUIRE_PROPAGATION */ + + call_atexit(a_teardown, NULL); + + return; +} + +/* --- XS ------------------------------------------------------------------ */ + +MODULE = autovivification PACKAGE = autovivification + +PROTOTYPES: ENABLE + +BOOT: +{ a_setup(); } @@ -1274,50 +1739,34 @@ PREINIT: #if A_WORKAROUND_REQUIRE_PROPAGATION ptable *t; #endif - ptable *s; - GV *gv; PPCODE: - { #if A_WORKAROUND_REQUIRE_PROPAGATION + { + a_ptable_clone_ud ud; dMY_CXT; - { - a_ptable_clone_ud ud; - - t = ptable_new(); - a_ptable_clone_ud_init(ud, t, MY_CXT.owner); - ptable_walk(MY_CXT.tbl, a_ptable_clone, &ud); - a_ptable_clone_ud_deinit(ud); - } -#endif - s = ptable_new(); + t = ptable_new(); + a_ptable_clone_ud_init(ud, t, MY_CXT.owner); + ptable_walk(MY_CXT.tbl, a_ptable_clone, &ud); + a_ptable_clone_ud_deinit(ud); } +#endif { MY_CXT_CLONE; #if A_WORKAROUND_REQUIRE_PROPAGATION MY_CXT.tbl = t; MY_CXT.owner = aTHX; #endif - MY_CXT.seen = s; - } - gv = gv_fetchpv(__PACKAGE__ "::_THREAD_CLEANUP", 0, SVt_PVCV); - if (gv) { - CV *cv = GvCV(gv); - if (!PL_endav) - PL_endav = newAV(); - SvREFCNT_inc(cv); - if (!av_store(PL_endav, av_len(PL_endav) + 1, (SV *) cv)) - SvREFCNT_dec(cv); - sv_magicext((SV *) PL_endav, NULL, PERL_MAGIC_ext, &a_endav_vtbl, NULL, 0); + MY_CXT.seen = ptable_new(); + { + int global_setup; + A_LOADED_LOCK; + global_setup = a_set_loaded_locked(&MY_CXT); + assert(!global_setup); + A_LOADED_UNLOCK; + } } XSRETURN(0); -void -_THREAD_CLEANUP(...) -PROTOTYPE: DISABLE -PPCODE: - a_thread_cleanup(aTHX_ NULL); - XSRETURN(0); - #endif /* A_THREADSAFE */ SV *