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] },