1#!perl
2
3BEGIN {
4    unshift @INC, 't';
5    require Config;
6    if (($Config::Config{'extensions'} !~ /\bB\b/) ){
7        print "1..0 # Skip -- Perl configured without B module\n";
8        exit 0;
9    }
10    if (!$Config::Config{useperlio}) {
11        print "1..0 # Skip -- need perlio to walk the optree\n";
12        exit 0;
13    }
14}
15
16use OptreeCheck;	# ALSO DOES @ARGV HANDLING !!!!!!
17use Config;
18
19plan tests => 99;
20
21#################################
22
23my sub lleexx {}
24sub tsub0 {}
25sub tsub1 {} $tsub1 = 1;
26sub t::tsub2 {}
27sub t::tsub3 {} $tsub3 = 1;
28{
29    package t;
30    sub tsub4 {}
31    sub tsub5 {} $tsub5 = 1;
32}
33
34use constant {		# see also t/op/gv.t line 358
35    myaref	=> [ 1,2,3 ],
36    myfl	=> 1.414213,
37    myglob	=> \*STDIN,
38    myhref	=> { a	=> 1 },
39    myint	=> 42,
40    myrex	=> qr/foo/,
41    mystr	=> 'hithere',
42    mysub	=> \&ok,
43    myundef	=> undef,
44    myunsub	=> \&nosuch,
45    myanonsub	=> sub {},
46    mylexsub	=> \&lleexx,
47    tsub0	=> \&tsub0,
48    tsub1	=> \&tsub1,
49    tsub2	=> \&t::tsub2,
50    tsub3	=> \&t::tsub3,
51    tsub4	=> \&t::tsub4,
52    tsub5	=> \&t::tsub5,
53};
54
55sub myyes() { 1==1 }
56sub myno () { return 1!=1 }
57sub pi () { 3.14159 };
58
59my $want = {	# expected types, how value renders in-line, todos (maybe)
60    mystr	=> [ 'PV', '"'.mystr.'"' ],
61    myhref	=> [ 'IV', '\\\\HASH'],
62    pi		=> [ 'NV', pi ],
63    myglob	=> [ 'IV', '\\\\' ],
64    mysub	=> [ 'IV', '\\\\&main::ok' ],
65    myunsub	=> [ 'IV', '\\\\&main::nosuch' ],
66    myanonsub	=> [ 'IV', '\\\\CODE' ],
67    mylexsub	=> [ 'IV', '\\\\&lleexx' ],
68    tsub0	=> [ 'IV', '\\\\&main::tsub0' ],
69    tsub1	=> [ 'IV', '\\\\&main::tsub1' ],
70    tsub2	=> [ 'IV', '\\\\&t::tsub2' ],
71    tsub3	=> [ 'IV', '\\\\&t::tsub3' ],
72    tsub4	=> [ 'IV', '\\\\&t::tsub4' ],
73    tsub5	=> [ 'IV', '\\\\&t::tsub5' ],
74    # these are not inlined, at least not per BC::Concise
75    #myyes	=> [ 'IV', ],
76    #myno	=> [ 'IV', ],
77    myaref	=> [ 'IV', '\\\\ARRAY' ],
78    myfl	=> [ 'NV', myfl ],
79    myint	=> [ 'IV', myint ],
80    myrex	=> [ 'IV', '\\\\"\\(?^:Foo\\)"' ],
81    myundef	=> [ 'NULL', ],
82};
83
84use constant WEEKDAYS
85    => qw ( Sunday Monday Tuesday Wednesday Thursday Friday Saturday );
86
87
88$::{napier} = \2.71828;	# counter-example (doesn't get optimized).
89eval "sub napier ();";
90
91
92# should be able to undefine constant::import here ???
93INIT {
94    # eval 'sub constant::import () {}';
95    # undef *constant::import::{CODE};
96};
97
98#################################
99pass("RENDER CONSTANT SUBS RETURNING SCALARS");
100
101for $func (sort keys %$want) {
102    # no strict 'refs';	# why not needed ?
103    checkOptree ( name      => "$func() as a coderef",
104		  code      => \&{$func},
105		  noanchors => 1,
106		  expect    => <<EOT_EOT, expect_nt => <<EONT_EONT);
107 is a constant sub, optimized to a $want->{$func}[0]
108EOT_EOT
109 is a constant sub, optimized to a $want->{$func}[0]
110EONT_EONT
111
112}
113
114pass("RENDER CALLS TO THOSE CONSTANT SUBS");
115
116for $func (sort keys %$want) {
117    # print "# doing $func\n";
118    checkOptree ( name    => "call $func",
119		  code    => "$func",
120		  ($want->{$func}[2]) ? ( todo => $want->{$func}[2]) : (),
121		  bc_opts => '-nobanner',
122		  expect  => <<EOT_EOT, expect_nt => <<EONT_EONT);
1233  <1> leavesub[2 refs] K/REFC,1 ->(end)
124-     <\@> lineseq KP ->3
1251        <;> dbstate(main 833 (eval 44):1) v ->2
1262        <\$> const[$want->{$func}[0] $want->{$func}[1]] s*/FOLD ->3
127EOT_EOT
1283  <1> leavesub[2 refs] K/REFC,1 ->(end)
129-     <\@> lineseq KP ->3
1301        <;> dbstate(main 833 (eval 44):1) v ->2
1312        <\$> const($want->{$func}[0] $want->{$func}[1]) s*/FOLD ->3
132EONT_EONT
133
134}
135
136##############
137pass("MORE TESTS");
138
139checkOptree ( name	=> 'myyes() as coderef',
140	      code	=> sub () { 1==1 },
141	      noanchors => 1,
142	      expect	=> <<'EOT_EOT', expect_nt => <<'EONT_EONT');
143 is a constant sub, optimized to a SPECIAL
144EOT_EOT
145 is a constant sub, optimized to a SPECIAL
146EONT_EONT
147
148
149checkOptree ( name	=> 'myyes() as coderef',
150	      prog	=> 'sub a() { 1==1 }; print a',
151	      noanchors => 1,
152	      strip_open_hints => 1,
153	      expect	=> <<'EOT_EOT', expect_nt => <<'EONT_EONT');
154# 6  <@> leave[1 ref] vKP/REFC ->(end)
155# 1     <0> enter v ->2
156# 2     <;> nextstate(main 2 -e:1) v:>,<,%,{ ->3
157# 5     <@> print vK ->6
158# 3        <0> pushmark s ->4
159# 4        <$> const[SPECIAL sv_yes] s*/FOLD ->5
160EOT_EOT
161# 6  <@> leave[1 ref] vKP/REFC ->(end)
162# 1     <0> enter v ->2
163# 2     <;> nextstate(main 2 -e:1) v:>,<,%,{ ->3
164# 5     <@> print vK ->6
165# 3        <0> pushmark s ->4
166# 4        <$> const(SPECIAL sv_yes) s*/FOLD ->5
167EONT_EONT
168
169
170# Need to do this as a prog, not code, as only the first constant to use
171# PL_sv_no actually gets to use the real thing - every one following is
172# copied.
173checkOptree ( name	=> 'myno() as coderef',
174	      prog	=> 'sub a() { 1!=1 }; print a',
175	      noanchors => 1,
176	      strip_open_hints => 1,
177	      expect	=> <<'EOT_EOT', expect_nt => <<'EONT_EONT');
178# 6  <@> leave[1 ref] vKP/REFC ->(end)
179# 1     <0> enter v ->2
180# 2     <;> nextstate(main 2 -e:1) v:>,<,%,{ ->3
181# 5     <@> print vK ->6
182# 3        <0> pushmark s ->4
183# 4        <$> const[SPECIAL sv_no] s*/FOLD ->5
184EOT_EOT
185# 6  <@> leave[1 ref] vKP/REFC ->(end)
186# 1     <0> enter v ->2
187# 2     <;> nextstate(main 2 -e:1) v:>,<,%,{ ->3
188# 5     <@> print vK ->6
189# 3        <0> pushmark s ->4
190# 4        <$> const(SPECIAL sv_no) s*/FOLD ->5
191EONT_EONT
192
193
194my ($expect, $expect_nt) = (" is a constant sub, optimized to a AV\n") x 2;
195
196
197checkOptree ( name	=> 'constant sub returning list',
198	      code	=> \&WEEKDAYS,
199	      noanchors => 1,
200	      expect => $expect, expect_nt => $expect_nt);
201
202
203sub printem {
204    printf "myint %d mystr %s myfl %f pi %f\n"
205	, myint, mystr, myfl, pi;
206}
207
208my ($expect, $expect_nt) = (<<'EOT_EOT', <<'EONT_EONT');
209# 9  <1> leavesub[1 ref] K/REFC,1 ->(end)
210# -     <@> lineseq KP ->9
211# 1        <;> nextstate(main 635 optree_constants.t:163) v:>,<,% ->2
212# 8        <@> prtf sK ->9
213# 2           <0> pushmark sM ->3
214# 3           <$> const[PV "myint %d mystr %s myfl %f pi %f\n"] sM/FOLD ->4
215# 4           <$> const[IV 42] sM*/FOLD ->5
216# 5           <$> const[PV "hithere"] sM*/FOLD ->6
217# 6           <$> const[NV 1.414213] sM*/FOLD ->7
218# 7           <$> const[NV 3.14159] sM*/FOLD ->8
219EOT_EOT
220# 9  <1> leavesub[1 ref] K/REFC,1 ->(end)
221# -     <@> lineseq KP ->9
222# 1        <;> nextstate(main 635 optree_constants.t:163) v:>,<,% ->2
223# 8        <@> prtf sK ->9
224# 2           <0> pushmark sM ->3
225# 3           <$> const(PV "myint %d mystr %s myfl %f pi %f\n") sM/FOLD ->4
226# 4           <$> const(IV 42) sM*/FOLD ->5
227# 5           <$> const(PV "hithere") sM*/FOLD ->6
228# 6           <$> const(NV 1.414213) sM*/FOLD ->7
229# 7           <$> const(NV 3.14159) sM*/FOLD ->8
230EONT_EONT
231
232s|\\n"[])] sM\K/FOLD|| for $expect, $expect_nt;
233
234checkOptree ( name	=> 'call many in a print statement',
235	      code	=> \&printem,
236	      strip_open_hints => 1,
237	      expect => $expect, expect_nt => $expect_nt);
238
239# test constant expression folding
240
241checkOptree ( name	=> 'arithmetic constant folding in print',
242	      code	=> 'print 1+2+3',
243	      strip_open_hints => 1,
244	      expect => <<'EOT_EOT', expect_nt => <<'EONT_EONT');
245# 5  <1> leavesub[1 ref] K/REFC,1 ->(end)
246# -     <@> lineseq KP ->5
247# 1        <;> nextstate(main 937 (eval 53):1) v ->2
248# 4        <@> print sK ->5
249# 2           <0> pushmark s ->3
250# 3           <$> const[IV 6] s/FOLD ->4
251EOT_EOT
252# 5  <1> leavesub[1 ref] K/REFC,1 ->(end)
253# -     <@> lineseq KP ->5
254# 1        <;> nextstate(main 937 (eval 53):1) v ->2
255# 4        <@> print sK ->5
256# 2           <0> pushmark s ->3
257# 3           <$> const(IV 6) s/FOLD ->4
258EONT_EONT
259
260checkOptree ( name	=> 'string constant folding in print',
261	      code	=> 'print "foo"."bar"',
262	      strip_open_hints => 1,
263	      expect => <<'EOT_EOT', expect_nt => <<'EONT_EONT');
264# 5  <1> leavesub[1 ref] K/REFC,1 ->(end)
265# -     <@> lineseq KP ->5
266# 1        <;> nextstate(main 942 (eval 55):1) v ->2
267# 4        <@> print sK ->5
268# 2           <0> pushmark s ->3
269# 3           <$> const[PV "foobar"] s/FOLD ->4
270EOT_EOT
271# 5  <1> leavesub[1 ref] K/REFC,1 ->(end)
272# -     <@> lineseq KP ->5
273# 1        <;> nextstate(main 942 (eval 55):1) v ->2
274# 4        <@> print sK ->5
275# 2           <0> pushmark s ->3
276# 3           <$> const(PV "foobar") s/FOLD ->4
277EONT_EONT
278
279checkOptree ( name	=> 'boolean or folding',
280	      code	=> 'print "foobar" if 1 or 0',
281	      strip_open_hints => 1,
282	      expect => <<'EOT_EOT', expect_nt => <<'EONT_EONT');
283# 5  <1> leavesub[1 ref] K/REFC,1 ->(end)
284# -     <@> lineseq KP ->5
285# 1        <;> nextstate(main 942 (eval 55):1) v ->2
286# 4        <@> print sK/FOLD ->5
287# 2           <0> pushmark s ->3
288# 3           <$> const[PV "foobar"] s ->4
289EOT_EOT
290# 5  <1> leavesub[1 ref] K/REFC,1 ->(end)
291# -     <@> lineseq KP ->5
292# 1        <;> nextstate(main 942 (eval 55):1) v ->2
293# 4        <@> print sK/FOLD ->5
294# 2           <0> pushmark s ->3
295# 3           <$> const(PV "foobar") s ->4
296EONT_EONT
297
298checkOptree ( name	=> 'lc*,uc*,gt,lt,ge,le,cmp',
299	      code	=> sub {
300		  $s = uc('foo.').ucfirst('bar.').lc('LOW.').lcfirst('LOW');
301		  print "a-lt-b" if "a" lt "b";
302		  print "b-gt-a" if "b" gt "a";
303		  print "a-le-b" if "a" le "b";
304		  print "b-ge-a" if "b" ge "a";
305		  print "b-cmp-a" if "b" cmp "a";
306		  print "a-gt-b" if "a" gt "b";	# should be suppressed
307	      },
308	      strip_open_hints => 1,
309	      expect => <<'EOT_EOT', expect_nt => <<'EONT_EONT');
310# r  <1> leavesub[1 ref] K/REFC,1 ->(end)
311# -     <@> lineseq KP ->r
312# 1        <;> nextstate(main 916 optree_constants.t:307) v:>,<,%,{ ->2
313# 4        <2> sassign vKS/2 ->5
314# 2           <$> const[PV "FOO.Bar.low.lOW"] s/FOLD ->3
315# -           <1> ex-rv2sv sKRM*/1 ->4
316# 3              <#> gvsv[*s] s ->4
317# 5        <;> nextstate(main 916 optree_constants.t:308) v:>,<,%,{ ->6
318# 8        <@> print vK/FOLD ->9
319# 6           <0> pushmark s ->7
320# 7           <$> const[PV "a-lt-b"] s ->8
321# 9        <;> nextstate(main 916 optree_constants.t:309) v:>,<,%,{ ->a
322# c        <@> print vK/FOLD ->d
323# a           <0> pushmark s ->b
324# b           <$> const[PV "b-gt-a"] s ->c
325# d        <;> nextstate(main 916 optree_constants.t:310) v:>,<,%,{ ->e
326# g        <@> print vK/FOLD ->h
327# e           <0> pushmark s ->f
328# f           <$> const[PV "a-le-b"] s ->g
329# h        <;> nextstate(main 916 optree_constants.t:311) v:>,<,%,{ ->i
330# k        <@> print vK/FOLD ->l
331# i           <0> pushmark s ->j
332# j           <$> const[PV "b-ge-a"] s ->k
333# l        <;> nextstate(main 916 optree_constants.t:312) v:>,<,%,{ ->m
334# o        <@> print vK/FOLD ->p
335# m           <0> pushmark s ->n
336# n           <$> const[PV "b-cmp-a"] s ->o
337# p        <;> nextstate(main 916 optree_constants.t:313) v:>,<,%,{ ->q
338# q        <$> const[SPECIAL sv_no] s/SHORT,FOLD ->r
339EOT_EOT
340# r  <1> leavesub[1 ref] K/REFC,1 ->(end)
341# -     <@> lineseq KP ->r
342# 1        <;> nextstate(main 916 optree_constants.t:307) v:>,<,%,{ ->2
343# 4        <2> sassign vKS/2 ->5
344# 2           <$> const(PV "FOO.Bar.low.lOW") s/FOLD ->3
345# -           <1> ex-rv2sv sKRM*/1 ->4
346# 3              <$> gvsv(*s) s ->4
347# 5        <;> nextstate(main 916 optree_constants.t:308) v:>,<,%,{ ->6
348# 8        <@> print vK/FOLD ->9
349# 6           <0> pushmark s ->7
350# 7           <$> const(PV "a-lt-b") s ->8
351# 9        <;> nextstate(main 916 optree_constants.t:309) v:>,<,%,{ ->a
352# c        <@> print vK/FOLD ->d
353# a           <0> pushmark s ->b
354# b           <$> const(PV "b-gt-a") s ->c
355# d        <;> nextstate(main 916 optree_constants.t:310) v:>,<,%,{ ->e
356# g        <@> print vK/FOLD ->h
357# e           <0> pushmark s ->f
358# f           <$> const(PV "a-le-b") s ->g
359# h        <;> nextstate(main 916 optree_constants.t:311) v:>,<,%,{ ->i
360# k        <@> print vK/FOLD ->l
361# i           <0> pushmark s ->j
362# j           <$> const(PV "b-ge-a") s ->k
363# l        <;> nextstate(main 916 optree_constants.t:312) v:>,<,%,{ ->m
364# o        <@> print vK/FOLD ->p
365# m           <0> pushmark s ->n
366# n           <$> const(PV "b-cmp-a") s ->o
367# p        <;> nextstate(main 916 optree_constants.t:313) v:>,<,%,{ ->q
368# q        <$> const(SPECIAL sv_no) s/SHORT,FOLD ->r
369EONT_EONT
370
371checkOptree ( name	=> 'mixed constant folding, with explicit braces',
372	      code	=> 'print "foo"."bar".(2+3)',
373	      strip_open_hints => 1,
374	      expect => <<'EOT_EOT', expect_nt => <<'EONT_EONT');
375# 5  <1> leavesub[1 ref] K/REFC,1 ->(end)
376# -     <@> lineseq KP ->5
377# 1        <;> nextstate(main 977 (eval 28):1) v ->2
378# 4        <@> print sK ->5
379# 2           <0> pushmark s ->3
380# 3           <$> const[PV "foobar5"] s/FOLD ->4
381EOT_EOT
382# 5  <1> leavesub[1 ref] K/REFC,1 ->(end)
383# -     <@> lineseq KP ->5
384# 1        <;> nextstate(main 977 (eval 28):1) v ->2
385# 4        <@> print sK ->5
386# 2           <0> pushmark s ->3
387# 3           <$> const(PV "foobar5") s/FOLD ->4
388EONT_EONT
389
390__END__
391
392=head NB
393
394Optimized constant subs are stored as bare scalars in the stash
395(package hash), which formerly held only GVs (typeglobs).
396
397But you cant create them manually - you cant assign a scalar to a
398stash element, and expect it to work like a constant-sub, even if you
399provide a prototype.
400
401This is a feature; alternative is too much action-at-a-distance.  The
402following test demonstrates - napier is not seen as a function at all,
403much less an optimized one.
404
405=cut
406
407checkOptree ( name	=> 'not evertnapier',
408	      code	=> \&napier,
409	      noanchors => 1,
410	      expect	=> <<'EOT_EOT', expect_nt => <<'EONT_EONT');
411 has no START
412EOT_EOT
413 has no START
414EONT_EONT
415
416
417