summaryrefslogtreecommitdiff
path: root/pp_hot.c
diff options
context:
space:
mode:
authorDavid Mitchell <davem@iabyn.com>2017-01-06 11:35:11 +0000
committerDavid Mitchell <davem@iabyn.com>2017-01-06 16:28:27 +0000
commitb243b19395066bedc2a6dc3051cd0678692aa7d5 (patch)
treef4d7cda87ff8b3b0c34483a7b615983103012237 /pp_hot.c
parent9d692a7f83cf8fe36c59aaa07bca533887350e9b (diff)
downloadperl-b243b19395066bedc2a6dc3051cd0678692aa7d5.tar.gz
In A && B, stop special-casing boolean-ness of A
Some ops, (currently PADHV and RV2HV) can be flagged as being in boolean context, and if so, may return a simple truth value which may be more efficient to calculate than a full scalar value. (This was originally motivated by code like if (%h) {...}, where the scalar context %h returned a bucket ratio string, which involved counting how many HvARRAY buckets were non-empty, which was slow in large hashes. It's been made less important since %h in scalar context now just returns a key count, which is quick to calculate.) There is an issue with the A argument of A||B, A//B and A&&B, in that, although A checked by the logop in boolean context, depending on its truth value the original A may be passed through to the next op. So in something like $x = (%h || -1), it's not sufficient for %h to return a truth value; it must return a full scalar value which may get assigned to $x. So in general, we only mark the A op as being in boolean context if the logop is in void context, or if the returned A would only be consumed in boolean context; so !(A||B) would be ok for example. However, && is a special case of this, since it will return the original A only if A was false. Before this commit, && was special-cased to mark A as being in boolean context regardless of the context of (A&&B). The downside of this is that the A op can't just return &PL_sv_no as a false value; it has to return something that is usable in scalar context too. For example with %h, it returns sv_2mortal(newSViv(0))), which stringifies to "0" while &PL_sv_no stringifies to "". This commit removes that special case and makes && behave like || and // again. The upside is that some ops in boolean context will be able to more cheaply return a false value (e.g. just &PL_sv_no verses sv_2mortal(newSViv(0))). The main downside is that && in unknown context (typically an 'if (%h} {...}' as the last statement in a sub) will have to check at runtime whether the caller context is slower. It will also have to return a scalar value for something like $y = (%h && $x), but that's a relatively uncommon occurrence, and now that %h in scalar context doesn't have to count used buckets, the extra cost in these rare cases is minor.
Diffstat (limited to 'pp_hot.c')
-rw-r--r--pp_hot.c4
1 files changed, 1 insertions, 3 deletions
diff --git a/pp_hot.c b/pp_hot.c
index cc939998ac..aeaecfc3df 100644
--- a/pp_hot.c
+++ b/pp_hot.c
@@ -1039,9 +1039,7 @@ PP(pp_rv2av)
|| ( PL_op->op_private & OPpMAYBE_TRUEBOOL
&& block_gimme() == G_VOID ))
&& (!SvRMAGICAL(sv) || !mg_find(sv, PERL_MAGIC_tied)))
- /* use newSViv(0) rather than PL_sv_no - see OP_AND comment in
- * S_check_for_bool_cxt() */
- SETs(HvUSEDKEYS(sv) ? &PL_sv_yes : sv_2mortal(newSViv(0)));
+ SETs(HvUSEDKEYS(sv) ? &PL_sv_yes : &PL_sv_no);
else if (gimme == G_SCALAR) {
dTARG;
TARG = Perl_hv_scalar(aTHX_ MUTABLE_HV(sv));