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

Comparing Faster/Faster.pm (file contents):
Revision 1.3 by root, Thu Mar 9 06:35:33 2006 UTC vs.
Revision 1.21 by root, Fri Mar 10 22:39:11 2006 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. It works by more or less
14translating every function it sees into a C program, compiling it and then
15replacing the function by the compiled code.
16
17As a result, startup times are immense, as every function might lead to a
18full-blown compilation.
19
20The speed improvements are also not great, you can expect 20% or so on
21average, for code that runs very often.
22
23Faster is in the early stages of development. Due to its design its
24relatively safe to use (it will either work or simply slowdown the program
25immensely, but rarely cause bugs).
26
27Usage is very easy, just C<use Faster> and every function called from then
28on will be compiled.
29
30Right now, Faster will leave lots of F<*.c>, F<*.o> and F<*.so> files in
31F</tmp>, and it will even create those temporary files in an insecure
32manner, so watch out.
33
11=over 4 34=over 4
12 35
13=cut 36=cut
14 37
15package Faster; 38package Faster;
16 39
17use strict; 40use strict;
41use Config;
42use B ();
43#use Digest::MD5 ();
44use DynaLoader ();
18 45
19BEGIN { 46BEGIN {
20 our $VERSION = '0.01'; 47 our $VERSION = '0.01';
21 48
22 require XSLoader; 49 require XSLoader;
23 XSLoader::load __PACKAGE__, $VERSION; 50 XSLoader::load __PACKAGE__, $VERSION;
24} 51}
25 52
26use B (); 53my $COMPILE = "$Config{cc} -c -I$Config{archlibexp}/CORE $Config{optimize} $Config{ccflags} $Config{cccdlflags}";
54my $LINK = "$Config{ld} $Config{ldflags} $Config{lddlflags} $Config{ccdlflags}";
55my $LIBS = "$Config{libs}";
56my $_o = $Config{_o};
57my $_so = ".so";
58
59# we don't need no steenking PIC on x86
60$COMPILE =~ s/-f(?:PIC|pic)//g
61 if $Config{archname} =~ /^(i[3456]86)-/;
62
63my $opt_assert = $ENV{FASTER_DEBUG};
64my $verbose = $ENV{FASTER_VERBOSE}+0;
27 65
28our $source; 66our $source;
29our $label_next;
30our $label_last;
31our $label_redo;
32 67
33my %flag; 68our @ops;
69our $op;
70our $op_name;
71our @op_loop;
72our %op_regcomp;
34 73
35for (split /\n/, <<EOF) { 74my %f_unsafe = map +($_ => undef), qw(
36 leavesub unsafe 75 leavesub leavesublv return
37 leavesublv unsafe 76 goto last redo next
38 return unsafe 77 eval flip leaveeval entertry
39 flip unsafe 78 formline grepstart mapstart
40 goto unsafe 79 substcont entereval require
41 last unsafe 80);
42 redo unsafe
43 next unsafe
44 eval unsafe
45 leaveeval unsafe
46 entertry unsafe
47 substconst unsafe
48 formline unsafe
49 grepstart unsafe
50EOF
51 my (undef, $op, @flags) = split /\s+/;
52 81
53 undef $flag{$_}{$op} 82# pushmark extend=0
54 for ("known", @flags); 83# padsv extend=1
84# padav extend=1
85# padhv extend=1
86# padany extend=1
87# const extend=1
88
89my %f_noasync = map +($_ => undef), qw(
90 mapstart grepstart match entereval
91 enteriter entersub leaveloop
92
93 pushmark nextstate
94
95 const stub unstack
96 last next redo seq
97 padsv padav padhv padany
98 aassign sassign orassign
99 rv2av rv2cv rv2gv rv2hv refgen
100 gv gvsv
101 add subtract multiply divide
102 complement cond_expr and or not
103 defined
104 method_named
105 preinc postinc predec postdec
106 aelem aelemfast helem delete exists
107 pushre subst list join split concat
108 length substr stringify ord
109 push pop shift unshift
110 eq ne gt lt ge le
111 regcomp regcreset regcmaybe
112);
113
114my %callop = (
115 entersub => "(PL_ppaddr [OP_ENTERSUB]) (aTHX)",
116 mapstart => "Perl_pp_grepstart (aTHX)",
117);
118
119sub callop {
120 $callop{$op_name} || "Perl_pp_$op_name (aTHX)"
121}
122
123sub assert {
124 return unless $opt_assert;
125 $source .= " assert ((\"$op_name\", ($_[0])));\n";
126}
127
128sub out_callop {
129 assert "nextop == (OP *)$$op";
130 $source .= " PL_op = nextop; nextop = " . (callop $op) . ";\n";
131}
132
133sub out_cond_jump {
134 $source .= " if (nextop == (OP *)${$_[0]}L) goto op_${$_[0]};\n";
135}
136
137sub out_jump_next {
138 out_cond_jump $op_regcomp{$$op}
139 if $op_regcomp{$$op};
140
141 assert "nextop == (OP *)${$op->next}";
142 $source .= " goto op_${$op->next};\n";
55} 143}
56 144
57sub out_next { 145sub out_next {
58 my ($op) = @_;
59
60 $source .= " PL_op = (OP *)${$op->next}L;\n"; 146 $source .= " nextop = (OP *)${$op->next}L;\n";
61 $source .= " goto op_${$op->next};\n"; 147
148 out_jump_next;
62} 149}
150
151sub out_linear {
152 out_callop;
153 out_jump_next;
154}
155
156sub op_entersub {
157 out_callop;
158 $source .= " RUNOPS_TILL ((OP *)${$op->next}L);\n";
159 out_jump_next;
160}
161
162*op_require = \&op_entersub;
63 163
64sub op_nextstate { 164sub op_nextstate {
65 my ($op) = @_;
66
67 $source .= " PL_curcop = (COP *)PL_op;\n"; 165 $source .= " PL_curcop = (COP *)nextop;\n";
68 $source .= " PL_stack_sp = PL_stack_base + cxstack[cxstack_ix].blk_oldsp;\n"; 166 $source .= " PL_stack_sp = PL_stack_base + cxstack[cxstack_ix].blk_oldsp;\n";
69 $source .= " FREETMPS;\n"; 167 $source .= " FREETMPS;\n";
70 168
71 out_next $op; 169 out_next;
72} 170}
73 171
74sub op_pushmark { 172sub op_pushmark {
75 my ($op) = @_;
76
77 $source .= " PUSHMARK (PL_stack_sp);\n"; 173 $source .= " PUSHMARK (PL_stack_sp);\n";
78 174
79 out_next $op; 175 out_next;
80} 176}
81 177
82sub op_const { 178if ($Config{useithreads} ne "define") {
83 my ($op) = @_; 179 # disable optimisations on ithreads
84 180
181 *op_const = sub {
85 $source .= " { dSP; XPUSHs ((SV *)${$op->sv}L); PUTBACK; }\n"; 182 $source .= " { dSP; XPUSHs ((SV *)${$op->sv}L); PUTBACK; }\n";
86 183
87 out_next $op; 184 out_next;
88} 185 };
89 186
90*op_gv = \&op_const; 187 *op_gv = \&op_const;
91 188
189 *op_aelemfast = sub {
190 my $targ = $op->targ;
191 my $private = $op->private;
192
193 $source .= " {\n";
194
195 if ($op->flags & B::OPf_SPECIAL) {
196 $source .= " AV *av = (AV*)PAD_SV((PADOFFSET)$targ);\n";
197 } else {
198 $source .= " AV *av = GvAV ((GV *)${$op->sv}L);\n";
199 }
200
201 if ($op->flags & B::OPf_MOD) {
202 $source .= " SV *sv = *av_fetch (av, $private, 1);\n";
203 } else {
204 $source .= " SV **svp = av_fetch (av, $private, 0); SV *sv = svp ? *svp : &PL_sv_undef;\n";
205 }
206
207 if (!($op->flags & B::OPf_MOD)) {
208 $source .= " if (SvGMAGICAL (sv)) sv = sv_mortalcopy (sv);\n";
209 }
210
211 $source .= " dSP;\n";
212 $source .= " XPUSHs (sv);\n";
213 $source .= " PUTBACK;\n";
214 $source .= " }\n";
215
216 out_next;
217 };
218
219 *op_gvsv = sub {
220 $source .= " {\n";
221 $source .= " dSP;\n";
222 $source .= " EXTEND (SP, 1);\n";
223
224 if ($op->private & B::OPpLVAL_INTRO) {
225 $source .= " PUSHs (save_scalar ((GV *)${$op->sv}L));\n";
226 } else {
227 $source .= " PUSHs (GvSV ((GV *)${$op->sv}L));\n";
228 }
229
230 $source .= " PUTBACK;\n";
231 $source .= " }\n";
232
233 out_next;
234 };
235}
236
237# does kill Crossfire/res2pm
92sub op_stringify { 238sub op_stringify {
93 my ($op) = @_; 239 my $targ = $op->targ;
94 240
95 $source .= " { dSP; dTARGET; sv_copypv (TARG, TOPs); SETTARG; }\n"; 241 $source .= <<EOF;
242 {
243 dSP;
244 SV *targ = PAD_SV ((PADOFFSET)$targ);
245 sv_copypv (TARG, TOPs);
246 SETTARG;
247 PUTBACK;
248 }
249EOF
96 250
97 out_next $op; 251 out_next;
252}
253
254sub op_and {
255 $source .= <<EOF;
256 {
257 dSP;
258
259 if (SvTRUE (TOPs))
260 {
261 --SP;
262 PUTBACK;
263 nextop = (OP *)${$op->other}L;
264 goto op_${$op->other};
265 }
266 }
267EOF
268
269 out_next;
270}
271
272sub op_or {
273 $source .= <<EOF;
274 {
275 dSP;
276
277 if (!SvTRUE (TOPs))
278 {
279 --SP;
280 PUTBACK;
281 nextop = (OP *)${$op->other}L;
282 goto op_${$op->other};
283 }
284 }
285EOF
286
287 out_next;
288}
289
290sub op_padsv {
291 my $flags = $op->flags;
292 my $targ = $op->targ;
293
294 $source .= <<EOF;
295 {
296 dSP;
297 XPUSHs (PAD_SV ((PADOFFSET)$targ));
298 PUTBACK;
299EOF
300 if ($op->flags & B::OPf_MOD) {
301 if ($op->private & B::OPpLVAL_INTRO) {
302 $source .= " SAVECLEARSV (PAD_SVl ((PADOFFSET)$targ));\n";
303 } elsif ($op->private & B::OPpDEREF) {
304 my $deref = $op->private & B::OPpDEREF;
305 $source .= " Perl_vivify_ref (PAD_SVl ((PADOFFSET)$targ), $deref);\n";
306 }
307 }
308 $source .= <<EOF;
309 }
310EOF
311
312 out_next;
98} 313}
99 314
100# pattern const+ (or general push1) 315# pattern const+ (or general push1)
101# pattern pushmark return(?) 316# pattern pushmark return(?)
102# pattern pushmark gv rv2av pushmark padsv+o.ä. aassign 317# pattern pushmark gv rv2av pushmark padsv+o.ä. aassign
103 318
104# pattern const method_named 319# pattern const method_named
105sub xop_method_named { 320sub op_method_named {
106 my ($op) = @_;
107
108 my $ppaddr = ppaddr $op->type;
109
110 $source .= <<EOF; 321 $source .= <<EOF;
111 { 322 {
112 dSP; 323 static HV *last_stash;
324 static SV *last_cv;
325 static U32 last_sub_generation;
113 326
114 if (SvROK (TOPm1s) && SvOBJECT (SvRV (TOPm1s))) 327 SV *obj = *(PL_stack_base + TOPMARK + 1);
328
329 if (!SvGMAGICAL (obj) && SvROK (obj) && SvOBJECT (SvRV (obj)))
115 { 330 {
116 static SV *last_stash, SV *last_res; 331 dSP;
117 SV *stash = SvSTASH (SvRV (TOPm1s)); 332 HV *stash = SvSTASH (SvRV (obj));
118 333
119 // simple polymorphic inline cache 334 /* simple "polymorphic" inline cache */
120 if (stash == last_stash) 335 if (stash == last_stash
336 && PL_sub_generation == last_sub_generation)
121 { 337 {
338 XPUSHs (last_cv);
122 dTARGET; 339 PUTBACK;
123 SETTARG (last_res);
124 } 340 }
125 else 341 else
126 { 342 {
127 PUTBACK; 343 PL_op = nextop; nextop = Perl_pp_method_named (aTHX);
128 ((PPFUNC)${ppaddr}L)(aTHX);\n"; 344
129 SPAGAIN; 345 SPAGAIN;
130 346 last_sub_generation = PL_sub_generation;
131 last_stash = stash; 347 last_stash = stash;
132 last_res = TOPs; 348 last_cv = TOPs;
133 } 349 }
134 } 350 }
351 else
352 {
353 /* error case usually */
354 PL_op = nextop; nextop = Perl_pp_method_named (aTHX);
355 }
135 } 356 }
136EOF 357EOF
137 358
138 out_next $op; 359 out_next;
139} 360}
140 361
141sub entersub { 362sub op_grepstart {
363 out_callop;
364 $op = $op->next;
365 out_cond_jump $op->other;
366 out_jump_next;
367}
368
369*op_mapstart = \&op_grepstart;
370
371sub op_substcont {
372 out_callop;
373 out_cond_jump $op->other->pmreplstart;
374 assert "nextop == (OP *)${$op->other->next}L";
375 $source .= " goto op_${$op->other->next};\n";
376}
377
378sub out_break_op {
379 my ($idx) = @_;
380
381 out_callop;
382
383 out_cond_jump $_->[$idx]
384 for reverse @op_loop;
385
386 $source .= " return nextop;\n";
387}
388
389sub xop_next {
390 out_break_op 0;
391}
392
393sub op_last {
394 out_break_op 1;
395}
396
397sub xop_redo {
398 out_break_op 2;
399}
400
401sub cv2c {
142 my ($cv) = @_; 402 my ($cv) = @_;
143 403
404 local @ops;
405 local @op_loop;
406 local %op_regcomp;
407
144 my %opsseen; 408 my %opsseen;
145 my @ops;
146 my @todo = $cv->START; 409 my @todo = $cv->START;
147 410
148 while (my $op = shift @todo) { 411 while (my $op = shift @todo) {
149 for (; $$op; $op = $op->next) { 412 for (; $$op; $op = $op->next) {
150 last if $opsseen{$$op}++; 413 last if $opsseen{$$op}++;
151 push @ops, $op; 414 push @ops, $op;
415
152 my $name = $op->name; 416 my $name = $op->name;
417 my $class = B::class $op;
418
153 if (B::class($op) eq "LOGOP") { 419 if ($class eq "LOGOP") {
154 push @todo, $op->other; 420 unshift @todo, $op->other; # unshift vs. push saves jumps
155 } elsif ($name eq "subst" and ${ $op->pmreplstart }) { 421
156 push @todo, $op->pmreplstart; 422 # regcomp/o patches ops at runtime, lets expect that
157 } elsif ($name =~ /^enter(loop|iter)$/) { 423 $op_regcomp{${$op->first}} = $op->next
158# if ($] > 5.009) { 424 if $name eq "regcomp" && $op->other->pmflags & B::PMf_KEEP;
159# $labels{${$op->nextop}} = "NEXT"; 425
160# $labels{${$op->lastop}} = "LAST"; 426 } elsif ($class eq "PMOP") {
161# $labels{${$op->redoop}} = "REDO"; 427 unshift @todo, $op->pmreplstart if ${$op->pmreplstart};
162# } else { 428
163# $labels{$op->nextop->seq} = "NEXT"; 429 } elsif ($class eq "LOOP") {
164# $labels{$op->lastop->seq} = "LAST"; 430 push @op_loop, [$op->nextop, $op->lastop->next, $op->redoop->next];
165# $labels{$op->redoop->seq} = "REDO"; 431 push @todo, $op->nextop, $op->lastop->next, $op->redoop->next;
166# }
167 } 432 }
168 } 433 }
169 } 434 }
170 435
171 local $source; 436 local $source = <<EOF;
437OP *%%%FUNC%%% (pTHX)
438{
439 register OP *nextop = (OP *)${$ops[0]}L;
440EOF
172 441
173 $source = "typedef OP *(*PPFUNC)(pTHX);\n\n"; 442 while (@ops) {
174 443 $op = shift @ops;
175 $source .= "OP *func (pTHX)\n{\n dTHX;\n";
176
177 for my $op (@ops) {
178 my $name = $op->name; 444 $op_name = $op->name;
179 my $ppaddr = ppaddr $op->type;
180 445
181 $source .= "op_$$op: /* $name */\n"; 446 $source .= "op_$$op: /* $op_name */\n";
447 #$source .= "fprintf (stderr, \"$$op in op $op_name\\n\");\n";#d#
448 #$source .= "{ dSP; sv_dump (TOPs); }\n";#d#
182 449
450 $source .= " PERL_ASYNC_CHECK ();\n"
451 unless exists $f_noasync{$op_name};
452
183 if (my $can = __PACKAGE__->can ("op_$name")) { 453 if (my $can = __PACKAGE__->can ("op_$op_name")) {
454 # handcrafted replacement
184 $can->($op); 455 $can->($op);
185 } elsif ("LOGOP" eq B::class $op or exists $flag{otherop}{$name}) { 456
186 $source .= " PL_op = ((PPFUNC)${ppaddr}L)(aTHX);\n";
187 $source .= " if (PL_op == (OP *)${$op->other}L) goto op_${$op->other};\n";
188 $source .= " goto op_${$op->next};\n";
189 } elsif (exists $flag{unsafe}{$name}) { 457 } elsif (exists $f_unsafe{$op_name}) {
190 $source .= " return ((PPFUNC)${ppaddr}L)(aTHX);\n"; 458 # unsafe, return to interpreter
459 assert "nextop == (OP *)$$op";
460 $source .= " return nextop;\n";
461
462 } elsif ("LOGOP" eq B::class $op) {
463 # logical operation with optionaö branch
464 out_callop;
465 out_cond_jump $op->other;
466 out_jump_next;
467
468 } elsif ("PMOP" eq B::class $op) {
469 # regex-thingy
470 out_callop;
471 out_cond_jump $op->pmreplroot if ${$op->pmreplroot};
472 out_jump_next;
473
191 } else { 474 } else {
192 $source .= " PL_op = ((PPFUNC)${ppaddr}L)(aTHX);\n"; 475 # normal operator, linear execution
193 $source .= " goto op_${$op->next};\n"; 476 out_linear;
194 } 477 }
195 } 478 }
196 479
480 $op_name = "func exit"; assert (0);
481
197 $source .= "}\n"; 482 $source .= <<EOF;
483op_0:
484 return 0;
485}
486EOF
487 #warn $source;
198 488
489 $source
490}
491
492my $uid = "aaaaaaa0";
493
494sub source2ptr {
495 my (@source) = @_;
496
497 my $stem = "/tmp/Faster-$$-" . $uid++;
498
499 open FILE, ">:raw", "$stem.c";
199 print <<EOF; 500 print FILE <<EOF;
501#define PERL_NO_GET_CONTEXT
502
503#include <assert.h>
504
200#include "EXTERN.h" 505#include "EXTERN.h"
201#include "perl.h" 506#include "perl.h"
202#include "XSUB.h" 507#include "XSUB.h"
508
509#define RUNOPS_TILL(op) \\
510while (nextop != (op)) \\
511 { \\
512 PERL_ASYNC_CHECK (); \\
513 PL_op = nextop; nextop = (PL_op->op_ppaddr)(aTHX); \\
514 }
515
203EOF 516EOF
204 print $source; 517 for (@source) {
518 my $func = $uid++;
519 $_ =~ s/%%%FUNC%%%/$func/g;
520 print FILE $_;
521 $_ = $func;
522 }
523
524 close FILE;
525 system "$COMPILE -o $stem$_o $stem.c";
526 #d#unlink "$stem.c";
527 system "$LINK -o $stem$_so $stem$_o $LIBS";
528 unlink "$stem$_o";
529
530 my $so = DynaLoader::dl_load_file "$stem$_so"
531 or die "$stem$_so: $!";
532
533 #unlink "$stem$_so";
534
535 map +(DynaLoader::dl_find_symbol $so, $_), @source
536}
537
538my %ignore;
539
540sub entersub {
541 my ($cv) = @_;
542
543 my $pkg = $cv->STASH->NAME;
544
545 return if $ignore{$pkg};
546
547 warn "compiling ", $cv->STASH->NAME, "\n"
548 if $verbose;
549
550 eval {
551 my @cv;
552 my @cv_source;
553
554 # always compile the whole stash
555 my %stash = $cv->STASH->ARRAY;
556 while (my ($k, $v) = each %stash) {
557 $v->isa (B::GV::)
558 or next;
559
560 my $cv = $v->CV;
561
562 if ($cv->isa (B::CV::)
563 && ${$cv->START}
564 && $cv->START->name ne "null") {
565 push @cv, $cv;
566 push @cv_source, cv2c $cv;
567 }
568 }
569
570 my @ptr = source2ptr @cv_source;
571
572 for (0 .. $#cv) {
573 patch_cv $cv[$_], $ptr[$_];
574 }
575 };
576
577 if ($@) {
578 $ignore{$pkg}++;
579 warn $@;
580 }
205} 581}
206 582
207hook_entersub; 583hook_entersub;
208 584
2091; 5851;
210 586
211=back 587=back
212 588
589=head1 ENVIRONMENT VARIABLES
590
591The following environment variables influence the behaviour of Faster:
592
593=over 4
594
595=item FASTER_VERBOSE
596
597Faster will output more informational messages when set to values higher
598than C<0>. Currently, C<1> outputs which packages are being compiled.
599
600=item FASTER_DEBUG
601
602Add debugging code when set to values higher than C<0>. Currently, this
603adds 1-3 C<assert>'s per perl op, to ensure that opcode order and C
604execution order are compatible.
605
606=item FASTER_CACHE
607
608NOT YET IMPLEMENTED
609
610Set a persistent cache directory that caches compiled code
611fragments. Normally, code compiled by Faster will be deleted immediately,
612and every restart will recompile everything. Setting this variable to a
613directory makes Faster cache the generated files for re-use.
614
615This directory will always grow in contents, so you might need to erase it
616from time to time.
617
618=back
619
213=head1 LIMITATIONS 620=head1 BUGS/LIMITATIONS
214 621
215Tainting and debugging will disable Faster. 622Perl will check much less often for asynchronous signals in
623Faster-compiled code. It tries to check on every function call, loop
624iteration and every I/O operator, though.
625
626The following things will disable Faster. If you manage to enable them at
627runtime, bad things will happen. Enabling them at startup will be fine,
628though.
629
630 enabled tainting
631 enabled debugging
632
633Thread-enabled builds of perl will dramatically reduce Faster's
634performance, but you don't care about speed if you enable threads anyway.
635
636These constructs will force the use of the interpreter for the currently
637executed function as soon as they are being encountered during execution.
638
639 goto
640 next, redo (but not well-behaved last's)
641 eval
642 require
643 any use of formats
644 .., ... (flipflop operators)
216 645
217=head1 AUTHOR 646=head1 AUTHOR
218 647
219 Marc Lehmann <schmorp@schmorp.de> 648 Marc Lehmann <schmorp@schmorp.de>
220 http://home.schmorp.de/ 649 http://home.schmorp.de/

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines