X-Git-Url: http://git.vpit.fr/?a=blobdiff_plain;f=autovivification.xs;h=46422c4002ce3cc80e2aae522e84e076b8672fd9;hb=7839df4d069fb2962a189f08bf7c92ff63d261fa;hp=d07bac01094f085c21be3176b4309ab4895f200a;hpb=d238fb2b10338d7ed57b1ec6ff5f8ee0e1d15bb8;p=perl%2Fmodules%2Fautovivification.git diff --git a/autovivification.xs b/autovivification.xs index d07bac0..46422c4 100644 --- a/autovivification.xs +++ b/autovivification.xs @@ -800,6 +800,40 @@ static OP *a_pp_root_binop(pTHX) { /* ... 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)) { @@ -841,7 +875,7 @@ static SV *a_do_fake_pp_unop_arg1(pTHX_ U32 type, U32 flags, SV *arg) { PUSHs(arg); PUTBACK; - return a_do_fake_pp(&unop); + return a_do_fake_pp((OP *) &unop); } static SV *a_do_fake_pp_unop_arg2(pTHX_ U32 type, U32 flags, SV *arg1, SV *arg2) { @@ -856,46 +890,40 @@ static SV *a_do_fake_pp_unop_arg2(pTHX_ U32 type, U32 flags, SV *arg1, SV *arg2) PUSHs(arg2); PUTBACK; - return a_do_fake_pp(&unop); + 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_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_aelem(A, I) a_do_fake_pp_unop_arg2(OP_AELEM, 0, (A), (I)) +#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_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)) -#define a_do_pp_helem(H, K) a_do_fake_pp_unop_arg2(OP_HELEM, 0, (H), (K)) +#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 = cUNOP_AUXx(PL_op)->op_aux; - UV actions = items->uv; - U8 isexdel = PL_op->op_private & (OPpMULTIDEREF_EXISTS|OPpMULTIDEREF_DELETE); - UV flags = 0; - SV *sv = NULL; + 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); - flags = oi->flags; - if (isexdel) { - if (isexdel & OPpMULTIDEREF_EXISTS) { - if (flags & A_HINT_EXISTS) - goto hijack; - } else if (flags & A_HINT_DELETE) { - assert(isexdel & OPpMULTIDEREF_DELETE); - goto hijack; - } - } else if (flags & A_HINT_FETCH) { - goto hijack; - } - return oi->old_pp(aTHX); + assert(oi); + flags = a_do_multideref(PL_op, oi->flags); + if (!flags) + return oi->old_pp(aTHX); } -hijack: + items = cUNOP_AUXx(PL_op)->op_aux; + actions = items->uv; + PL_multideref_pc = items; while (1) { @@ -929,10 +957,10 @@ hijack: goto do_AV_vivify_rv2av_aelem; case MDEREF_AV_padsv_vivify_rv2av_aelem: /* $lex->[...] */ sv = PAD_SVl((++items)->pad_offset); - if (a_undef(sv)) - goto ret_undef; /* FALLTHROUGH */ case MDEREF_AV_vivify_rv2av_aelem: /* vivify, ->[...] */ + if (a_undef(sv)) + goto ret_undef; do_AV_vivify_rv2av_aelem: sv = Perl_vivify_ref(aTHX_ sv, OPpDEREF_AV); do_AV_rv2av_aelem: @@ -963,17 +991,23 @@ check_elem: } PL_multideref_pc = items; if (actions & MDEREF_FLAG_last) { - if (isexdel) { - if (isexdel & OPpMULTIDEREF_EXISTS) + 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); - else + break; + case A_HINT_DELETE: sv = a_do_pp_adelete(sv, esv); - } else { - sv = a_do_pp_aelem(sv, esv); + break; } goto finish; } - sv = a_do_pp_aelem(sv, esv); + sv = a_do_pp_afetch(sv, esv); break; } case MDEREF_HV_padhv_helem: /* $lex{...} */ @@ -1002,10 +1036,10 @@ check_elem: goto do_HV_vivify_rv2hv_helem; case MDEREF_HV_padsv_vivify_rv2hv_helem: /* $lex->{...} */ sv = PAD_SVl((++items)->pad_offset); - if (a_undef(sv)) - goto ret_undef; /* FALLTHROUGH */ case MDEREF_HV_vivify_rv2hv_helem: /* vivify, ->{...} */ + if (a_undef(sv)) + goto ret_undef; do_HV_vivify_rv2hv_helem: sv = Perl_vivify_ref(aTHX_ sv, OPpDEREF_HV); do_HV_rv2hv_helem: @@ -1031,17 +1065,25 @@ do_HV_helem: } PL_multideref_pc = items; if (actions & MDEREF_FLAG_last) { - if (isexdel) { - if (isexdel & OPpMULTIDEREF_EXISTS) + 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); - else + break; + case A_HINT_DELETE: sv = a_do_pp_hdelete(sv, key); - } else { - sv = a_do_pp_helem(sv, key); + break; + default: + break; } goto finish; } - sv = a_do_pp_helem(sv, key); + sv = a_do_pp_hfetch(sv, key); break; } } @@ -1052,7 +1094,7 @@ do_HV_helem: ret_undef: if (flags & (A_HINT_NOTIFY|A_HINT_STORE)) a_cannot_vivify(flags); - if (isexdel & OPpMULTIDEREF_EXISTS) + if (flags & A_HINT_EXISTS) sv = &PL_sv_no; else sv = &PL_sv_undef; @@ -1366,16 +1408,12 @@ static void a_peep_rec(pTHX_ OP *o, ptable *seen) { #if A_HAS_MULTIDEREF case OP_MULTIDEREF: if (o->op_ppaddr != a_pp_multideref) { - UV isexdel; oi = a_map_fetch(cUNOPo->op_first); if (!oi) break; - flags = oi->flags; - isexdel = o->op_private & (OPpMULTIDEREF_EXISTS|OPpMULTIDEREF_DELETE); - if ( ((flags & A_HINT_FETCH) && !isexdel) - || (flags & A_HINT_EXISTS) && (isexdel & OPpMULTIDEREF_EXISTS) - || (flags & A_HINT_DELETE) && (isexdel & OPpMULTIDEREF_DELETE)) { - a_map_store(o, o->op_ppaddr, oi->next, oi->flags); + 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; } }