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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines