Skip to content

Commit de92f7e

Browse files
committed
Perl_newSLICEOP: Optimise '(caller)[0]' into 'scalar caller'
A subroutine can obtain just the package of its caller in a couple of ways. Both seem somewhat common. * `caller` - in scaler context, as in `my $x = caller;` * `(caller)[0]`, as in `my $x = (caller)[0];` In the first, `caller` finds the package name, sticks it in a new SV, and puts that (or `undef`) on the stack: <0> caller[t2] s In the second, `caller` (a) finds the package name, filename, and line (b) creates three new SVs to hold them all (c) puts those SVs on the stack (d) does a list slice to leave just the package SV on the stack. 7 <2> lslice sK/2 ->8 - <1> ex-list lK ->5 3 <0> pushmark s ->4 4 <$> const[IV 0] s ->5 - <1> ex-list lK ->7 5 <0> pushmark s ->6 6 <0> caller[t2] l ->7 This commit checks for the second case inside `Perl_newSLICEOP` and instead of constructing a `lslice` OP, returns just the `caller` OP with scalar context applied.
1 parent d6f09a8 commit de92f7e

File tree

5 files changed

+38
-3
lines changed

5 files changed

+38
-3
lines changed

lib/B/Deparse.pm

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
# This is based on the module of the same name by Malcolm Beattie,
88
# but essentially none of his code remains.
99

10-
package B::Deparse 1.85;
10+
package B::Deparse 1.86;
1111
use strict;
1212
use Carp;
1313
use B qw(class main_root main_start main_cv svref_2object opnumber perlstring
@@ -2586,7 +2586,14 @@ sub pp_akeys { unop(@_, "keys") }
25862586
sub pp_pop { unop(@_, "pop") }
25872587
sub pp_shift { unop(@_, "shift") }
25882588

2589-
sub pp_caller { unop(@_, "caller") }
2589+
sub pp_caller {
2590+
my ($self, $op, $cx) = @_;
2591+
if ($op->flags & OPf_SPECIAL) {
2592+
return "scalar ".unop(@_, "caller");
2593+
} else {
2594+
return unop(@_, "caller")
2595+
}
2596+
}
25902597
sub pp_reset { unop(@_, "reset") }
25912598
sub pp_exit { unop(@_, "exit") }
25922599
sub pp_prototype { unop(@_, "prototype") }

op.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8534,6 +8534,17 @@ constructed op tree.
85348534
OP *
85358535
Perl_newSLICEOP(pTHX_ I32 flags, OP *subscript, OP *listval)
85368536
{
8537+
/* (caller)[0] is much more efficiently written as scalar(caller) */
8538+
if (OP_TYPE_IS(subscript, OP_CONST) && OP_TYPE_IS(listval, OP_CALLER)
8539+
&& ! (listval->op_flags & OPf_KIDS) ) {
8540+
SV *theconst = cSVOPx_sv(subscript);
8541+
if (SvIOK(theconst) && 0 == SvIVX(theconst)) {
8542+
op_free(subscript);
8543+
listval->op_flags |= OPf_SPECIAL; /* For B::Deparse */
8544+
return scalar(listval);
8545+
}
8546+
}
8547+
85378548
return newBINOP(OP_LSLICE, flags,
85388549
list(op_force_list(subscript)),
85398550
list(op_force_list(listval)));

op.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ Deprecated. Use C<GIMME_V> instead.
164164
/* On OP_RETURN, module_true is in effect */
165165
/* On OP_NEXT/OP_LAST/OP_REDO, there is no
166166
* loop label */
167+
/* On OP_CALLER, "(caller)[0]" was optimised to
168+
* "caller" with scalar context explicitly set. */
167169
/* There is no room in op_flags for this one, so it has its own bit-
168170
field member (op_folded) instead. The flag is only used to tell
169171
op_convert_list to set op_folded. */

t/op/caller.t

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ BEGIN {
55
chdir 't' if -d 't';
66
require './test.pl';
77
set_up_inc('../lib');
8-
plan( tests => 112 ); # some tests are run in a BEGIN block
8+
plan( tests => 113 ); # some tests are run in a BEGIN block
99
}
1010

1111
my @c;
@@ -393,3 +393,9 @@ do './op/caller.pl' or die $@;
393393
}
394394
->($a[0], 'B');
395395
}
396+
397+
{
398+
my @x = (caller)[0]; # This may be optimised to: my @x = caller
399+
# either way, @x should only have one element
400+
is( $#x, 0, 'my @x = (caller)[0] puts one element in @x')
401+
}

t/perf/opcount.t

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,4 +1106,13 @@ test_opcount(0, "substr with const zero offset (gv)",
11061106
sassign => 1
11071107
});
11081108

1109+
test_opcount(0, "(caller)[0]",
1110+
sub { my $x = (caller)[0] },
1111+
{
1112+
caller => 1,
1113+
const => 0,
1114+
lslice => 0,
1115+
pushmark => 0,
1116+
});
1117+
11091118
done_testing();

0 commit comments

Comments
 (0)