ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Faster/Faster.pm
(Generate patch)

Comparing Faster/Faster.pm (file contents):
Revision 1.13 by root, Fri Mar 10 18:53:49 2006 UTC vs.
Revision 1.35 by root, Sat Feb 21 05:55:52 2009 UTC

4 4
5=head1 SYNOPSIS 5=head1 SYNOPSIS
6 6
7 use Faster; 7 use Faster;
8 8
9 perl -MFaster ...
10
9=head1 DESCRIPTION 11=head1 DESCRIPTION
10 12
13This module implements a very simple-minded "JIT" (or actually AIT, ahead
14of time compiler). It works by more or less translating every function it
15sees into a C program, compiling it and then replacing the function by the
16compiled code.
17
18As a result, startup times are immense, as every function might lead to a
19full-blown compilation.
20
21The speed improvements are also not great, you can expect 20% or so on
22average, for code that runs very often. The reason for this is that data
23handling is mostly being done by the same old code, it just gets called
24a bit faster. Regexes and string operations won't get faster. Airhtmetic
25doresn't become any faster. Just the operands and other stuff is put on
26the stack faster, and the opcodes themselves have a bit less overhead.
27
28Faster is in the early stages of development. Due to its design its
29relatively safe to use (it will either work or simply slowdown the program
30immensely, but rarely cause bugs).
31
32More intelligent algorithms (loop optimisation, type inference) could
33improve that easily, but requires a much more elaborate presentation and
34optimiser than what is in place. There are no plans to improve Faster in
35this way, yet, but it would provide a reasonably good place to start.
36
37Usage is very easy, just C<use Faster> and every function called from then
38on will be compiled.
39
40Right now, Faster can leave lots of F<*.c> and F<*.so> files in your
41F<$FASTER_CACHEDIR> (by default F<$HOME/.perl-faster-cache>), and it will
42even create those temporary files in an insecure manner, so watch out.
43
11=over 4 44=over 4
12 45
13=cut 46=cut
14 47
15package Faster; 48package Faster;
49
50no warnings;
16 51
17use strict; 52use strict;
18use Config; 53use Config;
19use B (); 54use B ();
55use DynaLoader ();
20use Digest::MD5 (); 56use Digest::MD5 ();
21use DynaLoader (); 57use Storable ();
58use Fcntl ();
22 59
23BEGIN { 60BEGIN {
24 our $VERSION = '0.01'; 61 our $VERSION = '0.01';
25 62
26 require XSLoader; 63 require XSLoader;
27 XSLoader::load __PACKAGE__, $VERSION; 64 XSLoader::load __PACKAGE__, $VERSION;
28} 65}
29 66
67my $CACHEDIR =
68 $ENV{FASTER_CACHE}
69 || (exists $ENV{HOME} && "$ENV{HOME}/.perl-faster-cache")
70 || do {
71 require File::Temp;
72 File::Temp::tempdir (CLEANUP => 1)
73 };
74
30my $COMPILE = "$Config{cc} -c -I$Config{archlibexp}/CORE $Config{optimize} $Config{ccflags} $Config{cccdlflags}"; 75my $COMPILE = "$Config{cc} -c -I$Config{archlibexp}/CORE $Config{optimize} $Config{ccflags} $Config{cccdlflags}";
31my $LINK = "$Config{ld} $Config{ldflags} $Config{lddlflags} $Config{ccdlflags}"; 76my $LINK = "$Config{ld} $Config{ldflags} $Config{lddlflags} $Config{ccdlflags}";
32my $LIBS = "$Config{libs}"; 77my $LIBS = "";
33my $_o = $Config{_o}; 78my $_o = $Config{_o};
34my $_so = ".so"; 79my $_so = ".so";
35 80
36# we don't need no steenking PIC on x86 81# we don't need no steenking PIC on x86
37$COMPILE =~ s/-f(?:PIC|pic)//g 82$COMPILE =~ s/-f(?:PIC|pic)//g
38 if $Config{archname} =~ /^(i[3456]86)-/; 83 if $Config{archname} =~ /^(i[3456]86)-/;
39 84
40my $opt_assert = 1; 85my $opt_assert = $ENV{FASTER_DEBUG} & 2;
86my $verbose = $ENV{FASTER_VERBOSE}+0;
87
88warn "Faster: CACHEDIR is $CACHEDIR\n" if $verbose > 2;
41 89
42our $source; 90our $source;
43 91
44my @ops; 92our @ops;
45my $op; 93our $insn;
94our $op;
46my $op_name; 95our $op_name;
47my @loop; 96our %op_regcomp;
48 97
49my %flag; 98# ops that cause immediate return to the interpreter
99my %f_unsafe = map +($_ => undef), qw(
100 leavesub leavesublv return
101 goto last redo next
102 eval flip leaveeval entertry
103 formline grepstart mapstart
104 substcont entereval require
105);
50 106
51# complex flag steting is no longer required, rewrite this ugly code 107# ops with known stack extend behaviour
52for (split /\n/, <<EOF) { 108# the values given are maximum values
53 leavesub unsafe 109my %extend = (
54 leavesublv unsafe 110 pushmark => 0,
55 return unsafe 111 nextstate => 0, # might reduce the stack
56 flip unsafe 112 unstack => 0,
57 goto unsafe 113 enter => 0,
58 last unsafe
59 redo unsafe
60 next unsafe
61 eval unsafe
62 leaveeval unsafe
63 entertry unsafe
64 formline unsafe
65 grepstart unsafe
66 mapstart unsafe
67 substcont unsafe
68 entereval unsafe noasync todo
69 require unsafe
70 114
71 mapstart noasync 115 stringify => 0,
72 grepstart noasync 116 not => 0,
73 match noasync 117 and => 0,
118 or => 0,
119 gvsv => 0,
120 rv2gv => 0,
121 preinc => 0,
122 predec => 0,
123 postinc => 0,
124 postdec => 0,
125 aelem => 0,
126 helem => 0,
127 qr => 1, #???
128 pushre => 1,
129 gv => 1,
130 aelemfast => 1,
131 aelem => 0,
132 padsv => 1,
133 const => 1,
134 pop => 1,
135 shift => 1,
136 eq => -1,
137 ne => -1,
138 gt => -1,
139 lt => -1,
140 ge => -1,
141 lt => -1,
142 cond_expr => -1,
143 add => -1,
144 subtract => -1,
145 multiply => -1,
146 divide => -1,
147 aassign => 0,
148 sassign => -2,
149 method => 0,
150 method_named => 1,
151);
74 152
75 last noasync 153# ops that do not need an ASYNC_CHECK
76 next noasync 154my %f_noasync = map +($_ => undef), qw(
77 redo noasync 155 mapstart grepstart match entereval
78 seq noasync 156 enteriter entersub leaveloop
79 pushmark noasync extend=0
80 padsv noasync extend=1
81 padav noasync extend=1
82 padhv noasync extend=1
83 padany noasync extend=1
84 entersub noasync
85 aassign noasync
86 sassign noasync
87 rv2av noasync
88 rv2cv noasync
89 rv2gv noasync
90 rv2hv noasync
91 refgen noasync
92 nextstate noasync
93 gv noasync
94 gvsv noasync
95 add noasync
96 subtract noasync
97 multiply noasync
98 divide noasync
99 complement noasync
100 cond_expr noasync
101 and noasync
102 or noasync
103 not noasync
104 defined noasync
105 method_named noasync
106 preinc noasync
107 postinc noasync
108 predec noasync
109 postdec noasync
110 stub noasync
111 unstack noasync
112 leaveloop noasync
113 aelem noasync
114 aelemfast noasync
115 helem noasync
116 pushre noasync
117 subst noasync
118 const noasync extend=1
119 list noasync
120 join noasync
121 split noasync
122 concat noasync
123 push noasync
124 pop noasync
125 shift noasync
126 unshift noasync
127 length noasync
128 substr noasync
129 stringify noasync
130 eq noasync
131 ne noasync
132 gt noasync
133 lt noasync
134 ge noasync
135 le noasync
136 enteriter noasync
137 ord noasync
138 157
139 iter async 158 pushmark nextstate caller
140EOF
141 my (undef, $op, @flags) = split /\s+/;
142 159
143 undef $flag{$_}{$op} 160 const stub unstack
144 for ("known", @flags); 161 last next redo goto seq
145} 162 padsv padav padhv padany
163 aassign sassign orassign
164 rv2av rv2cv rv2gv rv2hv refgen
165 gv gvsv
166 add subtract multiply divide
167 complement cond_expr and or not
168 bit_and bit_or bit_xor
169 defined
170 method method_named bless
171 preinc postinc predec postdec
172 aelem aelemfast helem delete exists
173 pushre subst list lslice join split concat
174 length substr stringify ord
175 push pop shift unshift
176 eq ne gt lt ge le
177 regcomp regcreset regcmaybe
178);
146 179
147my %callop = ( 180my %callop = (
148 entersub => "(PL_ppaddr [OP_ENTERSUB]) (aTHX)", 181 entersub => "(PL_op->op_ppaddr) (aTHX)",
149 mapstart => "Perl_pp_grepstart (aTHX)", 182 mapstart => "Perl_pp_grepstart (aTHX)",
150); 183);
151 184
152sub callop { 185sub callop {
153 $callop{$op_name} || "Perl_pp_$op_name (aTHX)" 186 $callop{$op_name} || "Perl_pp_$op_name (aTHX)"
161sub out_callop { 194sub out_callop {
162 assert "nextop == (OP *)$$op"; 195 assert "nextop == (OP *)$$op";
163 $source .= " PL_op = nextop; nextop = " . (callop $op) . ";\n"; 196 $source .= " PL_op = nextop; nextop = " . (callop $op) . ";\n";
164} 197}
165 198
199sub out_jump {
200 assert "nextop == (OP *)${$_[0]}L";
201 $source .= " goto op_${$_[0]};\n";
202}
203
204sub out_cond_jump {
205 $source .= " if (nextop == (OP *)${$_[0]}L) goto op_${$_[0]};\n";
206}
207
166sub out_jump_next { 208sub out_jump_next {
209 out_cond_jump $op_regcomp{$$op}
210 if $op_regcomp{$$op};
211
167 assert "nextop == (OP *)${$op->next}"; 212 assert "nextop == (OP *)${$op->next}";
168 $source .= " goto op_${$op->next};\n"; 213 $source .= " goto op_${$op->next};\n";
169} 214}
170 215
171sub out_next { 216sub out_next {
175} 220}
176 221
177sub out_linear { 222sub out_linear {
178 out_callop; 223 out_callop;
179 out_jump_next; 224 out_jump_next;
180}
181
182sub out_cond_jump {
183 $source .= " if (nextop == (OP *)${$_[0]}L) goto op_${$_[0]};\n";
184} 225}
185 226
186sub op_entersub { 227sub op_entersub {
187 out_callop; 228 out_callop;
188 $source .= " RUNOPS_TILL ((OP *)${$op->next}L);\n"; 229 $source .= " RUNOPS_TILL ((OP *)${$op->next}L);\n";
198 239
199 out_next; 240 out_next;
200} 241}
201 242
202sub op_pushmark { 243sub op_pushmark {
203 $source .= " PUSHMARK (PL_stack_sp);\n"; 244 $source .= " faster_PUSHMARK (PL_stack_sp);\n";
204 245
205 out_next; 246 out_next;
206} 247}
207 248
208if ($Config{useithreads} ne "define") { 249if ($Config{useithreads} ne "define") {
209 # disable optimisations on ithreads 250 # disable optimisations on ithreads
210 251
211 *op_const = sub { 252 *op_const = sub {
212 $source .= " { dSP; XPUSHs ((SV *)${$op->sv}L); PUTBACK; }\n"; 253 $source .= " { dSP; PUSHs ((SV *)${$op->sv}L); PUTBACK; }\n";
254
255 $ops[0]{follows_const}++ if @ops;#d#
213 256
214 out_next; 257 out_next;
215 }; 258 };
216 259
217 *op_gv = \&op_const; 260 *op_gv = \&op_const;
237 if (!($op->flags & B::OPf_MOD)) { 280 if (!($op->flags & B::OPf_MOD)) {
238 $source .= " if (SvGMAGICAL (sv)) sv = sv_mortalcopy (sv);\n"; 281 $source .= " if (SvGMAGICAL (sv)) sv = sv_mortalcopy (sv);\n";
239 } 282 }
240 283
241 $source .= " dSP;\n"; 284 $source .= " dSP;\n";
242 $source .= " XPUSHs (sv);\n"; 285 $source .= " PUSHs (sv);\n";
243 $source .= " PUTBACK;\n"; 286 $source .= " PUTBACK;\n";
244 $source .= " }\n"; 287 $source .= " }\n";
245 288
246 out_next; 289 out_next;
247 }; 290 };
248 291
249 *op_gvsv = sub { 292 *op_gvsv = sub {
250 $source .= " {\n"; 293 $source .= " {\n";
251 $source .= " dSP;\n"; 294 $source .= " dSP;\n";
252 $source .= " EXTEND (SP, 1);\n";
253 295
254 if ($op->private & B::OPpLVAL_INTRO) { 296 if ($op->private & B::OPpLVAL_INTRO) {
255 $source .= " PUSHs (save_scalar ((GV *)${$op->sv}L));\n"; 297 $source .= " PUSHs (save_scalar ((GV *)${$op->sv}L));\n";
256 } else { 298 } else {
257 $source .= " PUSHs (GvSV ((GV *)${$op->sv}L));\n"; 299 $source .= " PUSHs (GvSV ((GV *)${$op->sv}L));\n";
317 out_next; 359 out_next;
318} 360}
319 361
320sub op_padsv { 362sub op_padsv {
321 my $flags = $op->flags; 363 my $flags = $op->flags;
322 my $target = $op->targ; 364 my $padofs = "(PADOFFSET)" . $op->targ;
323 365
324 $source .= <<EOF; 366 $source .= <<EOF;
325 { 367 {
326 dSP; 368 dSP;
327 XPUSHs (PAD_SV ((PADOFFSET)$target)); 369 SV *sv = PAD_SVl ($padofs);
370EOF
371
372 if (($flags & B::OPf_MOD) && ($op->private & B::OPpLVAL_INTRO)) {
373 $source .= " SAVECLEARSV (PAD_SVl ($padofs));\n";
374 $ops[0]{follows_padsv_lval_intro}++ if @ops;#d#
375 }
376
377 $source .= <<EOF;
378 PUSHs (sv);
328 PUTBACK; 379 PUTBACK;
329EOF 380EOF
330 if ($op->flags & B::OPf_MOD) { 381
331 if ($op->private & B::OPpLVAL_INTRO) { 382 if (($flags & B::OPf_MOD) && ($op->private & B::OPpDEREF)) {
332 $source .= " SAVECLEARSV (PAD_SVl ((PADOFFSET)$target));\n"; 383 $source .= " if (!SvROK (sv)) vivify_ref (sv, " . $op->private . " & OPpDEREF);\n";
333 } elsif ($op->private & B::OPpDEREF) {
334 my $deref = $op->private & B::OPpDEREF;
335 $source .= " Perl_vivify_ref (PAD_SVl ((PADOFFSET)$target), $deref);\n";
336 }
337 } 384 }
385 $source .= " }\n";
386
387 out_next;
388}
389
390sub op_sassign {
391 $source .= <<EOF;
392 {
393 dSP;
394 dPOPTOPssrl;
395EOF
396 $source .= " SV *temp = left; left = right; right = temp;\n"
397 if $op->private & B::OPpASSIGN_BACKWARDS;
398
399 if ($insn->{follows_padsv_lval_intro} && !($op->private & B::OPpASSIGN_BACKWARDS)) {
400 # simple assignment - the target exists, but is basically undef
401 $source .= " SvSetSV (right, left);\n";
402 } else {
403 $source .= " SvSetMagicSV (right, left);\n";
404 }
405
338 $source .= <<EOF; 406 $source .= <<EOF;
407 SETs (right);
408 PUTBACK;
339 } 409 }
340EOF 410EOF
341 411
342 out_next; 412 out_next;
343} 413}
344 414
345# pattern const+ (or general push1) 415# pattern const+ (or general push1)
346# pattern pushmark return(?)
347# pattern pushmark gv rv2av pushmark padsv+o.ä. aassign 416# pattern pushmark gv rv2av pushmark padsv+o.ä. aassign
348 417
349# pattern const method_named
350sub op_method_named { 418sub op_method_named {
419 if ($insn->{follows_const}) {
351 $source .= <<EOF; 420 $source .= <<EOF;
421 {
422 dSP;
423 static SV *last_cv;
424 static U32 last_sub_generation;
425
426 /* simple "polymorphic" inline cache */
427 if (PL_sub_generation == last_sub_generation)
428 {
429 PUSHs (last_cv);
430 PUTBACK;
431 }
432 else
433 {
434 PL_op = nextop; nextop = Perl_pp_method_named (aTHX);
435
436 SPAGAIN;
437 last_sub_generation = PL_sub_generation;
438 last_cv = TOPs;
439 }
440 }
441EOF
442 } else {
443 $source .= <<EOF;
352 { 444 {
353 static HV *last_stash; 445 static HV *last_stash;
354 static SV *last_cv; 446 static SV *last_cv;
355 static U32 last_sub_generation; 447 static U32 last_sub_generation;
356 448
363 455
364 /* simple "polymorphic" inline cache */ 456 /* simple "polymorphic" inline cache */
365 if (stash == last_stash 457 if (stash == last_stash
366 && PL_sub_generation == last_sub_generation) 458 && PL_sub_generation == last_sub_generation)
367 { 459 {
368 XPUSHs (last_cv); 460 PUSHs (last_cv);
369 PUTBACK; 461 PUTBACK;
370 } 462 }
371 else 463 else
372 { 464 {
373 PL_op = nextop; nextop = Perl_pp_method_named (aTHX); 465 PL_op = nextop; nextop = Perl_pp_method_named (aTHX);
383 /* error case usually */ 475 /* error case usually */
384 PL_op = nextop; nextop = Perl_pp_method_named (aTHX); 476 PL_op = nextop; nextop = Perl_pp_method_named (aTHX);
385 } 477 }
386 } 478 }
387EOF 479EOF
480 }
388 481
389 out_next; 482 out_next;
390} 483}
391 484
392sub op_grepstart { 485sub op_grepstart {
393 out_callop; 486 out_callop;
487 $op = $op->next;
394 out_cond_jump $op->next->other; 488 out_cond_jump $op->other;
395 out_jump_next; 489 out_jump_next;
396} 490}
397 491
398*op_mapstart = \&op_grepstart; 492*op_mapstart = \&op_grepstart;
399 493
405} 499}
406 500
407sub out_break_op { 501sub out_break_op {
408 my ($idx) = @_; 502 my ($idx) = @_;
409 503
504 if ($op->flags & B::OPf_SPECIAL && $insn->{loop}) {
505 # common case: no label, innermost loop only
506 my $next = $insn->{loop}{loop_targ}[$idx];
410 out_callop; 507 out_callop;
411 508 out_jump $next;
412 out_cond_jump $_->[$idx] 509 } elsif (my $loop = $insn->{loop}) {
413 for reverse @loop; 510 # less common case: maybe break to some outer loop
414
415 $source .= " return nextop;\n"; 511 $source .= " return nextop;\n";
512 # todo: walk stack up
513 } else {
514 # fuck yourself for writing such hacks
515 $source .= " return nextop;\n";
516 }
416} 517}
417 518
418sub xop_next { 519sub op_next {
419 out_break_op 0; 520 out_break_op 0;
420} 521}
421 522
422sub op_last { 523sub op_last {
423 out_break_op 1; 524 out_break_op 1;
424} 525}
425 526
527# TODO: does not seem to work
426sub xop_redo { 528#sub op_redo {
427 out_break_op 2; 529# out_break_op 2;
428} 530#}
429 531
430sub cv2c { 532sub cv2c {
431 my ($cv) = @_; 533 my ($cv) = @_;
432 534
433 @loop = (); 535 local @ops;
536 local %op_regcomp;
434 537
435 my %opsseen; 538 my $curloop;
436 my @todo = $cv->START; 539 my @todo = $cv->START;
540 my %op_target;
541 my $numpushmark;
542 my $scope;
437 543
544 my %op_seen;
438 while (my $op = shift @todo) { 545 while (my $op = shift @todo) {
546 my $next;
439 for (; $$op; $op = $op->next) { 547 for (; $$op; $op = $next) {
440 last if $opsseen{$$op}++; 548 last if $op_seen{$$op}++;
441 push @ops, $op; 549
550 $next = $op->next;
442 551
443 my $name = $op->name; 552 my $name = $op->name;
444 my $class = B::class $op; 553 my $class = B::class $op;
445 554
555 my $insn = { op => $op };
556
557 # end of loop reached?
558 $curloop = $curloop->{loop} if $curloop && $$op == ${$curloop->{loop_targ}[1]};
559
560 # remember enclosing loop
561 $insn->{loop} = $curloop if $curloop;
562
563 push @ops, $insn;
564
565 if (exists $extend{$name}) {
566 my $extend = $extend{$name};
567 $extend = $extend->($op) if ref $extend;
568 $insn->{extend} = $extend if defined $extend;
569 }
570
571 # TODO: mark scopes similar to loops, make them comparable
572 # static cxstack(?)
446 if ($class eq "LOGOP") { 573 if ($class eq "LOGOP") {
447 unshift @todo, $op->other; # unshift vs. push saves jumps 574 push @todo, $op->other;
575 $op_target{${$op->other}}++;
576
577 # regcomp/o patches ops at runtime, lets expect that
578 if ($name eq "regcomp" && $op->other->pmflags & B::PMf_KEEP) {
579 $op_target{${$op->first}}++;
580 $op_regcomp{${$op->first}} = $op->next;
581 }
582
448 } elsif ($class eq "PMOP") { 583 } elsif ($class eq "PMOP") {
584 if (${$op->pmreplstart}) {
449 unshift @todo, $op->pmreplstart if ${$op->pmreplstart}; 585 unshift @todo, $op->pmreplstart;
586 $op_target{${$op->pmreplstart}}++;
587 }
588
450 } elsif ($class eq "LOOP") { 589 } elsif ($class eq "LOOP") {
451 push @loop, [$op->nextop, $op->lastop->next, $op->redoop->next]; 590 my @targ = ($op->nextop, $op->lastop->next, $op->redoop);
591
592 unshift @todo, $next, $op->redoop, $op->nextop, $op->lastop;
593 $next = $op->redoop;
594
595 $op_target{$$_}++ for @targ;
596
597 $insn->{loop_targ} = \@targ;
598 $curloop = $insn;
599
600 } elsif ($class eq "COP") {
601 if (defined $op->label) {
602 $insn->{bblock}++;
603 $curloop->{contains_label}{$op->label}++ if $curloop; #TODO: should be within loop
604 }
605
606 } else {
607 if ($name eq "pushmark") {
608 $numpushmark++;
609 }
452 } 610 }
453 } 611 }
454 } 612 }
613
614 $_->{bblock}++ for grep $op_target{${$_->{op}}}, @ops;
455 615
456 local $source = <<EOF; 616 local $source = <<EOF;
457OP *%%%FUNC%%% (pTHX) 617OP *%%%FUNC%%% (pTHX)
458{ 618{
459 register OP *nextop = (OP *)${$ops[0]}L; 619 register OP *nextop = (OP *)${$ops[0]->{op}}L;
460EOF 620EOF
621
622 $source .= " faster_PUSHMARK_PREALLOC ($numpushmark);\n"
623 if $numpushmark;
461 624
462 while (@ops) { 625 while (@ops) {
463 $op = shift @ops; 626 $insn = shift @ops;
627
628 $op = $insn->{op};
464 $op_name = $op->name; 629 $op_name = $op->name;
465 630
631 my $class = B::class $op;
632
633 $source .= "\n/* start basic block */\n" if exists $insn->{bblock};#d#
466 $source .= "op_$$op: /* $op_name */\n"; 634 $source .= "op_$$op: /* $op_name */\n";
467 #$source .= "fprintf (stderr, \"$$op in op $op_name\\n\");\n";#d# 635 #$source .= "fprintf (stderr, \"$$op in op $op_name\\n\");\n";#d#
468 #$source .= "{ dSP; sv_dump (TOPs); }\n";#d# 636 #$source .= "{ dSP; sv_dump (TOPs); }\n";#d#
469 637
470 $source .= " PERL_ASYNC_CHECK ();\n" 638 $source .= " PERL_ASYNC_CHECK ();\n"
471 unless exists $flag{noasync}{$op_name}; 639 unless exists $f_noasync{$op_name};
472 640
473 if (my $can = __PACKAGE__->can ("op_$op_name")) { 641 if (my $can = __PACKAGE__->can ("op_$op_name")) {
474 # handcrafted replacement 642 # handcrafted replacement
643
644 if ($insn->{extend} > 0) {
645 # coalesce EXTENDs
646 # TODO: properly take negative preceeding and following EXTENDs into account
647 for my $i (@ops) {
648 last if exists $i->{bblock};
649 last unless exists $i->{extend};
650 my $extend = delete $i->{extend};
651 $insn->{extend} += $extend if $extend > 0;
652 }
653
654 $source .= " { dSP; EXTEND (SP, $insn->{extend}); PUTBACK; }\n"
655 if $insn->{extend} > 0;
656 }
657
475 $can->($op); 658 $can->($op);
476 659
477 } elsif (exists $flag{unsafe}{$op_name}) { 660 } elsif (exists $f_unsafe{$op_name}) {
478 # unsafe, return to interpreter 661 # unsafe, return to interpreter
479 assert "nextop == (OP *)$$op"; 662 assert "nextop == (OP *)$$op";
480 $source .= " return nextop;\n"; 663 $source .= " return nextop;\n";
481 664
482 } elsif ("LOGOP" eq B::class $op) { 665 } elsif ("LOGOP" eq $class) {
483 # logical operation with optionaö branch 666 # logical operation with optional branch
484 out_callop; 667 out_callop;
485 out_cond_jump $op->other; 668 out_cond_jump $op->other;
486 out_jump_next; 669 out_jump_next;
487 670
488 } elsif ("PMOP" eq B::class $op) { 671 } elsif ("PMOP" eq $class) {
489 # regex-thingy 672 # regex-thingy
490 out_callop; 673 out_callop;
491 out_cond_jump $op->pmreplroot if ${$op->pmreplroot}; 674 out_cond_jump $op->pmreplroot if $op_name ne "pushre" && ${$op->pmreplroot};
492 out_jump_next; 675 out_jump_next;
493 676
494 } else { 677 } else {
495 # normal operator, linear execution 678 # normal operator, linear execution
496 out_linear; 679 out_linear;
507 #warn $source; 690 #warn $source;
508 691
509 $source 692 $source
510} 693}
511 694
695my $uid = "aaaaaaa0";
696my %so;
697
512sub source2ptr { 698sub func2ptr {
513 my ($source) = @_; 699 my (@func) = @_;
514 700
515 my $md5 = Digest::MD5::md5_hex $source; 701 #LOCK
516 $source =~ s/%%%FUNC%%%/Faster_$md5/; 702 mkdir $CACHEDIR, 0777;
703 sysopen my $meta_fh, "$CACHEDIR/meta", &Fcntl::O_RDWR | &Fcntl::O_CREAT, 0666
704 or die "$$CACHEDIR/meta: $!";
705 binmode $meta_fh, ":raw:perlio";
706 fcntl_lock fileno $meta_fh
707 or die "$CACHEDIR/meta: $!";
517 708
518 my $stem = "/tmp/$md5"; 709 my $meta = eval { Storable::fd_retrieve $meta_fh } || { version => 1 };
519 710
520 unless (-e "$stem$_so") { 711 for my $f (@func) {
712 $f->{func} = "F" . Digest::MD5::md5_hex ($f->{source});
713 $f->{so} = $meta->{$f->{func}};
714 }
715
716 if (grep !$_->{so}, @func) {
717 my $stem;
718
719 do {
720 $stem = "$CACHEDIR/$$-" . $uid++;
721 } while -e "$stem$_so";
722
521 open FILE, ">:raw", "$stem.c"; 723 open my $fh, ">:raw", "$stem.c";
522 print FILE <<EOF; 724 print $fh <<EOF;
523#define PERL_NO_GET_CONTEXT 725#define PERL_NO_GET_CONTEXT
726#define PERL_CORE
524 727
525#include <assert.h> 728#include <assert.h>
526 729
527#include "EXTERN.h" 730#include "EXTERN.h"
528#include "perl.h" 731#include "perl.h"
529#include "XSUB.h" 732#include "XSUB.h"
733
734#if 1
735# define faster_PUSHMARK_PREALLOC(count) while (PL_markstack_ptr + (count) >= PL_markstack_max) markstack_grow ()
736# define faster_PUSHMARK(p) *++PL_markstack_ptr = (p) - PL_stack_base
737#else
738# define faster_PUSHMARK_PREALLOC(count) 1
739# define faster_PUSHMARK(p) PUSHMARK(p)
740#endif
530 741
531#define RUNOPS_TILL(op) \\ 742#define RUNOPS_TILL(op) \\
532 while (nextop != (op)) \\ 743 while (nextop != (op)) \\
533 { \\ 744 { \\
534 PERL_ASYNC_CHECK (); \\ 745 PERL_ASYNC_CHECK (); \\
535 PL_op = nextop; nextop = (PL_op->op_ppaddr)(aTHX); \\ 746 PL_op = nextop; nextop = (PL_op->op_ppaddr)(aTHX); \\
536 } 747 }
537 748
538EOF 749EOF
750 for my $f (grep !$_->{so}, @func) {
751 next if $f->{so} = $meta->{$f->{func}}; # some cv's alias others
752
753 warn "compiling $f->{name} to $stem$_so:$f->{func}\n" if $verbose > 1;
754 my $source = $f->{source};
755 $source =~ s/%%%FUNC%%%/$f->{func}/g;
539 print FILE $source; 756 print $fh $source;
757 $meta->{$f->{func}} = $f->{so} = $stem;
758 }
759
540 close FILE; 760 close $fh;
541 system "$COMPILE -o $stem$_o $stem.c"; 761 system "$COMPILE -o $stem$_o $stem.c";
762 unlink "$stem.c" unless $ENV{FASTER_DEBUG} & 1;
542 system "$LINK -o $stem$_so $stem$_o $LIBS"; 763 system "$LINK -o $stem$_so $stem$_o $LIBS";
764 unlink "$stem$_o";
543 } 765 }
544 766
545# warn $source; 767 for my $f (@func) {
768 my $stem = $f->{so};
769
546 my $so = DynaLoader::dl_load_file "$stem$_so" 770 my $so = ($so{$stem} ||= DynaLoader::dl_load_file "$stem$_so")
547 or die "$stem$_so: $!"; 771 or die "$stem$_so: $!";
548 772
549 DynaLoader::dl_find_symbol $so, "Faster_$md5" 773 #unlink "$stem$_so";
550 or die "Faster_$md5: $!" 774
775 $f->{ptr} = DynaLoader::dl_find_symbol $so, $f->{func}
776 or die "$f->{func} not found in $stem$_so: $!";
777 }
778
779 seek $meta_fh, 0, 0 or die "$CACHEDIR/meta: $!";
780 Storable::nstore_fd $meta, $meta_fh;
781 truncate $meta_fh, tell $meta_fh;
782
783 # UNLOCK (by closing $meta_fh)
551} 784}
785
786my %ignore;
552 787
553sub entersub { 788sub entersub {
554 my ($cv) = @_; 789 my ($cv) = @_;
555 790
556 # always compile the whole stash 791 my $pkg = $cv->STASH->NAME;
557# my @stash = $cv->STASH->ARRAY; 792
558# warn join ":", @stash; 793 return if $ignore{$pkg};
559# exit; 794
795 warn "optimising ", $cv->STASH->NAME, "\n"
796 if $verbose;
560 797
561 eval { 798 eval {
799 my @func;
800
801 push @func, {
802 cv => $cv,
803 name => "<>",
562 my $source = cv2c $cv; 804 source => cv2c $cv,
805 };
563 806
564 my $ptr = source2ptr $source; 807 # always compile the whole stash
808 my %stash = $cv->STASH->ARRAY;
809 while (my ($k, $v) = each %stash) {
810 $v->isa (B::GV::)
811 or next;
565 812
813 my $cv = $v->CV;
814
815 if ($cv->isa (B::CV::)
816 && ${$cv->START}
817 && $cv->START->name ne "null") {
818
819 push @func, {
820 cv => $cv,
821 name => $k,
822 source => cv2c $cv,
823 };
824 }
825 }
826
827 func2ptr @func;
828
829 for my $f (@func) {
566 patch_cv $cv, $ptr; 830 patch_cv $f->{cv}, $f->{ptr};
831 }
567 }; 832 };
568 833
569 warn $@ if $@; 834 if ($@) {
835 $ignore{$pkg}++;
836 warn $@;
837 }
570} 838}
571 839
572hook_entersub; 840hook_entersub;
573 841
5741; 8421;
843
844=back
845
846=head1 ENVIRONMENT VARIABLES
847
848The following environment variables influence the behaviour of Faster:
849
850=over 4
851
852=item FASTER_VERBOSE
853
854Faster will output more informational messages when set to values higher
855than C<0>. Currently, C<1> outputs which packages are being compiled, C<3>
856outputs the cache directory and C<10> outputs information on which perl
857function is compiled into which shared object.
858
859=item FASTER_DEBUG
860
861Add debugging code when set to values higher than C<0>. Currently, this
862adds 1-3 C<assert>'s per perl op (FASTER_DEBUG > 1), to ensure that opcode
863order and C execution order are compatible.
864
865=item FASTER_CACHE
866
867Set a persistent cache directory that caches compiled code fragments. The
868default is C<$HOME/.perl-faster-cache> if C<HOME> is set and a temporary
869directory otherwise.
870
871This directory will always grow in size, so you might need to erase it
872from time to time.
575 873
576=back 874=back
577 875
578=head1 BUGS/LIMITATIONS 876=head1 BUGS/LIMITATIONS
579 877
580Perl will check much less often for asynchronous signals in 878Perl will check much less often for asynchronous signals in
581Faster-compiled code. It tries to check on every function call, loop 879Faster-compiled code. It tries to check on every function call, loop
582iteration and every I/O operator, though. 880iteration and every I/O operator, though.
583 881
584The following things will disable Faster. If you manage to enable them at 882The following things will disable Faster. If you manage to enable them at
585runtime, bad things will happen. 883runtime, bad things will happen. Enabling them at startup will be fine,
884though.
586 885
587 enabled tainting 886 enabled tainting
588 enabled debugging 887 enabled debugging
589 888
590This will dramatically reduce Faster's performance: 889Thread-enabled builds of perl will dramatically reduce Faster's
890performance, but you don't care about speed if you enable threads anyway.
591 891
592 threads (but you don't care about speed if you use threads anyway)
593
594These constructs will force the use of the interpreter as soon as they are 892These constructs will force the use of the interpreter for the currently
595being executed, for the rest of the currently executed: 893executed function as soon as they are being encountered during execution.
596 894
597 .., ... (flipflop operators)
598 goto 895 goto
599 next, redo (but not well-behaved last's) 896 next, redo (but not well-behaved last's)
897 labels, if used
600 eval 898 eval
601 require 899 require
602 any use of formats 900 any use of formats
901 .., ... (flipflop operators)
603 902
604=head1 AUTHOR 903=head1 AUTHOR
605 904
606 Marc Lehmann <schmorp@schmorp.de> 905 Marc Lehmann <schmorp@schmorp.de>
607 http://home.schmorp.de/ 906 http://home.schmorp.de/

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines