/* ... 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))
{
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) {
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) {
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:
}
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{...} */
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:
}
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;
}
}
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;
#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;
}
}