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

Comparing Faster/Faster.pm (file contents):
Revision 1.10 by root, Fri Mar 10 02:03:50 2006 UTC vs.
Revision 1.18 by root, Fri Mar 10 19:52:07 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
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 ltos 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.
10 33
11=over 4 34=over 4
12 35
13=cut 36=cut
14 37
31my $LINK = "$Config{ld} $Config{ldflags} $Config{lddlflags} $Config{ccdlflags}"; 54my $LINK = "$Config{ld} $Config{ldflags} $Config{lddlflags} $Config{ccdlflags}";
32my $LIBS = "$Config{libs}"; 55my $LIBS = "$Config{libs}";
33my $_o = $Config{_o}; 56my $_o = $Config{_o};
34my $_so = ".so"; 57my $_so = ".so";
35 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 = 1;
64
36our $source; 65our $source;
37our $label_next;
38our $label_last;
39our $label_redo;
40 66
41my @ops; 67our @ops;
42my $op; 68our $op;
43my $op_name; 69our $op_name;
70our @op_loop;
71our %op_regcomp;
44 72
45my %flag; 73my %flag;
46 74
75# complex flag steting is no longer required, rewrite this ugly code
47for (split /\n/, <<EOF) { 76for (split /\n/, <<EOF) {
48 leavesub unsafe 77 leavesub unsafe
49 leavesublv unsafe 78 leavesublv unsafe
50 return unsafe 79 return unsafe
51 flip unsafe 80 flip unsafe
54 redo unsafe 83 redo unsafe
55 next unsafe 84 next unsafe
56 eval unsafe 85 eval unsafe
57 leaveeval unsafe 86 leaveeval unsafe
58 entertry unsafe 87 entertry unsafe
59 substconst unsafe
60 formline unsafe 88 formline unsafe
61 grepstart unsafe 89 grepstart unsafe
90 mapstart unsafe
91 substcont unsafe
92 entereval unsafe noasync todo
62 require unsafe 93 require unsafe
63 match unsafe noasync todo 94
64 subst unsafe noasync todo
65 entereval unsafe noasync todo
66 mapstart unsafe noasync todo 95 mapstart noasync
67 96 grepstart noasync
68 mapwhile noasync 97 match noasync
69 grepwhile noasync
70 98
99 last noasync
100 next noasync
101 redo noasync
71 seq noasync 102 seq noasync
72 pushmark noasync 103 pushmark noasync extend=0
73 padsv noasync extend=1 104 padsv noasync extend=1
74 padav noasync extend=1 105 padav noasync extend=1
75 padhv noasync extend=1 106 padhv noasync extend=1
76 padany noasync extend=1 107 padany noasync extend=1
77 entersub noasync 108 entersub noasync
105 leaveloop noasync 136 leaveloop noasync
106 aelem noasync 137 aelem noasync
107 aelemfast noasync 138 aelemfast noasync
108 helem noasync 139 helem noasync
109 pushre noasync 140 pushre noasync
141 subst noasync
110 const noasync extend=1 142 const noasync extend=1
111 list noasync 143 list noasync
112 join noasync 144 join noasync
113 split noasync 145 split noasync
114 concat noasync 146 concat noasync
115 push noasync 147 push noasync
116 pop noasync 148 pop noasync
117 shift noasync 149 shift noasync
118 unshift noasync 150 unshift noasync
119 require noasync
120 length noasync 151 length noasync
121 substr noasync 152 substr noasync
122 stringify noasync 153 stringify noasync
123 eq noasync 154 eq noasync
124 ne noasync 155 ne noasync
125 gt noasync 156 gt noasync
126 lt noasync 157 lt noasync
127 ge noasync 158 ge noasync
128 le noasync 159 le noasync
129 enteriter noasync 160 enteriter noasync
161 ord noasync
162 orassign noasync
163 regcomp noasync
164 regcreset noasync
165 regcmaybe noasync
130 166
131 iter async 167 iter async
132EOF 168EOF
133 my (undef, $op, @flags) = split /\s+/; 169 my (undef, $op, @flags) = split /\s+/;
134 170
135 undef $flag{$_}{$op} 171 undef $flag{$_}{$op}
136 for ("known", @flags); 172 for ("known", @flags);
137} 173}
138 174
175my %callop = (
176 entersub => "(PL_ppaddr [OP_ENTERSUB]) (aTHX)",
177 mapstart => "Perl_pp_grepstart (aTHX)",
178);
179
139sub callop { 180sub callop {
140 $op_name eq "entersub" 181 $callop{$op_name} || "Perl_pp_$op_name (aTHX)"
141 ? "(PL_ppaddr [OP_ENTERSUB]) (aTHX)"
142 : $op_name eq "mapstart"
143 ? "Perl_pp_grepstart (aTHX)"
144 : "Perl_pp_$op_name (aTHX)"
145} 182}
146 183
184sub assert {
185 return unless $opt_assert;
186 $source .= " assert ((\"$op_name\", ($_[0])));\n";
187}
188
189sub out_callop {
190 assert "nextop == (OP *)$$op";
191 $source .= " PL_op = nextop; nextop = " . (callop $op) . ";\n";
192}
193
194sub out_cond_jump {
195 $source .= " if (nextop == (OP *)${$_[0]}L) goto op_${$_[0]};\n";
196}
197
147sub out_gotonext { 198sub out_jump_next {
148 if (${$op->next}) { 199 out_cond_jump $op_regcomp{$$op}
149 $source .= " assert ((\"$op_name\", nextop == (OP *)${$op->next}));\n"; 200 if $op_regcomp{$$op};
201
202 assert "nextop == (OP *)${$op->next}";
150 $source .= " goto op_${$op->next};\n"; 203 $source .= " goto op_${$op->next};\n";
151 } else {
152 $source .= " return 0;\n";
153 }
154} 204}
155 205
156sub out_next { 206sub out_next {
157 $source .= " nextop = (OP *)${$op->next}L;\n"; 207 $source .= " nextop = (OP *)${$op->next}L;\n";
158 208
159 out_gotonext; 209 out_jump_next;
160} 210}
161 211
162sub out_linear { 212sub out_linear {
163 $source .= " assert ((\"$op_name\", nextop == (OP *)$$op));\n";#d# 213 out_callop;
164 $source .= " PL_op = nextop; nextop = " . (callop $op) . ";\n"; 214 out_jump_next;
165 if ($op_name eq "entersub") {
166 $source .= <<EOF;
167 while (nextop != (OP *)${$op->next}L)
168 {
169 PERL_ASYNC_CHECK ();
170 PL_op = nextop; nextop = (PL_op->op_ppaddr)(aTHX);
171 }
172EOF
173 }
174
175 out_gotonext;
176} 215}
216
217sub op_entersub {
218 out_callop;
219 $source .= " RUNOPS_TILL ((OP *)${$op->next}L);\n";
220 out_jump_next;
221}
222
223*op_require = \&op_entersub;
177 224
178sub op_nextstate { 225sub op_nextstate {
179 $source .= " PL_curcop = (COP *)nextop;\n"; 226 $source .= " PL_curcop = (COP *)nextop;\n";
180 $source .= " PL_stack_sp = PL_stack_base + cxstack[cxstack_ix].blk_oldsp;\n"; 227 $source .= " PL_stack_sp = PL_stack_base + cxstack[cxstack_ix].blk_oldsp;\n";
181 $source .= " FREETMPS;\n"; 228 $source .= " FREETMPS;\n";
246 293
247 out_next; 294 out_next;
248 }; 295 };
249} 296}
250 297
298# does kill Crossfire/res2pm
251sub op_stringify { 299sub op_stringify {
252 $source .= " { dSP; dTARGET; sv_copypv (TARG, TOPs); SETTARG; }\n"; 300 my $targ = $op->targ;
301
302 $source .= <<EOF;
303 {
304 dSP;
305 SV *targ = PAD_SV ((PADOFFSET)$targ);
306 sv_copypv (TARG, TOPs);
307 SETTARG;
308 PUTBACK;
309 }
310EOF
253 311
254 out_next; 312 out_next;
255} 313}
256 314
257sub op_and { 315sub op_and {
322# pattern const method_named 380# pattern const method_named
323sub op_method_named { 381sub op_method_named {
324 $source .= <<EOF; 382 $source .= <<EOF;
325 { 383 {
326 static HV *last_stash; 384 static HV *last_stash;
327 static SV *last_res; 385 static SV *last_cv;
386 static U32 last_sub_generation;
328 387
329 SV *obj = *(PL_stack_base + TOPMARK + 1); 388 SV *obj = *(PL_stack_base + TOPMARK + 1);
330 389
331 printf ("todo: PL_subgeneration or somesuch\\n");
332 if (SvROK (obj) && SvOBJECT (SvRV (obj))) 390 if (!SvGMAGICAL (obj) && SvROK (obj) && SvOBJECT (SvRV (obj)))
333 { 391 {
334 dSP; 392 dSP;
335 HV *stash = SvSTASH (SvRV (obj)); 393 HV *stash = SvSTASH (SvRV (obj));
336 394
337 /* simple "polymorphic" inline cache */ 395 /* simple "polymorphic" inline cache */
338 if (stash == last_stash) 396 if (stash == last_stash
397 && PL_sub_generation == last_sub_generation)
339 { 398 {
340 XPUSHs (last_res); 399 XPUSHs (last_cv);
341 PUTBACK; 400 PUTBACK;
342 } 401 }
343 else 402 else
344 { 403 {
345 PL_op = nextop;
346 nextop = Perl_pp_method_named (aTHX); 404 PL_op = nextop; nextop = Perl_pp_method_named (aTHX);
347 405
348 SPAGAIN; 406 SPAGAIN;
407 last_sub_generation = PL_sub_generation;
349 last_stash = stash; 408 last_stash = stash;
350 last_res = TOPs; 409 last_cv = TOPs;
351 } 410 }
352 } 411 }
353 else 412 else
354 { 413 {
355 /* error case usually */ 414 /* error case usually */
356 PL_op = nextop;
357 nextop = Perl_pp_method_named (aTHX); 415 PL_op = nextop; nextop = Perl_pp_method_named (aTHX);
358 } 416 }
359 } 417 }
360EOF 418EOF
361 419
362 out_next; 420 out_next;
421}
422
423sub op_grepstart {
424 out_callop;
425 $op = $op->next;
426 out_cond_jump $op->other;
427 out_jump_next;
428}
429
430*op_mapstart = \&op_grepstart;
431
432sub op_substcont {
433 out_callop;
434 out_cond_jump $op->other->pmreplstart;
435 assert "nextop == (OP *)${$op->other->next}L";
436 $source .= " goto op_${$op->other->next};\n";
437}
438
439sub out_break_op {
440 my ($idx) = @_;
441
442 out_callop;
443
444 out_cond_jump $_->[$idx]
445 for reverse @op_loop;
446
447 $source .= " return nextop;\n";
448}
449
450sub xop_next {
451 out_break_op 0;
452}
453
454sub op_last {
455 out_break_op 1;
456}
457
458sub xop_redo {
459 out_break_op 2;
363} 460}
364 461
365sub cv2c { 462sub cv2c {
366 my ($cv) = @_; 463 my ($cv) = @_;
464
465 local @ops;
466 local @op_loop;
467 local %op_regcomp;
367 468
368 my %opsseen; 469 my %opsseen;
369 my @todo = $cv->START; 470 my @todo = $cv->START;
370 471
371 while (my $op = shift @todo) { 472 while (my $op = shift @todo) {
372 for (; $$op; $op = $op->next) { 473 for (; $$op; $op = $op->next) {
373 last if $opsseen{$$op}++; 474 last if $opsseen{$$op}++;
374 push @ops, $op; 475 push @ops, $op;
476
375 my $name = $op->name; 477 my $name = $op->name;
478 my $class = B::class $op;
479
376 if (B::class($op) eq "LOGOP") { 480 if ($class eq "LOGOP") {
377 push @todo, $op->other; 481 unshift @todo, $op->other; # unshift vs. push saves jumps
378 } elsif ($name eq "subst" and ${ $op->pmreplstart }) { 482
379 push @todo, $op->pmreplstart; 483 # regcomp/o patches ops at runtime, lets expect that
380 } elsif ($name =~ /^enter(loop|iter)$/) { 484 $op_regcomp{${$op->first}} = $op->next
381# if ($] > 5.009) { 485 if $name eq "regcomp" && $op->other->pmflags & B::PMf_KEEP;
382# $labels{${$op->nextop}} = "NEXT"; 486
383# $labels{${$op->lastop}} = "LAST"; 487 } elsif ($class eq "PMOP") {
384# $labels{${$op->redoop}} = "REDO"; 488 unshift @todo, $op->pmreplstart if ${$op->pmreplstart};
385# } else { 489
386# $labels{$op->nextop->seq} = "NEXT"; 490 } elsif ($class eq "LOOP") {
387# $labels{$op->lastop->seq} = "LAST"; 491 push @op_loop, [$op->nextop, $op->lastop->next, $op->redoop->next];
388# $labels{$op->redoop->seq} = "REDO"; 492 push @todo, $op->nextop, $op->lastop->next, $op->redoop->next;
389# }
390 } 493 }
391 } 494 }
392 } 495 }
393 496
394 local $source = <<EOF; 497 local $source = <<EOF;
498OP *%%%FUNC%%% (pTHX)
499{
500 register OP *nextop = (OP *)${$ops[0]}L;
501EOF
502
503 while (@ops) {
504 $op = shift @ops;
505 $op_name = $op->name;
506
507 $source .= "op_$$op: /* $op_name */\n";
508 #$source .= "fprintf (stderr, \"$$op in op $op_name\\n\");\n";#d#
509 #$source .= "{ dSP; sv_dump (TOPs); }\n";#d#
510
511 $source .= " PERL_ASYNC_CHECK ();\n"
512 unless exists $flag{noasync}{$op_name};
513
514 if (my $can = __PACKAGE__->can ("op_$op_name")) {
515 # handcrafted replacement
516 $can->($op);
517
518 } elsif (exists $flag{unsafe}{$op_name}) {
519 # unsafe, return to interpreter
520 assert "nextop == (OP *)$$op";
521 $source .= " return nextop;\n";
522
523 } elsif ("LOGOP" eq B::class $op) {
524 # logical operation with optionaö branch
525 out_callop;
526 out_cond_jump $op->other;
527 out_jump_next;
528
529 } elsif ("PMOP" eq B::class $op) {
530 # regex-thingy
531 out_callop;
532 out_cond_jump $op->pmreplroot if ${$op->pmreplroot};
533 out_jump_next;
534
535 } else {
536 # normal operator, linear execution
537 out_linear;
538 }
539 }
540
541 $op_name = "func exit"; assert (0);
542
543 $source .= <<EOF;
544op_0:
545 return 0;
546}
547EOF
548 #warn $source;
549
550 $source
551}
552
553sub source2ptr {
554 my ($source) = @_;
555
556 my $md5 = Digest::MD5::md5_hex $source;
557 $source =~ s/%%%FUNC%%%/Faster_$md5/;
558
559 my $stem = "/tmp/$md5";
560
561 unless (-e "$stem$_so") {
562 open FILE, ">:raw", "$stem.c";
563 print FILE <<EOF;
395#define PERL_NO_GET_CONTEXT 564#define PERL_NO_GET_CONTEXT
396 565
397//#define NDEBUG 1
398#include <assert.h> 566#include <assert.h>
399 567
400#include "EXTERN.h" 568#include "EXTERN.h"
401#include "perl.h" 569#include "perl.h"
402#include "XSUB.h" 570#include "XSUB.h"
403 571
404OP *%%%FUNC%%% (pTHX) 572#define RUNOPS_TILL(op) \\
405{ 573 while (nextop != (op)) \\
406 register OP *nextop = (OP *)${$ops[0]}L; 574 { \\
407EOF 575 PERL_ASYNC_CHECK (); \\
408 576 PL_op = nextop; nextop = (PL_op->op_ppaddr)(aTHX); \\
409 while (@ops) {
410 $op = shift @ops;
411 $op_name = $op->name;
412
413 $source .= "op_$$op: /* $op_name */\n";
414 #$source .= "fprintf (stderr, \"$$op in op $op_name\\n\");\n";#d#
415 #$source .= "{ dSP; sv_dump (TOPs); }\n";#d#
416
417 unless (exists $flag{noasync}{$op_name}) {
418 $source .= " PERL_ASYNC_CHECK ();\n";
419 }
420
421 if (my $can = __PACKAGE__->can ("op_$op_name")) {
422 $can->($op);
423 } elsif (exists $flag{unsafe}{$op_name}) {
424 $source .= " assert ((\"$op_name\", nextop == (OP *)$$op));\n";
425 $source .= " return nextop;\n";
426 } elsif ("LOGOP" eq B::class $op or exists $flag{otherop}{$op_name}) {
427 $source .= " assert ((\"$op_name\", nextop == (OP *)$$op));\n";
428 $source .= " PL_op = nextop; nextop = " . (callop $op) . ";\n";
429 $source .= " if (nextop == (OP *)${$op->other}L) goto op_${$op->other};\n";
430 $source .= " assert ((\"$op_name\", nextop == (OP *)${$op->next}));\n";
431 $source .= ${$op->next} ? " goto op_${$op->next};\n" : " return 0;\n";
432 } else {
433 out_linear;
434 }
435 } 577 }
436 578
437 $source .= "}\n"; 579EOF
438 #warn $source;
439
440 $source
441}
442
443sub source2ptr {
444 my ($source) = @_;
445
446 my $md5 = Digest::MD5::md5_hex $source;
447 $source =~ s/%%%FUNC%%%/Faster_$md5/;
448
449 my $stem = "/tmp/$md5";
450
451 unless (-e "$stem$_so") {
452 open FILE, ">:raw", "$stem.c";
453 print FILE $source; 580 print FILE $source;
454 close FILE; 581 close FILE;
455 system "$COMPILE -o $stem$_o $stem.c"; 582 system "$COMPILE -o $stem$_o $stem.c";
456 system "$LINK -o $stem$_so $stem$_o $LIBS"; 583 system "$LINK -o $stem$_so $stem$_o $LIBS";
457 } 584 }
465} 592}
466 593
467sub entersub { 594sub entersub {
468 my ($cv) = @_; 595 my ($cv) = @_;
469 596
597 # always compile the whole stash
598# my @stash = $cv->STASH->ARRAY;
599# warn join ":", @stash;
600# exit;
601
470 eval { 602 eval {
471 my $source = cv2c $cv; 603 my $source = cv2c $cv;
472 604
473 my $ptr = source2ptr $source; 605 my $ptr = source2ptr $source;
474 606
475 patch_cv $cv, $ptr; 607 patch_cv $cv, $ptr;
476 }; 608 };
477 609
478 warn $@ if $@; 610 warn $@ if $@;
479} 611}
480 612
482 614
4831; 6151;
484 616
485=back 617=back
486 618
487=head1 LIMITATIONS 619=head1 BUGS/LIMITATIONS
488 620
489Tainting and debugging will disable Faster. 621Perl will check much less often for asynchronous signals in
622Faster-compiled code. It tries to check on every function call, loop
623iteration and every I/O operator, though.
624
625The following things will disable Faster. If you manage to enable them at
626runtime, bad things will happen.
627
628 enabled tainting
629 enabled debugging
630
631This will dramatically reduce Faster's performance:
632
633 threads (but you don't care about speed if you use threads anyway)
634
635These constructs will force the use of the interpreter as soon as they are
636being executed, for the rest of the currently executed:
637
638 .., ... (flipflop operators)
639 goto
640 next, redo (but not well-behaved last's)
641 eval
642 require
643 any use of formats
490 644
491=head1 AUTHOR 645=head1 AUTHOR
492 646
493 Marc Lehmann <schmorp@schmorp.de> 647 Marc Lehmann <schmorp@schmorp.de>
494 http://home.schmorp.de/ 648 http://home.schmorp.de/

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines