Skip to content

Commit 4df8577

Browse files
committed
put signature ops in their own subtree.
The following code: sub f ($x,$y) { study; } used to compile as: a <1> leavesub[1 ref] K/REFC,1 ->(end) - <@> lineseq KP ->a 1 <;> nextstate(main 5 p:5) v:%,fea=7 ->2 2 <+> argcheck(2,0) v ->3 3 <;> nextstate(main 3 p:5) v:%,fea=7 ->4 4 <+> argelem(0)[$x:3,5] v/SV ->5 5 <;> nextstate(main 4 p:5) v:%,fea=7 ->6 6 <+> argelem(1)[$y:4,5] v/SV ->7 - <;> ex-nextstate(main 5 p:5) v:%,fea=7 ->7 7 <;> nextstate(main 5 p:6) v:%,fea=7 ->8 9 <1> study sK/1 ->a - <1> ex-rv2sv sK/1 ->9 8 <$> gvsv(*_) s ->9 Following this commit, it compiles as: a <1> leavesub[1 ref] K/REFC,1 ->(end) - <@> lineseq KP ->a - <1> ex-argcheck vK/1 ->7 - <@> lineseq vK ->- 1 <;> nextstate(main 5 p:5) v:%,fea=7 ->2 2 <+> argcheck(2,0) v ->3 3 <;> nextstate(main 3 p:5) v:%,fea=7 ->4 4 <+> argelem(0)[$x:3,5] v/SV ->5 5 <;> nextstate(main 4 p:5) v:%,fea=7 ->6 6 <+> argelem(1)[$y:4,5] v/SV ->7 - <;> ex-nextstate(main 5 p:5) v:%,fea=7 ->- 7 <;> nextstate(main 5 p:6) v:%,fea=7 ->8 9 <1> study sK/1 ->a - <1> ex-rv2sv sK/1 ->9 8 <#> gvsv[*_] s ->9 All the ops associated with the signature have been put in their own subtree, with an extra NULL ex-argcheck op "on top". The op on top serves two purposes: first, it makes it easier for Deparse.pm etc to spot siganure code; secondly, it may at some point in the future be upgraded to OP_SIGNATURE when signatures get optimised. It's of type ex-argcheck only because when being created it needs to be an op type that's in class UNOP_AUX so that the created op will be suitable for later optimising, and making it an ex-type associated with signatures helps flag it as such. There should be no functional changes apart from the shape of the optree.
1 parent 64265ce commit 4df8577

File tree

8 files changed

+269
-215
lines changed

8 files changed

+269
-215
lines changed

ext/XS-APItest/APItest.pm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use strict;
55
use warnings;
66
use Carp;
77

8-
our $VERSION = '1.03';
8+
our $VERSION = '1.04';
99

1010
require XSLoader;
1111

ext/XS-APItest/APItest.xs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,10 +1061,17 @@ static OP *THX_parse_keyword_subsignature(pTHX)
10611061

10621062
/* We can't yield the optree as is to the caller because it won't be
10631063
* executable outside of a called sub. We'll have to convert it into
1064-
* something safe for them to invoke
1065-
* sigop should be a OP_LINESEQ containing OP_NEXTSTATE-separated
1066-
* OP_ARGCHECK and OP_ARGELEMs
1064+
* something safe for them to invoke.
1065+
* sigop should be an OP_NULL above a OP_LINESEQ containing
1066+
* OP_NEXTSTATE-separated OP_ARGCHECK and OP_ARGELEMs
10671067
*/
1068+
if(sigop->op_type != OP_NULL)
1069+
croak("Expected parse_subsignature() to yield an OP_NULL");
1070+
1071+
if(!(sigop->op_flags & OPf_KIDS))
1072+
croak("Expected parse_subsignature() to yield an OP_NULL with kids");
1073+
sigop = cUNOPx(sigop)->op_first;
1074+
10681075
if(sigop->op_type != OP_LINESEQ)
10691076
croak("Expected parse_subsignature() to yield an OP_LINESEQ");
10701077

