5 # Test whether various constructs have the right numbers of particular op
6 # types. This is chiefly to test that various optimisations are not
7 # inadvertently removed.
9 # For example the array access in sub { $a[0] } should get optimised from
10 # aelem into aelemfast. So we want to test that there are 1 aelemfast, 0
11 # aelem and 1 ex-aelem ops in the optree for that sub.
16 skip_all_if_miniperl("No B under miniperl");
31 # for a given op, increment $count{opname}. Treat null ops
32 # as "ex-foo" where possible
34 sub B::OP::test_opcount_callback {
37 if ($name eq 'null') {
40 $name = "ex-" . substr(B::ppname($targ), 3);
46 # Given a code ref and a hash ref of expected op counts, check that
47 # for each opname => count pair, whether that op appears that many
48 # times in the op tree for that sub. If $debug is 1, display all the
49 # op counts for the sub.
52 my ($debug, $desc, $coderef, $expected_counts) = @_;
55 B::walkoptree(B::svref_2object($coderef)->ROOT,
56 'test_opcount_callback');
59 note(sprintf "%3d %s", $counts{$_}, $_) for sort keys %counts;
63 for (sort keys %$expected_counts) {
64 my ($c, $e) = ($counts{$_}//0, $expected_counts->{$_});
66 push @exp, "expected $e, got $c: $_";
76 # aelem => aelemfast: a basic test that this test file works
78 test_opcount(0, "basic aelemfast",
79 sub { our @a; $a[0] = 1 },
87 # Porting/bench.pl tries to create an empty and active loop, with the
88 # ops executed being exactly the same apart from the additional ops
89 # in the active loop. Check that this remains true.
92 test_opcount(0, "bench.pl empty loop",
93 sub { for my $x (1..$ARGV[0]) { 1; } },
111 test_opcount(0, "bench.pl active loop",
112 sub { for my $x (1..$ARGV[0]) { $x; } },
124 padsv => 1, # this is the additional active op
134 # try many permutations of aggregate lookup expressions
139 my (@agg_lex, %agg_lex, $i_lex, $r_lex);
140 our (@agg_pkg, %agg_pkg, $i_pkg, $r_pkg);
143 my @bodies = ('[0]', '[128]', '[$i_lex]', '[$i_pkg]',
144 '{foo}', '{$i_lex}', '{$i_pkg}',
147 for my $prefix ('$f->()->', '$agg_lex', '$agg_pkg', '$r_lex->', '$r_pkg->')
149 for my $mod ('', 'local', 'exists', 'delete') {
150 for my $body0 (@bodies) {
151 for my $body1 ('', @bodies) {
152 for my $body2 ('', '[2*$i_lex]') {
153 my $code = "$mod $prefix$body0$body1$body2";
154 my $sub = "sub { $code }";
155 my $coderef = eval $sub
156 or die "eval '$sub': $@";
168 if ($code =~ /^\s*\$agg_...\[0\]$/) {
169 # we should expect aelemfast rather than multideref
170 $top = $code =~ /lex/ ? 'aelemfast_lex'
179 # trailing index; top aelem/exists/whatever
181 $top = $mod unless $mod eq '' or $mod eq 'local';
185 ::test_opcount(0, $sub, $coderef, \%c);
194 # multideref: ensure that the prefix expression and trailing index
195 # expression are optimised (include aelemfast in those expressions)
198 test_opcount(0, 'multideref expressions',
199 sub { ($_[0] // $_)->[0]{2*$_[0]} },
207 # multideref with interesting constant indices
210 test_opcount(0, 'multideref const index',
211 sub { $_->{1}{1.1} },
218 use constant my_undef => undef;
219 test_opcount(0, 'multideref undef const index',
220 sub { $_->{+my_undef} },
227 # multideref when its the first op in a subchain
229 test_opcount(0, 'multideref op_other etc',
230 sub { $_{foo} = $_ ? $_{bar} : $_{baz} },
237 # multideref without hints
243 test_opcount(0, 'multideref no hints',
253 # exists shouldn't clash with aelemfast
255 test_opcount(0, 'multideref exists',
256 sub { exists $_[0] },
264 test_opcount(0, 'barewords can be constant-folded',
265 sub { no strict 'subs'; FOO . BAR },
271 no warnings 'experimental::signatures';
272 use feature 'signatures';
275 test_opcount(0, 'signature default expressions get optimised',
286 local our @global = (3,2,1);
289 test_opcount(0, 'in-place sort of global',
290 sub { @global = sort @global; 1 },
296 test_opcount(0, 'in-place sort of lexical',
297 sub { @lex = sort @lex; 1 },
303 test_opcount(0, 'in-place reversed sort of global',
304 sub { @global = sort { $b <=> $a } @global; 1 },
311 test_opcount(0, 'in-place custom sort of global',
312 sub { @global = sort { $a<$b?1:$a>$b?-1:0 } @global; 1 },
318 sub mysort { $b cmp $a };
319 test_opcount(0, 'in-place sort with function of lexical',
320 sub { @lex = sort mysort @lex; 1 },
329 # in-place assign optimisation for @a = split
341 # partial implies that the aassign has been optimised away, but
343 my ($code, $partial) = @$_;
344 test_opcount(0, "in-place assignment for split: $code",
345 eval qq{sub { $code = split }},
354 # index(...) == -1 and variants optimise away the EQ/NE/etc and CONST
355 # and with $lex = (index(...) == -1), the assignment is optimised away
363 for my $assign (0, 1) {
364 for my $index ('index($x,$y)', 'rindex($x,$y)') {
383 my $expr = sprintf $fmt, $index;
384 $expr = "\$z = ($expr)" if $assign;
386 test_opcount(0, "optimise away compare,const in $expr",
387 eval qq{sub { $expr }},
405 # a sprintf that can't be optimised shouldn't stop the .= concat being
410 test_opcount(0, "sprintf pessimised",
411 sub { $s .= sprintf "%d%d",$i, $j },
422 # sprintf with constant args should be constant folded
424 test_opcount(0, "sprintf constant args",
425 sub { sprintf "%s%s", "abc", "def" },
433 # concats and assigns that should be optimised into a single multiconcat
438 my %seen; # weed out duplicate combinations
440 # these are the ones where using multiconcat isn't a gain, so should
442 my %pessimise = map { $_ => 1 }
449 # these already constant folded
451 '$pkg = sprintf("-")',
452 '$lex = sprintf("-")',
453 'my $l = sprintf("-")',
464 for my $nargs (0..3) {
465 for my $type (0..2) {
468 # 2: sprintf("%s%s", $a, $b)
470 for my $const (0..4) {
471 # 0: no consts: "$a1$a2"
472 # 1: interior consts: "$a1-$a2"
473 # 2: + LH edge: "-$a1-$a2"
474 # 3: + RH edge: "$a1-$a2-"
475 # 4: + both edge: "-$a1-$a2-"
479 my $c = $type == 0 ? '"-"' : '-';
480 push @args, $c if $const == 2 || $const == 4;
481 for my $n (1..$nargs) {
484 push @sprintf_args, "\$a$n";
490 push @args, $c if $const;
492 pop @args if $const == 1 || $const == 2;
494 push @args, $c if $nargs == 0 && $const == 1;
502 # To ensure that there's at least once concat
503 # action, if appending, need at least one RHS arg;
507 next unless @args >= ($lhs =~ /\./ ? 1 : 2);
512 $rhs = join('.', @args);
515 $rhs = '"' . join('', @args) . '"'
521 . join('', map ",$_", @sprintf_args)
525 my $expr = $lhs . $rhs;
527 next if exists $seen{$expr};
533 my $sub = eval qq{sub { $expr }};
534 die "eval(sub { $expr }: $@" if $@;
536 my $pm = $pessimise{$expr};
537 test_opcount(0, ($pm ? "concat " : "multiconcat")
541 ? { multiconcat => 0 }
550 gv => 0, # optimised to gvsv
558 # $lex = "foo" should *not* get converted into a multiconcat - there's
559 # no actual concatenation involved, and treating it as a degnerate concat
560 # would forego any COW copy efficiency
562 test_opcount(0, '$lex = "foo"', sub { my $x; $x = "foo"; },
567 # for '$lex1 = $lex2 . $lex3', multiconcat is normally slower than
568 # concat, except in the specific case of '$lex1 = $lex2 . $lex1'
570 test_opcount(0, '$lex1 = $lex2 . $lex1', sub { my ($x,$y); $x = $y . $x },
573 padsv => 4, # 2 are from the my()
578 test_opcount(0, '$lex1 = "$lex2$lex1"', sub { my ($x,$y); $x = "$y$x" },
581 padsv => 4, # 2 are from the my()
586 test_opcount(0, '$lex1 = $lex1 . $lex1', sub { my $x; $x = $x . $x },
591 # 'my $x .= ...' doesn't make a lot of sense and so isn't optimised
592 test_opcount(0, 'my $a .= $b.$c.$d', sub { our ($b,$c,$d); my $a .= $b.$c.$d },
597 # prefer rcatline optimisation over multiconcat
599 test_opcount(0, "rcatline", sub { my ($x,$y); open FOO, "xxx"; $x .= <FOO> },
607 # long chains of concats should be converted into chained multiconcats
611 for my $i (60..68) { # check each side of 64 threshold
612 my $c = join '.', map "\$a[$_]", 1..$i;
613 my $sub = eval qq{sub { $c }} or die $@;
614 test_opcount(0, "long chain $i", $sub,
616 multiconcat => $i > 65 ? 2 : 1,
617 concat => $i == 65 ? 1 : 0,
624 # with C<$state $s = $a . $b . ....>, the assign is optimised away,
625 # but the padsv isn't (it's treated like a general LHS expression rather
626 # than using OPpTARGET_MY).
628 test_opcount(0, "state works with multiconcat",
629 sub { use feature 'state'; our ($a, $b, $c); state $s = $a . $b . $c },
635 padsv => 2, # one each for the next/once branches
638 # multiple concats of constants preceded by at least one non-constant
639 # shouldn't get constant-folded so that a concat overload method is called
640 # for each arg. So every second constant string is left as an OP_CONST
642 test_opcount(0, "multiconcat: 2 adjacent consts",
643 sub { my ($a, $b); $a = $b . "c" . "d" },
650 test_opcount(0, "multiconcat: 3 adjacent consts",
651 sub { my ($a, $b); $a = $b . "c" . "d" . "e" },
658 test_opcount(0, "multiconcat: 4 adjacent consts",
659 sub { my ($a, $b); $a = $b . "c" . "d" . "e" ."f" },
667 # multiconcat shouldn't include the assign if the LHS has 'local'
669 test_opcount(0, "multiconcat: local assign",
670 sub { our $global; local $global = "$global-X" },
681 no warnings 'experimental::defer';
683 test_opcount(1, "pushdefer: block is optimized",
684 sub { my @a; defer { $a[0] } },