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

Comparing Faster/Faster.pm (file contents):
Revision 1.8 by root, Fri Mar 10 01:51:14 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)" 182}
142 : $op_name eq "mapstart" 183
143 ? "Perl_pp_grepstart (aTHX)" 184sub assert {
144 : "Perl_pp_$op_name (aTHX)" 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
198sub out_jump_next {
199 out_cond_jump $op_regcomp{$$op}
200 if $op_regcomp{$$op};
201
202 assert "nextop == (OP *)${$op->next}";
203 $source .= " goto op_${$op->next};\n";
145} 204}
146 205
147sub out_next { 206sub out_next {
148 if (${$op->next}) {
149 $source .= " nextop = (OP *)${$op->next}L;\n"; 207 $source .= " nextop = (OP *)${$op->next}L;\n";
150 $source .= " assert ((\"$op_name\", nextop == (OP *)${$op->next}));\n"; 208
151 $source .= " goto op_${$op->next};\n"; 209 out_jump_next;
152 } else {
153 $source .= " return 0;\n";
154 }
155} 210}
156 211
157sub out_linear { 212sub out_linear {
158 $source .= " assert ((\"$op_name\", nextop == (OP *)$$op));\n";#d# 213 out_callop;
159 $source .= " PL_op = nextop; nextop = " . (callop $op) . ";\n";
160 if ($op_name eq "entersub") {
161 $source .= <<EOF;
162 while (nextop != (OP *)${$op->next}L)
163 {
164 PERL_ASYNC_CHECK ();
165 PL_op = nextop; nextop = (PL_op->op_ppaddr)(aTHX);
166 }
167EOF
168 }
169
170 out_next; 214 out_jump_next;
171} 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;
172 224
173sub op_nextstate { 225sub op_nextstate {
174 $source .= " PL_curcop = (COP *)nextop;\n"; 226 $source .= " PL_curcop = (COP *)nextop;\n";
175 $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";
176 $source .= " FREETMPS;\n"; 228 $source .= " FREETMPS;\n";
241 293
242 out_next; 294 out_next;
243 }; 295 };
244} 296}
245 297
298# does kill Crossfire/res2pm
246sub op_stringify { 299sub op_stringify {
247 $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
248 311
249 out_next; 312 out_next;
250} 313}
251 314
252sub op_and { 315sub op_and {
317# pattern const method_named 380# pattern const method_named
318sub op_method_named { 381sub op_method_named {
319 $source .= <<EOF; 382 $source .= <<EOF;
320 { 383 {
321 static HV *last_stash; 384 static HV *last_stash;
322 static SV *last_res; 385 static SV *last_cv;
386 static U32 last_sub_generation;
323 387
324 SV *obj = *(PL_stack_base + TOPMARK + 1); 388 SV *obj = *(PL_stack_base + TOPMARK + 1);
325 389
326 if (SvROK (obj) && SvOBJECT (SvRV (obj))) 390 if (!SvGMAGICAL (obj) && SvROK (obj) && SvOBJECT (SvRV (obj)))
327 { 391 {
328 dSP; 392 dSP;
329 HV *stash = SvSTASH (SvRV (obj)); 393 HV *stash = SvSTASH (SvRV (obj));
330 394
331 /* simple "polymorphic" inline cache */ 395 /* simple "polymorphic" inline cache */
332 if (stash == last_stash) 396 if (stash == last_stash
397 && PL_sub_generation == last_sub_generation)
333 { 398 {
334 XPUSHs (last_res); 399 XPUSHs (last_cv);
335 PUTBACK; 400 PUTBACK;
336 } 401 }
337 else 402 else
338 { 403 {
339 PL_op = nextop;
340 nextop = Perl_pp_method_named (aTHX); 404 PL_op = nextop; nextop = Perl_pp_method_named (aTHX);
341 405
342 SPAGAIN; 406 SPAGAIN;
407 last_sub_generation = PL_sub_generation;
343 last_stash = stash; 408 last_stash = stash;
344 last_res = TOPs; 409 last_cv = TOPs;
345 } 410 }
346 } 411 }
347 else 412 else
348 { 413 {
349 /* error case usually */ 414 /* error case usually */
350 PL_op = nextop;
351 nextop = Perl_pp_method_named (aTHX); 415 PL_op = nextop; nextop = Perl_pp_method_named (aTHX);
352 } 416 }
353 } 417 }
354EOF 418EOF
355 419
356 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;
357} 460}
358 461
359sub cv2c { 462sub cv2c {
360 my ($cv) = @_; 463 my ($cv) = @_;
464
465 local @ops;
466 local @op_loop;
467 local %op_regcomp;
361 468
362 my %opsseen; 469 my %opsseen;
363 my @todo = $cv->START; 470 my @todo = $cv->START;
364 471
365 while (my $op = shift @todo) { 472 while (my $op = shift @todo) {
366 for (; $$op; $op = $op->next) { 473 for (; $$op; $op = $op->next) {
367 last if $opsseen{$$op}++; 474 last if $opsseen{$$op}++;
368 push @ops, $op; 475 push @ops, $op;
476
369 my $name = $op->name; 477 my $name = $op->name;
478 my $class = B::class $op;
479
370 if (B::class($op) eq "LOGOP") { 480 if ($class eq "LOGOP") {
371 push @todo, $op->other; 481 unshift @todo, $op->other; # unshift vs. push saves jumps
372 } elsif ($name eq "subst" and ${ $op->pmreplstart }) { 482
373 push @todo, $op->pmreplstart; 483 # regcomp/o patches ops at runtime, lets expect that
374 } elsif ($name =~ /^enter(loop|iter)$/) { 484 $op_regcomp{${$op->first}} = $op->next
375# if ($] > 5.009) { 485 if $name eq "regcomp" && $op->other->pmflags & B::PMf_KEEP;
376# $labels{${$op->nextop}} = "NEXT"; 486
377# $labels{${$op->lastop}} = "LAST"; 487 } elsif ($class eq "PMOP") {
378# $labels{${$op->redoop}} = "REDO"; 488 unshift @todo, $op->pmreplstart if ${$op->pmreplstart};
379# } else { 489
380# $labels{$op->nextop->seq} = "NEXT"; 490 } elsif ($class eq "LOOP") {
381# $labels{$op->lastop->seq} = "LAST"; 491 push @op_loop, [$op->nextop, $op->lastop->next, $op->redoop->next];
382# $labels{$op->redoop->seq} = "REDO"; 492 push @todo, $op->nextop, $op->lastop->next, $op->redoop->next;
383# }
384 } 493 }
385 } 494 }
386 } 495 }
387 496
388 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;
389#define PERL_NO_GET_CONTEXT 564#define PERL_NO_GET_CONTEXT
390 565
391//#define NDEBUG 1
392#include <assert.h> 566#include <assert.h>
393 567
394#include "EXTERN.h" 568#include "EXTERN.h"
395#include "perl.h" 569#include "perl.h"
396#include "XSUB.h" 570#include "XSUB.h"
397 571
398OP *%%%FUNC%%% (pTHX) 572#define RUNOPS_TILL(op) \\
399{ 573 while (nextop != (op)) \\
400 register OP *nextop = (OP *)${$ops[0]}L; 574 { \\
401EOF 575 PERL_ASYNC_CHECK (); \\
402 576 PL_op = nextop; nextop = (PL_op->op_ppaddr)(aTHX); \\
403 while (@ops) {
404 $op = shift @ops;
405 $op_name = $op->name;
406
407 $source .= "op_$$op: /* $op_name */\n";
408 #$source .= "fprintf (stderr, \"$$op in op $op_name\\n\");\n";#d#
409 #$source .= "{ dSP; sv_dump (TOPs); }\n";#d#
410
411 unless (exists $flag{noasync}{$op_name}) {
412 $source .= " PERL_ASYNC_CHECK ();\n";
413 }
414
415 if (my $can = __PACKAGE__->can ("op_$op_name")) {
416 $can->($op);
417 } elsif (exists $flag{unsafe}{$op_name}) {
418 $source .= " assert ((\"$op_name\", nextop == (OP *)$$op));\n";
419 $source .= " PL_op = nextop; return " . (callop $op) . ";\n";
420 } elsif ("LOGOP" eq B::class $op or exists $flag{otherop}{$op_name}) {
421 $source .= " assert ((\"$op_name\", nextop == (OP *)$$op));\n";
422 $source .= " PL_op = nextop; nextop = " . (callop $op) . ";\n";
423 $source .= " if (nextop == (OP *)${$op->other}L) goto op_${$op->other};\n";
424 $source .= " assert ((\"$op_name\", nextop == (OP *)${$op->next}));\n";
425 $source .= ${$op->next} ? " goto op_${$op->next};\n" : " return 0;\n";
426 } else {
427 out_linear;
428 }
429 } 577 }
430 578
431 $source .= "}\n"; 579EOF
432 #warn $source;
433
434 $source
435}
436
437sub source2ptr {
438 my ($source) = @_;
439
440 my $md5 = Digest::MD5::md5_hex $source;
441 $source =~ s/%%%FUNC%%%/Faster_$md5/;
442
443 my $stem = "/tmp/$md5";
444
445 unless (-e "$stem$_so") {
446 open FILE, ">:raw", "$stem.c";
447 print FILE $source; 580 print FILE $source;
448 close FILE; 581 close FILE;
449 system "$COMPILE -o $stem$_o $stem.c"; 582 system "$COMPILE -o $stem$_o $stem.c";
450 system "$LINK -o $stem$_so $stem$_o $LIBS"; 583 system "$LINK -o $stem$_so $stem$_o $LIBS";
451 } 584 }
459} 592}
460 593
461sub entersub { 594sub entersub {
462 my ($cv) = @_; 595 my ($cv) = @_;
463 596
597 # always compile the whole stash
598# my @stash = $cv->STASH->ARRAY;
599# warn join ":", @stash;
600# exit;
601
464 eval { 602 eval {
465 my $source = cv2c $cv; 603 my $source = cv2c $cv;
466 604
467 my $ptr = source2ptr $source; 605 my $ptr = source2ptr $source;
468 606
469 patch_cv $cv, $ptr; 607 patch_cv $cv, $ptr;
470 }; 608 };
471 609
472 warn $@ if $@; 610 warn $@ if $@;
473} 611}
474 612
476 614
4771; 6151;
478 616
479=back 617=back
480 618
481=head1 LIMITATIONS 619=head1 BUGS/LIMITATIONS
482 620
483Tainting 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
484 644
485=head1 AUTHOR 645=head1 AUTHOR
486 646
487 Marc Lehmann <schmorp@schmorp.de> 647 Marc Lehmann <schmorp@schmorp.de>
488 http://home.schmorp.de/ 648 http://home.schmorp.de/

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines