diff options
author | Matthew Horsfall (alh) <wolfsage@gmail.com> | 2013-10-08 12:56:08 -0400 |
---|---|---|
committer | Tony Cook <tony@develop-help.com> | 2013-11-19 15:35:32 +1100 |
commit | e45d8982aeb58420180a04c6079471d707058965 (patch) | |
tree | aabdb7bb894da028248ce14d1828af50c4720f69 /op.c | |
parent | d320cf40013cb3d9e9458bacdc38048ffba35a6c (diff) | |
download | perl-e45d8982aeb58420180a04c6079471d707058965.tar.gz |
Optimise 'if ($a || $b)' and 'unless ($a && $b)' early exit
An OP_AND/OP_OR in void context provides a short circuit
through ->op_other that can be used if AND/OR ops contained
within it jump out early. Use that short circuit.
Previously:
$ ./perl -Ilib -MO=Concise -e 'if ($aa || $bb) {}'
8 <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 3 -e:1) v:{ ->3
- <1> null vK/1 ->8
6 <|> and(other->7) vK/1 ->8
- <1> null sK/1 ->6
4 <|> or(other->5) sK/1 ->6 <-- Not optimised
- <1> ex-rv2sv sK/1 ->4
3 <$> gvsv(*aa) s ->4
- <1> ex-rv2sv sK/1 ->-
5 <$> gvsv(*bb) s ->6
- <@> scope vK ->-
7 <0> stub v ->8
Now:
$ ./perl -Ilib -MO=Concise -e 'if ($aa || $bb) {}'
8 <@> leave[1 ref] vKP/REFC ->(end)
1 <0> enter ->2
2 <;> nextstate(main 3 -e:1) v:{ ->3
- <1> null vK/1 ->8
6 <|> and(other->7) vK/1 ->8
- <1> null sK/1 ->6
4 <|> or(other->5) sK/1 ->7 <-- Short circuited
- <1> ex-rv2sv sK/1 ->4
3 <$> gvsv(*aa) s ->4
- <1> ex-rv2sv sK/1 ->-
5 <$> gvsv(*bb) s ->6
- <@> scope vK ->-
7 <0> stub v ->8
Diffstat (limited to 'op.c')
-rw-r--r-- | op.c | 18 |
1 files changed, 18 insertions, 0 deletions
@@ -11062,6 +11062,9 @@ S_inplace_aassign(pTHX_ OP *o) { defer_queue[(defer_base + ++defer_ix) % MAX_DEFERRED] = o; \ } STMT_END +#define IS_AND_OP(o) (o->op_type == OP_AND) +#define IS_OR_OP(o) (o->op_type == OP_OR) + /* A peephole optimizer. We visit the ops in the order they're to execute. * See the comments at the top of this file for more details about when * peep() is called */ @@ -11529,6 +11532,21 @@ Perl_rpeep(pTHX_ OP *o) while (o->op_next && ( o->op_type == o->op_next->op_type || o->op_next->op_type == OP_NULL)) o->op_next = o->op_next->op_next; + + /* if we're an OR and our next is a AND in void context, we'll + follow it's op_other on short circuit, same for reverse. + We can't do this with OP_DOR since if it's true, its return + value is the underlying value which must be evaluated + by the next op */ + if (o->op_next && + ( + (IS_AND_OP(o) && IS_OR_OP(o->op_next)) + || (IS_OR_OP(o) && IS_AND_OP(o->op_next)) + ) + && (o->op_next->op_flags & OPf_WANT) == OPf_WANT_VOID + ) { + o->op_next = ((LOGOP*)o->op_next)->op_other; + } DEFER(cLOGOP->op_other); o->op_opt = 1; |