xref: /openbsd-src/gnu/usr.bin/perl/t/perf/optree.t (revision 5759b3d249badf144a6240f7eec4dcf9df003e6b)
1b8851fccSafresh1#!./perl
2b8851fccSafresh1
3b8851fccSafresh1# Use B to test that optimisations are not inadvertently removed,
4b8851fccSafresh1# by examining particular nodes in the optree.
5b8851fccSafresh1
6*5759b3d2Safresh1use warnings;
7*5759b3d2Safresh1use strict;
8*5759b3d2Safresh1
9b8851fccSafresh1BEGIN {
10b8851fccSafresh1    chdir 't';
11b8851fccSafresh1    require './test.pl';
12b8851fccSafresh1    skip_all_if_miniperl("No B under miniperl");
13b8851fccSafresh1    @INC = '../lib';
14b8851fccSafresh1}
15b8851fccSafresh1
16*5759b3d2Safresh1plan 2285;
17b8851fccSafresh1
18b8851fccSafresh1use v5.10; # state
19b8851fccSafresh1use B qw(svref_2object
20b8851fccSafresh1         OPpASSIGN_COMMON_SCALAR
21b8851fccSafresh1         OPpASSIGN_COMMON_RC1
22b8851fccSafresh1         OPpASSIGN_COMMON_AGG
23*5759b3d2Safresh1         OPpTRUEBOOL
24*5759b3d2Safresh1         OPpMAYBE_TRUEBOOL
25*5759b3d2Safresh1         OPpASSIGN_TRUEBOOL
26b8851fccSafresh1      );
27b8851fccSafresh1
28*5759b3d2Safresh1# for debugging etc. Basic dump of an optree
29*5759b3d2Safresh1
30*5759b3d2Safresh1sub dump_optree {
31*5759b3d2Safresh1    my ($o, $depth) = @_;
32*5759b3d2Safresh1
33*5759b3d2Safresh1    return '' unless $$o;
34*5759b3d2Safresh1    # use Devel::Peek; Dump $o;
35*5759b3d2Safresh1    my $s = ("  " x $depth) . $o->name . "\n";
36*5759b3d2Safresh1    my $n = eval { $o->first };
37*5759b3d2Safresh1    while ($n && $$n) {
38*5759b3d2Safresh1        $s .= dump_optree($n, $depth+1);
39*5759b3d2Safresh1        $n = $n->sibling;
40*5759b3d2Safresh1    }
41*5759b3d2Safresh1    $s;
42*5759b3d2Safresh1}
43*5759b3d2Safresh1
44*5759b3d2Safresh1
45b8851fccSafresh1
46b8851fccSafresh1# Test that OP_AASSIGN gets the appropriate
47b8851fccSafresh1# OPpASSIGN_COMMON* flags set.
48b8851fccSafresh1#
49b8851fccSafresh1# Too few flags set is likely to cause code to misbehave;
50b8851fccSafresh1# too many flags set unnecessarily slows things down.
51b8851fccSafresh1# See also the tests in t/op/aassign.t
52b8851fccSafresh1
53b8851fccSafresh1for my $test (
54b8851fccSafresh1    # Each anon array contains:
55b8851fccSafresh1    # [
56b8851fccSafresh1    #   expected flags:
57b8851fccSafresh1    #      a 3 char string, each char showing whether we expect a
58b8851fccSafresh1    #      particular flag to be set:
59b8851fccSafresh1    #           '-' indicates any char not set, while
60b8851fccSafresh1    #           'S':  char 0: OPpASSIGN_COMMON_SCALAR,
61b8851fccSafresh1    #           'R':  char 1: OPpASSIGN_COMMON_RC1,
62b8851fccSafresh1    #           'A'   char 2: OPpASSIGN_COMMON_AGG,
63b8851fccSafresh1    #   code to eval,
64b8851fccSafresh1    #   description,
65b8851fccSafresh1    # ]
66b8851fccSafresh1
67b8851fccSafresh1    [ "---", '() = (1, $x, my $y, @z, f($p))', 'no LHS' ],
68b8851fccSafresh1    [ "---", '(undef, $x, my $y, @z, ($a ? $b : $c)) = ()', 'no RHS' ],
69b8851fccSafresh1    [ "---", '(undef, $x, my $y, @z, ($a ? $b : $c)) = (1,2)', 'safe RHS' ],
70b8851fccSafresh1    [ "---", 'my @a = (1,2)', 'safe RHS: my array' ],
71b8851fccSafresh1    [ "---", 'my %h = (1,2)', 'safe RHS: my hash' ],
72b8851fccSafresh1    [ "---", 'my ($a,$b,$c,$d) = 1..6; ($a,$b) = ($c,$d);', 'non-common lex' ],
73b8851fccSafresh1    [ "---", '($x,$y) = (1,2)', 'pkg var LHS only' ],
74b8851fccSafresh1    [ "---", 'my $p; my ($x,$y) = ($p, $p)', 'my; dup lex var on RHS' ],
75b8851fccSafresh1    [ "---", 'my $p; my ($x,$y); ($x,$y) = ($p, $p)', 'dup lex var on RHS' ],
76b8851fccSafresh1    [ "---", 'my ($self) = @_', 'LHS lex scalar only' ],
77b8851fccSafresh1    [ "--A", 'my ($self, @rest) = @_', 'LHS lex mixed' ],
78b8851fccSafresh1    [ "-R-", 'my ($x,$y) = ($p, $q)', 'pkg var RHS only' ],
79b8851fccSafresh1    [ "S--", '($x,$y) = ($p, $q)', 'pkg scalar both sides' ],
80b8851fccSafresh1    [ "--A", 'my (@a, @b); @a = @b', 'lex ary both sides' ],
81b8851fccSafresh1    [ "-R-", 'my ($x,$y,$z,@a); ($x,$y,$z) = @a ', 'lex vars to lex ary' ],
82b8851fccSafresh1    [ "--A", '@a = @b', 'pkg ary both sides' ],
83b8851fccSafresh1    [ "--A", 'my (%a,%b); %a = %b', 'lex hash both sides' ],
84b8851fccSafresh1    [ "--A", '%a = %b', 'pkg hash both sides' ],
85b8851fccSafresh1    [ "--A", 'my $x; @a = ($a[0], $a[$x])', 'common ary' ],
86b8851fccSafresh1    [ "--A", 'my ($x,@a); @a = ($a[0], $a[$x])', 'common lex ary' ],
87b8851fccSafresh1    [ "S-A", 'my $x; ($a[$x], $a[0]) = ($a[0], $a[$x])', 'common ary elems' ],
88b8851fccSafresh1    [ "S-A", 'my ($x,@a); ($a[$x], $a[0]) = ($a[0], $a[$x])',
89b8851fccSafresh1                                                    'common lex ary elems' ],
90b8851fccSafresh1    [ "--A", 'my $x; my @a = @$x', 'lex ary may have stuff' ],
91b8851fccSafresh1    [ "-RA", 'my $x; my ($b, @a) = @$x', 'lex ary may have stuff' ],
92b8851fccSafresh1    [ "--A", 'my $x; my %a = @$x', 'lex hash may have stuff' ],
93b8851fccSafresh1    [ "-RA", 'my $x; my ($b, %a) = @$x', 'lex hash may have stuff' ],
94b8851fccSafresh1    [ "--A", 'my (@a,@b); @a = ($b[0])', 'lex ary and elem' ],
95b8851fccSafresh1    [ "S-A", 'my @a; ($a[1],$a[0]) = @a', 'lex ary and elem' ],
96b8851fccSafresh1    [ "--A", 'my @x; @y = $x[0]', 'pkg ary from lex elem' ],
97b8851fccSafresh1    [ "---", '(undef,$x) = f()', 'single scalar on LHS' ],
98b8851fccSafresh1    [ "---", '($x,$y) = ($x)', 'single scalar on RHS, no AGG' ],
99b8851fccSafresh1    [ "--A", '($x,@b) = ($x)', 'single scalar on RHS' ],
100*5759b3d2Safresh1    [ "--A", 'my @a; @a = (@a = split())',      'split a/a'   ],
101*5759b3d2Safresh1    [ "--A", 'my (@a,@b); @a = (@b = split())', 'split a/b'   ],
102*5759b3d2Safresh1    [ "---", 'my @a; @a = (split(), 1)',        '(split(),1)' ],
103*5759b3d2Safresh1    [ "---", '@a = (split(//, @a), 1)',         'split(@a)'   ],
104*5759b3d2Safresh1    [ "--A", 'my @a; my $ar = @a; @a = (@$ar = split())', 'a/ar split'  ],
105b8851fccSafresh1) {
106*5759b3d2Safresh1
107b8851fccSafresh1    my ($exp, $code, $desc) = @$test;
108*5759b3d2Safresh1    my $sub;
109*5759b3d2Safresh1    {
110*5759b3d2Safresh1        # package vars used in code snippets
111*5759b3d2Safresh1        our (@a, %a, @b, %b, $c, $p, $q, $x, $y, @y, @z);
112*5759b3d2Safresh1
113*5759b3d2Safresh1        $sub = eval "sub { $code }"
114b8851fccSafresh1            or die
115*5759b3d2Safresh1                "aassign eval('$code') failed: this test needs"
116*5759b3d2Safresh1                . "to be rewritten:\n$@"
117*5759b3d2Safresh1    }
118b8851fccSafresh1
119b8851fccSafresh1    my $last_expr = svref_2object($sub)->ROOT->first->last;
120b8851fccSafresh1    if ($last_expr->name ne 'aassign') {
121b8851fccSafresh1        die "Expected aassign but found ", $last_expr->name,
122b8851fccSafresh1            "; this test needs to be rewritten"
123b8851fccSafresh1    }
124b8851fccSafresh1    my $got =
125b8851fccSafresh1        (($last_expr->private & OPpASSIGN_COMMON_SCALAR) ? 'S' : '-')
126b8851fccSafresh1      . (($last_expr->private & OPpASSIGN_COMMON_RC1)    ? 'R' : '-')
127b8851fccSafresh1      . (($last_expr->private & OPpASSIGN_COMMON_AGG)    ? 'A' : '-');
128b8851fccSafresh1    is $got, $exp,  "OPpASSIGN_COMMON: $desc: '$code'";
129b8851fccSafresh1}
130b8851fccSafresh1
131b8851fccSafresh1
132b8851fccSafresh1# join -> stringify/const
133b8851fccSafresh1
134b8851fccSafresh1for (['CONSTANT', sub {          join "foo", $_ }],
135b8851fccSafresh1     ['$var'    , sub {          join  $_  , $_ }],
136b8851fccSafresh1     ['$myvar'  , sub { my $var; join  $var, $_ }],
137b8851fccSafresh1) {
138b8851fccSafresh1    my($sep,$sub) = @$_;
139b8851fccSafresh1    my $last_expr = svref_2object($sub)->ROOT->first->last;
140b8851fccSafresh1    is $last_expr->name, 'stringify',
141b8851fccSafresh1      "join($sep, \$scalar) optimised to stringify";
142b8851fccSafresh1}
143b8851fccSafresh1
144b8851fccSafresh1for (['CONSTANT', sub {          join "foo", "bar"    }, 0, "bar"    ],
145b8851fccSafresh1     ['CONSTANT', sub {          join "foo", "bar", 3 }, 1, "barfoo3"],
146b8851fccSafresh1     ['$var'    , sub {          join  $_  , "bar"    }, 0, "bar"    ],
147b8851fccSafresh1     ['$myvar'  , sub { my $var; join  $var, "bar"    }, 0, "bar"    ],
148b8851fccSafresh1) {
149b8851fccSafresh1    my($sep,$sub,$is_list,$expect) = @$_;
150b8851fccSafresh1    my $last_expr = svref_2object($sub)->ROOT->first->last;
151b8851fccSafresh1    my $tn = "join($sep, " . ($is_list?'list of constants':'const') . ")";
152b8851fccSafresh1    is $last_expr->name, 'const', "$tn optimised to constant";
153b8851fccSafresh1    is $sub->(), $expect, "$tn folded correctly";
154b8851fccSafresh1}
155b8851fccSafresh1
156b8851fccSafresh1
157b8851fccSafresh1# list+pushmark in list context elided out of the execution chain
158b8851fccSafresh1is svref_2object(sub { () = ($_, ($_, $_)) })
159b8851fccSafresh1    ->START # nextstate
160b8851fccSafresh1    ->next  # pushmark
161b8851fccSafresh1    ->next  # gvsv
162b8851fccSafresh1    ->next  # should be gvsv, not pushmark
163b8851fccSafresh1  ->name, 'gvsv',
164b8851fccSafresh1  "list+pushmark in list context where list's elder sibling is a null";
165b8851fccSafresh1
166b8851fccSafresh1
167b8851fccSafresh1# nextstate multiple times becoming one nextstate
168b8851fccSafresh1
169b8851fccSafresh1is svref_2object(sub { 0;0;0;0;0;0;time })->START->next->name, 'time',
170b8851fccSafresh1  'multiple nextstates become one';
171b8851fccSafresh1
172b8851fccSafresh1
173b8851fccSafresh1# pad[ahs]v state declarations in void context
174b8851fccSafresh1
175b8851fccSafresh1is svref_2object(sub{state($foo,@fit,%far);state $bar;state($a,$b); time})
176b8851fccSafresh1    ->START->next->name, 'time',
177b8851fccSafresh1  'pad[ahs]v state declarations in void context';
178b8851fccSafresh1
179b8851fccSafresh1
180b8851fccSafresh1# pushmark-padsv-padav-padhv in list context --> padrange
181b8851fccSafresh1
182b8851fccSafresh1{
183b8851fccSafresh1    my @ops;
184b8851fccSafresh1    my $sub = sub { \my( $f, @f, %f ) };
185b8851fccSafresh1    my $op = svref_2object($sub)->START;
186b8851fccSafresh1    push(@ops, $op->name), $op = $op->next while $$op;
187b8851fccSafresh1    is "@ops", "nextstate padrange refgen leavesub", 'multi-type padrange'
188b8851fccSafresh1}
189b8851fccSafresh1
190b8851fccSafresh1
191b8851fccSafresh1# rv2[ahs]v in void context
192b8851fccSafresh1
193b8851fccSafresh1is svref_2object(sub { our($foo,@fit,%far); our $bar; our($a,$b); time })
194b8851fccSafresh1    ->START->next->name, 'time',
195b8851fccSafresh1  'rv2[ahs]v in void context';
196b8851fccSafresh1
197b8851fccSafresh1
198b8851fccSafresh1# split to array
199b8851fccSafresh1
200b8851fccSafresh1for(['@pkgary'      , '@_'       ],
201b8851fccSafresh1    ['@lexary'      , 'my @a; @a'],
202b8851fccSafresh1    ['my(@array)'   , 'my(@a)'   ],
203b8851fccSafresh1    ['local(@array)', 'local(@_)'],
204b8851fccSafresh1    ['@{...}'       , '@{\@_}'   ],
205b8851fccSafresh1){
206b8851fccSafresh1    my($tn,$code) = @$_;
207b8851fccSafresh1    my $sub = eval "sub { $code = split }";
208b8851fccSafresh1    my $split = svref_2object($sub)->ROOT->first->last;
209b8851fccSafresh1    is $split->name, 'split', "$tn = split swallows up the assignment";
210b8851fccSafresh1}
211b8851fccSafresh1
212b8851fccSafresh1
213b8851fccSafresh1# stringify with join kid --> join
214b8851fccSafresh1is svref_2object(sub { "@_" })->ROOT->first->last->name, 'join',
215b8851fccSafresh1  'qq"@_" optimised from stringify(join(...)) to join(...)';
216*5759b3d2Safresh1
217*5759b3d2Safresh1
218*5759b3d2Safresh1# Check that certain ops, when in boolean context, have the
219*5759b3d2Safresh1# right private "is boolean" or "maybe boolean" flags set.
220*5759b3d2Safresh1#
221*5759b3d2Safresh1# A maybe flag is set when the context at the end of a chain of and/or/dor
222*5759b3d2Safresh1# ops isn't known till runtime, e.g.
223*5759b3d2Safresh1#   sub f { ....; ((%h || $x) || $y)) }
224*5759b3d2Safresh1# If f() is called in void context, then %h can return a boolean value;
225*5759b3d2Safresh1# if in scalar context, %h must return a key count.
226*5759b3d2Safresh1
227*5759b3d2Safresh1for my $ops (
228*5759b3d2Safresh1    #  op          code        op_path flag               maybe_flag
229*5759b3d2Safresh1    #  ---------   ----------  ------- -----------------  ----------------
230*5759b3d2Safresh1    [ 'aassign',  '(@pkg = @lex)',[],  OPpASSIGN_TRUEBOOL,0,                ],
231*5759b3d2Safresh1    [ 'grepwhile','grep($_,1)',   [],  OPpTRUEBOOL,       0,                ],
232*5759b3d2Safresh1    [ 'length',   'length($x)',   [],  OPpTRUEBOOL,       0,                ],
233*5759b3d2Safresh1    [ 'padav',    '@lex',         [],  OPpTRUEBOOL,       0,                ],
234*5759b3d2Safresh1    [ 'padav',    'scalar @lex',  [0], OPpTRUEBOOL,       0,                ],
235*5759b3d2Safresh1    [ 'padhv',    '%lex',         [],  OPpTRUEBOOL,       OPpMAYBE_TRUEBOOL ],
236*5759b3d2Safresh1    [ 'padhv',    'scalar(%lex)', [0], OPpTRUEBOOL,       OPpMAYBE_TRUEBOOL ],
237*5759b3d2Safresh1    [ 'pos',      'pos($x)',      [],  OPpTRUEBOOL,       0,                ],
238*5759b3d2Safresh1    [ 'ref',      'ref($x)',      [],  OPpTRUEBOOL,       OPpMAYBE_TRUEBOOL ],
239*5759b3d2Safresh1    [ 'rv2av',    '@pkg',         [],  OPpTRUEBOOL,       0,                ],
240*5759b3d2Safresh1    [ 'rv2av',    'scalar(@pkg)', [0], OPpTRUEBOOL,       0,                ],
241*5759b3d2Safresh1    [ 'rv2hv',    '%pkg',         [],  OPpTRUEBOOL,       OPpMAYBE_TRUEBOOL ],
242*5759b3d2Safresh1    [ 'rv2hv',    'scalar(%pkg)', [0], OPpTRUEBOOL,       OPpMAYBE_TRUEBOOL ],
243*5759b3d2Safresh1    [ 'subst',    's/a/b/',       [],  OPpTRUEBOOL,       0,                ],
244*5759b3d2Safresh1) {
245*5759b3d2Safresh1    my ($op_name, $op_code, $post_op_path, $bool_flag, $maybe_flag) = @$ops;
246*5759b3d2Safresh1
247*5759b3d2Safresh1    for my $test (
248*5759b3d2Safresh1        # 1st column: what to expect for each $context (void, scalar, unknown),
249*5759b3d2Safresh1        #                0: expect no flag
250*5759b3d2Safresh1        #                1: expect bool flag
251*5759b3d2Safresh1        #                2: expect maybe bool flag
252*5759b3d2Safresh1        #                9: skip test
253*5759b3d2Safresh1        #  2nd column: path though the op subtree to the flagged op:
254*5759b3d2Safresh1        #                0 is first child, 1 is second child etc.
255*5759b3d2Safresh1        #                Will have @$post_op_path from above appended.
256*5759b3d2Safresh1        #  3rd column: code to execute: %s holds the code for the op
257*5759b3d2Safresh1        #
258*5759b3d2Safresh1        # [V S U]  PATH        CODE
259*5759b3d2Safresh1
260*5759b3d2Safresh1        # INNER PLAIN
261*5759b3d2Safresh1
262*5759b3d2Safresh1        [ [0,0,0], [],        '%s'                               ],
263*5759b3d2Safresh1        [ [1,9,1], [0,0],     'if (%s) {$x}'                     ],
264*5759b3d2Safresh1        [ [1,9,1], [0,0],     'if (%s) {$x} else {$y}'           ],
265*5759b3d2Safresh1        [ [1,9,2], [0,0],     'unless (%s) {$x}'                 ],
266*5759b3d2Safresh1
267*5759b3d2Safresh1        # INNER NOT
268*5759b3d2Safresh1
269*5759b3d2Safresh1        [ [1,1,1], [0],       '!%s'                              ],
270*5759b3d2Safresh1        [ [1,9,1], [0,0,0],   'if (!%s) {$x}'                    ],
271*5759b3d2Safresh1        [ [1,9,1], [0,0,0],   'if (!%s) {$x} else {$y}'          ],
272*5759b3d2Safresh1        [ [1,9,1], [0,0,0],   'unless (!%s) {$x}'                ],
273*5759b3d2Safresh1
274*5759b3d2Safresh1        # INNER COND
275*5759b3d2Safresh1
276*5759b3d2Safresh1        [ [1,1,1], [0,0,],    '%s ? $p : $q'                     ],
277*5759b3d2Safresh1        [ [1,9,1], [0,0,0,0], 'if (%s ? $p : $q) {$x}'           ],
278*5759b3d2Safresh1        [ [1,9,1], [0,0,0,0], 'if (%s ? $p : $q) {$x} else {$y}' ],
279*5759b3d2Safresh1        [ [1,9,1], [0,0,0,0], 'unless (%s ? $p : $q) {$x}'       ],
280*5759b3d2Safresh1
281*5759b3d2Safresh1
282*5759b3d2Safresh1        # INNER OR LHS
283*5759b3d2Safresh1
284*5759b3d2Safresh1        [ [1,0,2], [0,0],     '%s || $x'                         ],
285*5759b3d2Safresh1        [ [1,1,1], [0,0,0],   '!(%s || $x)'                      ],
286*5759b3d2Safresh1        [ [1,0,2], [0,1,0,0], '$y && (%s || $x)'                 ],
287*5759b3d2Safresh1        [ [1,9,1], [0,0,0,0], 'if (%s || $x) {$x}'               ],
288*5759b3d2Safresh1        [ [1,9,1], [0,0,0,0], 'if (%s || $x) {$x} else {$y}'     ],
289*5759b3d2Safresh1        [ [1,9,2], [0,0,0,0], 'unless (%s || $x) {$x}'           ],
290*5759b3d2Safresh1
291*5759b3d2Safresh1        # INNER OR RHS
292*5759b3d2Safresh1
293*5759b3d2Safresh1        [ [0,0,0], [0,1],     '$x || %s'                         ],
294*5759b3d2Safresh1        [ [1,1,1], [0,0,1],   '!($x || %s)'                      ],
295*5759b3d2Safresh1        [ [0,0,0], [0,1,0,1], '$y && ($x || %s)'                 ],
296*5759b3d2Safresh1        [ [1,9,1], [0,0,0,1], 'if ($x || %s) {$x}'               ],
297*5759b3d2Safresh1        [ [1,9,1], [0,0,0,1], 'if ($x || %s) {$x} else {$y}'     ],
298*5759b3d2Safresh1        [ [1,9,2], [0,0,0,1], 'unless ($x || %s) {$x}'           ],
299*5759b3d2Safresh1
300*5759b3d2Safresh1        # INNER DOR LHS
301*5759b3d2Safresh1
302*5759b3d2Safresh1        [ [1,0,2], [0,0],     '%s // $x'                         ],
303*5759b3d2Safresh1        [ [1,1,1], [0,0,0],   '!(%s // $x)'                      ],
304*5759b3d2Safresh1        [ [1,0,2], [0,1,0,0], '$y && (%s // $x)'                 ],
305*5759b3d2Safresh1        [ [1,9,1], [0,0,0,0], 'if (%s // $x) {$x}'               ],
306*5759b3d2Safresh1        [ [1,9,1], [0,0,0,0], 'if (%s // $x) {$x} else {$y}'     ],
307*5759b3d2Safresh1        [ [1,9,2], [0,0,0,0], 'unless (%s // $x) {$x}'           ],
308*5759b3d2Safresh1
309*5759b3d2Safresh1        # INNER DOR RHS
310*5759b3d2Safresh1
311*5759b3d2Safresh1        [ [0,0,0], [0,1],     '$x // %s'                         ],
312*5759b3d2Safresh1        [ [1,1,1], [0,0,1],   '!($x // %s)'                      ],
313*5759b3d2Safresh1        [ [0,0,0], [0,1,0,1], '$y && ($x // %s)'                 ],
314*5759b3d2Safresh1        [ [1,9,1], [0,0,0,1], 'if ($x // %s) {$x}'               ],
315*5759b3d2Safresh1        [ [1,9,1], [0,0,0,1], 'if ($x // %s) {$x} else {$y}'     ],
316*5759b3d2Safresh1        [ [1,9,2], [0,0,0,1], 'unless ($x // %s) {$x}'           ],
317*5759b3d2Safresh1
318*5759b3d2Safresh1        # INNER AND LHS
319*5759b3d2Safresh1
320*5759b3d2Safresh1        [ [1,1,1], [0,0],     '%s && $x'                         ],
321*5759b3d2Safresh1        [ [1,1,1], [0,0,0],   '!(%s && $x)'                      ],
322*5759b3d2Safresh1        [ [1,1,1], [0,1,0,0], '$y || (%s && $x)'                 ],
323*5759b3d2Safresh1        [ [1,9,1], [0,0,0,0], 'if (%s && $x) {$x}'               ],
324*5759b3d2Safresh1        [ [1,9,1], [0,0,0,0], 'if (%s && $x) {$x} else {$y}'     ],
325*5759b3d2Safresh1        [ [1,9,1], [0,0,0,0], 'unless (%s && $x) {$x}'           ],
326*5759b3d2Safresh1
327*5759b3d2Safresh1        # INNER AND RHS
328*5759b3d2Safresh1
329*5759b3d2Safresh1        [ [0,0,0], [0,1],     '$x && %s'                         ],
330*5759b3d2Safresh1        [ [1,1,1], [0,0,1],   '!($x && %s)'                      ],
331*5759b3d2Safresh1        [ [0,0,0], [0,1,0,1], '$y || ($x && %s)'                 ],
332*5759b3d2Safresh1        [ [1,9,1], [0,0,0,1], 'if ($x && %s) {$x}'               ],
333*5759b3d2Safresh1        [ [1,9,1], [0,0,0,1], 'if ($x && %s) {$x} else {$y}'     ],
334*5759b3d2Safresh1        [ [1,9,2], [0,0,0,1], 'unless ($x && %s) {$x}'           ],
335*5759b3d2Safresh1
336*5759b3d2Safresh1        # INNER XOR LHS
337*5759b3d2Safresh1
338*5759b3d2Safresh1            # LHS of XOR is currently too hard to detect as
339*5759b3d2Safresh1            # being in boolean context
340*5759b3d2Safresh1
341*5759b3d2Safresh1        # INNER XOR RHS
342*5759b3d2Safresh1
343*5759b3d2Safresh1        [ [1,1,1], [1],       '($x xor %s)'                      ],
344*5759b3d2Safresh1        [ [1,1,1], [0,1],     '!($x xor %s)'                     ],
345*5759b3d2Safresh1        [ [1,1,1], [0,1,1],   '$y || ($x xor %s)'                ],
346*5759b3d2Safresh1        [ [1,9,1], [0,0,1],   'if ($x xor %s) {$x}'              ],
347*5759b3d2Safresh1        [ [1,9,1], [0,0,1],   'if ($x xor %s) {$x} else {$y}'    ],
348*5759b3d2Safresh1        [ [1,9,1], [0,0,1],   'unless ($x xor %s) {$x}'          ],
349*5759b3d2Safresh1
350*5759b3d2Safresh1        # GREP
351*5759b3d2Safresh1
352*5759b3d2Safresh1        [ [1,1,1], [0,1,0],    'grep(%s,1,2)'                    ],
353*5759b3d2Safresh1        [ [1,1,1], [0,1,0,0],  'grep(!%s,1,2)'                   ],
354*5759b3d2Safresh1        [ [1,1,1], [0,1,0,0,1],'grep($y || %s,1,2)'              ],
355*5759b3d2Safresh1
356*5759b3d2Safresh1        # FLIP
357*5759b3d2Safresh1
358*5759b3d2Safresh1        [ [1,1,1], [0,0,0,0],      '%s..$x'                      ],
359*5759b3d2Safresh1        [ [1,1,1], [0,0,0,0,0],    '!%s..$x'                     ],
360*5759b3d2Safresh1        [ [1,1,1], [0,0,0,0,0,1],  '($y || %s)..$x'              ],
361*5759b3d2Safresh1
362*5759b3d2Safresh1        # FLOP
363*5759b3d2Safresh1
364*5759b3d2Safresh1        [ [1,1,1], [0,0,0,1],      '$x..%s'                      ],
365*5759b3d2Safresh1        [ [1,1,1], [0,0,0,1,0],    '$x..!%s'                     ],
366*5759b3d2Safresh1        [ [1,1,1], [0,0,0,1,0,1],  '$x..($y || %s)'              ],
367*5759b3d2Safresh1
368*5759b3d2Safresh1    ) {
369*5759b3d2Safresh1        my ($expects, $op_path, $code_fmt) = @$test;
370*5759b3d2Safresh1
371*5759b3d2Safresh1        for my $context (0,1,2) {
372*5759b3d2Safresh1            # 0: void
373*5759b3d2Safresh1            # 1: scalar
374*5759b3d2Safresh1            # 2: unknown
375*5759b3d2Safresh1            # 9: skip test (principally if() can't be in scalar context)
376*5759b3d2Safresh1
377*5759b3d2Safresh1            next if $expects->[$context] == 9;
378*5759b3d2Safresh1
379*5759b3d2Safresh1            my $base_code = sprintf $code_fmt, $op_code;
380*5759b3d2Safresh1            my $code = $base_code;
381*5759b3d2Safresh1            my @op_path = @$op_path;
382*5759b3d2Safresh1            push @op_path, @$post_op_path;
383*5759b3d2Safresh1
384*5759b3d2Safresh1            # where to find the expression in the top-level lineseq
385*5759b3d2Safresh1            my $seq_offset = -1;
386*5759b3d2Safresh1
387*5759b3d2Safresh1            if ($context == 0) {
388*5759b3d2Safresh1                $seq_offset -= 2;
389*5759b3d2Safresh1                $code .= "; 1";
390*5759b3d2Safresh1            }
391*5759b3d2Safresh1            elsif ($context == 1) {
392*5759b3d2Safresh1                $code = "\$pkg_result = ($code)";
393*5759b3d2Safresh1                unshift @op_path, 0;
394*5759b3d2Safresh1            }
395*5759b3d2Safresh1
396*5759b3d2Safresh1
397*5759b3d2Safresh1            my $sub;
398*5759b3d2Safresh1            {
399*5759b3d2Safresh1                # don't use 'my' for $pkg_result to avoid the assignment in
400*5759b3d2Safresh1                # '$result = foo()' being optimised away with OPpTARGET_MY
401*5759b3d2Safresh1                our (@pkg, %pkg, $pkg_result);
402*5759b3d2Safresh1                my  (@lex, %lex, $p, $q, $x, $y);
403*5759b3d2Safresh1
404*5759b3d2Safresh1                no warnings 'void';
405*5759b3d2Safresh1                $sub = eval "sub { $code }"
406*5759b3d2Safresh1                    or die
407*5759b3d2Safresh1                        "eval'$code' failed: this test needs to be rewritten;\n"
408*5759b3d2Safresh1                        . "Errors were:\n$@";
409*5759b3d2Safresh1            }
410*5759b3d2Safresh1
411*5759b3d2Safresh1            # find the expression subtree in the main lineseq of the sub
412*5759b3d2Safresh1            my $expr = svref_2object($sub)->ROOT->first;
413*5759b3d2Safresh1            my $orig_expr = $expr;
414*5759b3d2Safresh1            my @ops;
415*5759b3d2Safresh1            my $next = $expr->first;
416*5759b3d2Safresh1            while ($$next) {
417*5759b3d2Safresh1                push @ops, $next;
418*5759b3d2Safresh1                $next = $next->sibling;
419*5759b3d2Safresh1            }
420*5759b3d2Safresh1            $expr = $ops[$seq_offset];
421*5759b3d2Safresh1
422*5759b3d2Safresh1            # search through the expr subtree looking for the named op -
423*5759b3d2Safresh1            # this assumes that for all the code examples above, the
424*5759b3d2Safresh1            # op is always in the LH branch
425*5759b3d2Safresh1            my @orig_op_path = @op_path;
426*5759b3d2Safresh1            while (defined (my $p = shift @op_path)) {
427*5759b3d2Safresh1                eval {
428*5759b3d2Safresh1                    $expr = $expr->first;
429*5759b3d2Safresh1                    $expr = $expr->sibling while $p--;
430*5759b3d2Safresh1                }
431*5759b3d2Safresh1            }
432*5759b3d2Safresh1
433*5759b3d2Safresh1            if (!$expr || !$$expr || $expr->name ne $op_name) {
434*5759b3d2Safresh1                my $optree = dump_optree($orig_expr,2);
435*5759b3d2Safresh1                print STDERR "Can't find $op_name op in optree for '$code'.\n";
436*5759b3d2Safresh1                print STDERR "This test needs to be rewritten\n";
437*5759b3d2Safresh1                print STDERR "seq_offset=$seq_offset op_path=(@orig_op_path)\n";
438*5759b3d2Safresh1                print STDERR "optree=\n$optree";
439*5759b3d2Safresh1                exit 1;
440*5759b3d2Safresh1            }
441*5759b3d2Safresh1
442*5759b3d2Safresh1            my $exp = $expects->[$context];
443*5759b3d2Safresh1            $exp =   $exp == 0 ? 0
444*5759b3d2Safresh1                   : $exp == 1 ? $bool_flag
445*5759b3d2Safresh1                   :             $maybe_flag;
446*5759b3d2Safresh1
447*5759b3d2Safresh1            my $got = ($expr->private & ($bool_flag | $maybe_flag));
448*5759b3d2Safresh1            my $cxt_name = ('void   ', 'scalar ', 'unknown')[$context];
449*5759b3d2Safresh1            is $got, $exp,  "boolean: $op_name $cxt_name '$base_code'";
450*5759b3d2Safresh1        }
451*5759b3d2Safresh1    }
452*5759b3d2Safresh1}
453*5759b3d2Safresh1
454