ext/XS-APItest/t/subsignature.t

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ eval q{
1818
push @t, (subsignature $one = 1);
1919
};
2020
is $@, "";
21+
use Data::Dumper; print Dumper \@t;
2122
is_deeply \@t, [
2223
['nextstate:4', 'argcheck:2:0:-', 'argelem:$x', 'argelem:$y'],
2324
['nextstate:5', 'argcheck:2:0:-', 'argelem:$z',],

lib/B/Deparse.pm

Lines changed: 73 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ use B qw(class main_root main_start main_cv svref_2object opnumber perlstring
5252
MDEREF_SHIFT
5353
);
5454

55-
$VERSION = '1.49';
55+
$VERSION = '1.50';
5656
use strict;
5757
our $AUTOLOAD;
5858
use warnings ();
@@ -271,7 +271,7 @@ BEGIN {
271271

272272

273273
BEGIN { for (qw[ const stringify rv2sv list glob pushmark null aelem
274-
kvaslice kvhslice padsv
274+
kvaslice kvhslice padsv argcheck
275275
nextstate dbstate rv2av rv2hv helem custom ]) {
276276
eval "sub OP_\U$_ () { " . opnumber($_) . "}"
277277
}}
@@ -1176,42 +1176,68 @@ sub pad_subs {
11761176
# or altered. In this case we return "()" and fall back to general
11771177
# deparsing of the individual sigelems as 'my $x = $_[N]' etc.
11781178
#
1179-
# We're only called if the first two ops are nextstate and argcheck.
1179+
# We're only called if the top is an ex-argcheck, which is a placeholder
1180+
# indicating a signature subtree.
1181+
#
1182+
# Return a signature string, or an empty list if no deparseable as a
1183+
# signature
11801184

11811185
sub deparse_argops {
1182-
my ($self, $firstop, $cv) = @_;
1186+
my ($self, $topop, $cv) = @_;
11831187

11841188
my @sig;
1185-
my $o = $firstop;
1186-
return if $o->label; #first nextstate;
1189+
1190+
1191+
$topop = $topop->first;
1192+
return unless $$topop and $topop->name eq 'lineseq';
1193+
1194+
1195+
# last op should be nextstate
1196+
my $last = $topop->last;
1197+
return unless $$last
1198+
and ( _op_is_or_was($last, OP_NEXTSTATE)
1199+
or _op_is_or_was($last, OP_DBSTATE));
1200+
1201+
# first OP_NEXTSTATE
1202+
1203+
my $o = $topop->first;
1204+
return unless $$o;
1205+
return if $o->label;
11871206

11881207
# OP_ARGCHECK
11891208

11901209
$o = $o->sibling;
1210+
return unless $$o and $o->name eq 'argcheck';
1211+
11911212
my ($params, $opt_params, $slurpy) = $o->aux_list($cv);
11921213
my $mandatory = $params - $opt_params;
11931214
my $seen_slurpy = 0;
11941215
my $last_ix = -1;
11951216

1196-
# keep looking for valid nextstate + argelem pairs
1217+
# keep looking for valid nextstate + argelem pairs, terminated
1218+
# by a final nextstate
11971219

11981220
while (1) {
1199-
# OP_NEXTSTATE
12001221
$o = $o->sibling;
1201-
last unless $$o;
1202-
last unless $o->name =~ /^(next|db)state$/;
1203-
last if $o->label;
1222+
return unless $$o;
1223+
1224+
# skip trailing nextstate
1225+
last if $$o == $$last;
1226+
1227+
# OP_NEXTSTATE
1228+
return unless $o->name =~ /^(next|db)state$/;
1229+
return if $o->label;
12041230

12051231
# OP_ARGELEM
1206-
my $o2 = $o->sibling;
1207-
last unless $$o2;
1232+
$o = $o->sibling;
1233+
last unless $$o;
12081234

1209-
if ($o2->name eq 'argelem') {
1210-
my $ix = $o2->string($cv);
1235+
if ($o->name eq 'argelem') {
1236+
my $ix = $o->string($cv);
12111237
while (++$last_ix < $ix) {
12121238
push @sig, $last_ix < $mandatory ? '$' : '$=';
12131239
}
1214-
my $var = $self->padname($o2->targ);
1240+
my $var = $self->padname($o->targ);
12151241
if ($var =~ /^[@%]/) {
12161242
return if $seen_slurpy;
12171243
$seen_slurpy = 1;
@@ -1221,22 +1247,22 @@ sub deparse_argops {
12211247
else {
12221248
return if $ix >= $params;
12231249
}
1224-
if ($o2->flags & OPf_KIDS) {
1225-
my $kid = $o2->first;
1250+
if ($o->flags & OPf_KIDS) {
1251+
my $kid = $o->first;
12261252
return unless $$kid and $kid->name eq 'argdefelem';
12271253
my $def = $self->deparse($kid->first, 7);
12281254
$def = "($def)" if $kid->first->flags & OPf_PARENS;
12291255
$var .= " = $def";
12301256
}
12311257
push @sig, $var;
12321258
}
1233-
elsif ($o2->name eq 'null'
1234-
and ($o2->flags & OPf_KIDS)
1235-
and $o2->first->name eq 'argdefelem')
1259+
elsif ($o->name eq 'null'
1260+
and ($o->flags & OPf_KIDS)
1261+
and $o->first->name eq 'argdefelem')
12361262
{
12371263
# special case - a void context default expression: $ = expr
12381264

1239-
my $defop = $o2->first;
1265+
my $defop = $o->first;
12401266
my $ix = $defop->targ;
12411267
while (++$last_ix < $ix) {
12421268
push @sig, $last_ix < $mandatory ? '$' : '$=';
@@ -1248,20 +1274,20 @@ sub deparse_argops {
12481274
push @sig, '$ = ' . $def;
12491275
}
12501276
else {
1251-
last;
1277+
return;
12521278
}
12531279

1254-
$o = $o2;
12551280
}
12561281

12571282
while (++$last_ix < $params) {
12581283
push @sig, $last_ix < $mandatory ? '$' : '$=';
12591284
}
12601285
push @sig, $slurpy if $slurpy and !$seen_slurpy;
12611286

1262-
return ($o, join(', ', @sig));
1287+
return (join(', ', @sig));
12631288
}
12641289

1290+
12651291
# Deparse a sub. Returns everything except the 'sub foo',
12661292
# e.g. ($$) : method { ...; }
12671293
# or : prototype($$) lvalue ($a, $b) { ...; };
@@ -1304,27 +1330,26 @@ Carp::confess("SPECIAL in deparse_sub") if $cv->isa("B::SPECIAL");
13041330
$self->pad_subs($cv);
13051331
$self->pessimise($root, $cv->START);
13061332
my $lineseq = $root->first;
1307-
if ($lineseq->name eq "lineseq") {
1308-
my $firstop = $lineseq->first;
1309-
1310-
if ($has_sig) {
1311-
my $o2;
1312-
# try to deparse first few ops as a signature if possible
1313-
if ( $$firstop
1314-
and $firstop->name =~ /^(next|db)state$/
1315-
and (($o2 = $firstop->sibling))
1316-
and $$o2)
1317-
{
1318-
if ($o2->name eq 'argcheck') {
1319-
my ($nexto, $mysig) = $self->deparse_argops($firstop, $cv);
1320-
if (defined $nexto) {
1321-
$firstop = $nexto;
1322-
$sig = $mysig;
1323-
}
1324-
}
1325-
}
1333+
1334+
# stub sub may have single op rather than list of ops
1335+
my $is_list = ($lineseq->name eq "lineseq");
1336+
my $firstop = $is_list ? $lineseq->first : $lineseq;
1337+
1338+
# Try to deparse first subtree as a signature if possible.
1339+
# Top of signature subtree has an ex-argcheck as a placeholder
1340+
if ( $has_sig
1341+
and $$firstop
1342+
and $firstop->name eq 'null'
1343+
and $firstop->targ == OP_ARGCHECK
1344+
) {
1345+
my ($mysig) = $self->deparse_argops($firstop, $cv);
1346+
if (defined $mysig) {
1347+
$sig = $mysig;
1348+
$firstop = $is_list ? $firstop->sibling : undef;
13261349
}
1350+
}
13271351

1352+
if ($is_list && $firstop) {
13281353
my @ops;
13291354
for (my $o = $firstop; $$o; $o=$o->sibling) {
13301355
push @ops, $o;
@@ -1341,9 +1366,12 @@ Carp::confess("SPECIAL in deparse_sub") if $cv->isa("B::SPECIAL");
13411366
$body .= ";\n$subs" if length($subs);
13421367
}
13431368
}
1344-
else {
1369+
elsif ($firstop) {
13451370
$body = $self->deparse($root->first, 0);
13461371
}
1372+
else {
1373+
$body = ';'; # stub sub
1374+
}
13471375

13481376
my $l = '';
13491377
if ($self->{'linenums'}) {

0 commit comments

Comments
 (0)