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

Comparing Faster/Faster.pm (file contents):
Revision 1.14 by root, Fri Mar 10 18:58:20 2006 UTC vs.
Revision 1.20 by root, Fri Mar 10 22:32:15 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 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.
10 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;
18use Config; 41use Config;
19use B (); 42use B ();
20use Digest::MD5 (); 43#use Digest::MD5 ();
21use DynaLoader (); 44use DynaLoader ();
22 45
23BEGIN { 46BEGIN {
24 our $VERSION = '0.01'; 47 our $VERSION = '0.01';
25 48
35 58
36# we don't need no steenking PIC on x86 59# we don't need no steenking PIC on x86
37$COMPILE =~ s/-f(?:PIC|pic)//g 60$COMPILE =~ s/-f(?:PIC|pic)//g
38 if $Config{archname} =~ /^(i[3456]86)-/; 61 if $Config{archname} =~ /^(i[3456]86)-/;
39 62
40my $opt_assert = 1; 63my $opt_assert = 0;
41 64
42our $source; 65our $source;
43 66
44my @ops; 67our @ops;
45my $op; 68our $op;
46my $op_name; 69our $op_name;
47my @loop; 70our @op_loop;
71our %op_regcomp;
48 72
49my %flag; 73my %f_unsafe = map +($_ => undef), qw(
74 leavesub leavesublv return
75 goto last redo next
76 eval flip leaveeval entertry
77 formline grepstart mapstart
78 substcont entereval require
79);
50 80
51# complex flag steting is no longer required, rewrite this ugly code
52for (split /\n/, <<EOF) {
53 leavesub unsafe
54 leavesublv unsafe
55 return unsafe
56 flip unsafe
57 goto unsafe
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
71 mapstart noasync
72 grepstart noasync
73 match noasync
74
75 last noasync
76 next noasync
77 redo noasync
78 seq noasync
79 pushmark noasync extend=0 81# pushmark extend=0
80 padsv noasync extend=1 82# padsv extend=1
81 padav noasync extend=1 83# padav extend=1
82 padhv noasync extend=1 84# padhv extend=1
83 padany noasync extend=1 85# padany extend=1
84 entersub noasync 86# const extend=1
85 aassign noasync 87
86 sassign noasync 88my %f_noasync = map +($_ => undef), qw(
87 rv2av noasync 89 mapstart grepstart match entereval
88 rv2cv noasync 90 enteriter entersub leaveloop
89 rv2gv noasync 91
90 rv2hv noasync 92 pushmark nextstate
91 refgen noasync 93
92 nextstate noasync 94 const stub unstack
93 gv noasync 95 last next redo seq
94 gvsv noasync 96 padsv padav padhv padany
95 add noasync 97 aassign sassign orassign
96 subtract noasync 98 rv2av rv2cv rv2gv rv2hv refgen
97 multiply noasync 99 gv gvsv
98 divide noasync 100 add subtract multiply divide
99 complement noasync 101 complement cond_expr and or not
100 cond_expr noasync 102 defined
101 and noasync
102 or noasync
103 not noasync
104 defined noasync
105 method_named noasync 103 method_named
106 preinc noasync 104 preinc postinc predec postdec
107 postinc noasync 105 aelem aelemfast helem delete exists
108 predec noasync 106 pushre subst list join split concat
109 postdec noasync 107 length substr stringify ord
110 stub noasync 108 push pop shift unshift
111 unstack noasync 109 eq ne gt lt ge le
112 leaveloop noasync 110 regcomp regcreset regcmaybe
113 aelem noasync 111);
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
139 iter async
140EOF
141 my (undef, $op, @flags) = split /\s+/;
142
143 undef $flag{$_}{$op}
144 for ("known", @flags);
145}
146 112
147my %callop = ( 113my %callop = (
148 entersub => "(PL_ppaddr [OP_ENTERSUB]) (aTHX)", 114 entersub => "(PL_ppaddr [OP_ENTERSUB]) (aTHX)",
149 mapstart => "Perl_pp_grepstart (aTHX)", 115 mapstart => "Perl_pp_grepstart (aTHX)",
150); 116);
161sub out_callop { 127sub out_callop {
162 assert "nextop == (OP *)$$op"; 128 assert "nextop == (OP *)$$op";
163 $source .= " PL_op = nextop; nextop = " . (callop $op) . ";\n"; 129 $source .= " PL_op = nextop; nextop = " . (callop $op) . ";\n";
164} 130}
165 131
132sub out_cond_jump {
133 $source .= " if (nextop == (OP *)${$_[0]}L) goto op_${$_[0]};\n";
134}
135
166sub out_jump_next { 136sub out_jump_next {
137 out_cond_jump $op_regcomp{$$op}
138 if $op_regcomp{$$op};
139
167 assert "nextop == (OP *)${$op->next}"; 140 assert "nextop == (OP *)${$op->next}";
168 $source .= " goto op_${$op->next};\n"; 141 $source .= " goto op_${$op->next};\n";
169} 142}
170 143
171sub out_next { 144sub out_next {
175} 148}
176 149
177sub out_linear { 150sub out_linear {
178 out_callop; 151 out_callop;
179 out_jump_next; 152 out_jump_next;
180}
181
182sub out_cond_jump {
183 $source .= " if (nextop == (OP *)${$_[0]}L) goto op_${$_[0]};\n";
184} 153}
185 154
186sub op_entersub { 155sub op_entersub {
187 out_callop; 156 out_callop;
188 $source .= " RUNOPS_TILL ((OP *)${$op->next}L);\n"; 157 $source .= " RUNOPS_TILL ((OP *)${$op->next}L);\n";
409 my ($idx) = @_; 378 my ($idx) = @_;
410 379
411 out_callop; 380 out_callop;
412 381
413 out_cond_jump $_->[$idx] 382 out_cond_jump $_->[$idx]
414 for reverse @loop; 383 for reverse @op_loop;
415 384
416 $source .= " return nextop;\n"; 385 $source .= " return nextop;\n";
417} 386}
418 387
419sub xop_next { 388sub xop_next {
429} 398}
430 399
431sub cv2c { 400sub cv2c {
432 my ($cv) = @_; 401 my ($cv) = @_;
433 402
434 @loop = (); 403 local @ops;
404 local @op_loop;
405 local %op_regcomp;
435 406
436 my %opsseen; 407 my %opsseen;
437 my @todo = $cv->START; 408 my @todo = $cv->START;
438 409
439 while (my $op = shift @todo) { 410 while (my $op = shift @todo) {
444 my $name = $op->name; 415 my $name = $op->name;
445 my $class = B::class $op; 416 my $class = B::class $op;
446 417
447 if ($class eq "LOGOP") { 418 if ($class eq "LOGOP") {
448 unshift @todo, $op->other; # unshift vs. push saves jumps 419 unshift @todo, $op->other; # unshift vs. push saves jumps
420
421 # regcomp/o patches ops at runtime, lets expect that
422 $op_regcomp{${$op->first}} = $op->next
423 if $name eq "regcomp" && $op->other->pmflags & B::PMf_KEEP;
424
449 } elsif ($class eq "PMOP") { 425 } elsif ($class eq "PMOP") {
450 unshift @todo, $op->pmreplstart if ${$op->pmreplstart}; 426 unshift @todo, $op->pmreplstart if ${$op->pmreplstart};
427
451 } elsif ($class eq "LOOP") { 428 } elsif ($class eq "LOOP") {
452 push @loop, [$op->nextop, $op->lastop->next, $op->redoop->next]; 429 push @op_loop, [$op->nextop, $op->lastop->next, $op->redoop->next];
430 push @todo, $op->nextop, $op->lastop->next, $op->redoop->next;
453 } 431 }
454 } 432 }
455 } 433 }
456 434
457 local $source = <<EOF; 435 local $source = <<EOF;
467 $source .= "op_$$op: /* $op_name */\n"; 445 $source .= "op_$$op: /* $op_name */\n";
468 #$source .= "fprintf (stderr, \"$$op in op $op_name\\n\");\n";#d# 446 #$source .= "fprintf (stderr, \"$$op in op $op_name\\n\");\n";#d#
469 #$source .= "{ dSP; sv_dump (TOPs); }\n";#d# 447 #$source .= "{ dSP; sv_dump (TOPs); }\n";#d#
470 448
471 $source .= " PERL_ASYNC_CHECK ();\n" 449 $source .= " PERL_ASYNC_CHECK ();\n"
472 unless exists $flag{noasync}{$op_name}; 450 unless exists $f_noasync{$op_name};
473 451
474 if (my $can = __PACKAGE__->can ("op_$op_name")) { 452 if (my $can = __PACKAGE__->can ("op_$op_name")) {
475 # handcrafted replacement 453 # handcrafted replacement
476 $can->($op); 454 $can->($op);
477 455
478 } elsif (exists $flag{unsafe}{$op_name}) { 456 } elsif (exists $f_unsafe{$op_name}) {
479 # unsafe, return to interpreter 457 # unsafe, return to interpreter
480 assert "nextop == (OP *)$$op"; 458 assert "nextop == (OP *)$$op";
481 $source .= " return nextop;\n"; 459 $source .= " return nextop;\n";
482 460
483 } elsif ("LOGOP" eq B::class $op) { 461 } elsif ("LOGOP" eq B::class $op) {
508 #warn $source; 486 #warn $source;
509 487
510 $source 488 $source
511} 489}
512 490
491my $uid = "aaaaaaa0";
492
513sub source2ptr { 493sub source2ptr {
514 my ($source) = @_; 494 my (@source) = @_;
515 495
516 my $md5 = Digest::MD5::md5_hex $source; 496 my $stem = "/tmp/Faster-$$-" . $uid++;
517 $source =~ s/%%%FUNC%%%/Faster_$md5/;
518 497
519 my $stem = "/tmp/$md5";
520
521 unless (-e "$stem$_so") {
522 open FILE, ">:raw", "$stem.c"; 498 open FILE, ">:raw", "$stem.c";
523 print FILE <<EOF; 499 print FILE <<EOF;
524#define PERL_NO_GET_CONTEXT 500#define PERL_NO_GET_CONTEXT
525 501
526#include <assert.h> 502#include <assert.h>
527 503
528#include "EXTERN.h" 504#include "EXTERN.h"
529#include "perl.h" 505#include "perl.h"
530#include "XSUB.h" 506#include "XSUB.h"
531 507
532#define RUNOPS_TILL(op) \\ 508#define RUNOPS_TILL(op) \\
533 while (nextop != (op)) \\ 509while (nextop != (op)) \\
534 { \\ 510 { \\
535 PERL_ASYNC_CHECK (); \\ 511 PERL_ASYNC_CHECK (); \\
536 PL_op = nextop; nextop = (PL_op->op_ppaddr)(aTHX); \\ 512 PL_op = nextop; nextop = (PL_op->op_ppaddr)(aTHX); \\
537 } 513 }
538 514
539EOF 515EOF
516 for (@source) {
517 my $func = $uid++;
518 $_ =~ s/%%%FUNC%%%/$func/g;
540 print FILE $source; 519 print FILE $_;
541 close FILE; 520 $_ = $func;
542 system "$COMPILE -o $stem$_o $stem.c";
543 system "$LINK -o $stem$_so $stem$_o $LIBS";
544 } 521 }
545 522
546# warn $source; 523 close FILE;
524 system "$COMPILE -o $stem$_o $stem.c";
525 #d#unlink "$stem.c";
526 system "$LINK -o $stem$_so $stem$_o $LIBS";
527 unlink "$stem$_o";
528
547 my $so = DynaLoader::dl_load_file "$stem$_so" 529 my $so = DynaLoader::dl_load_file "$stem$_so"
548 or die "$stem$_so: $!"; 530 or die "$stem$_so: $!";
549 531
550 DynaLoader::dl_find_symbol $so, "Faster_$md5" 532 #unlink "$stem$_so";
551 or die "Faster_$md5: $!" 533
534 map +(DynaLoader::dl_find_symbol $so, $_), @source
552} 535}
536
537my %ignore;
553 538
554sub entersub { 539sub entersub {
555 my ($cv) = @_; 540 my ($cv) = @_;
556 541
557 # always compile the whole stash 542 my $pkg = $cv->STASH->NAME;
558# my @stash = $cv->STASH->ARRAY; 543
559# warn join ":", @stash; 544 return if $ignore{$pkg};
560# exit; 545
546 warn "compiling ", $cv->STASH->NAME;#d#
561 547
562 eval { 548 eval {
563 my $source = cv2c $cv; 549 my @cv;
550 my @cv_source;
564 551
552 # always compile the whole stash
553 my %stash = $cv->STASH->ARRAY;
554 while (my ($k, $v) = each %stash) {
555 $v->isa (B::GV::)
556 or next;
557
558 my $cv = $v->CV;
559
560 if ($cv->isa (B::CV::)
561 && ${$cv->START}
562 && $cv->START->name ne "null") {
563 push @cv, $cv;
564 push @cv_source, cv2c $cv;
565 }
566 }
567
565 my $ptr = source2ptr $source; 568 my @ptr = source2ptr @cv_source;
566 569
570 for (0 .. $#cv) {
567 patch_cv $cv, $ptr; 571 patch_cv $cv[$_], $ptr[$_];
572 }
568 }; 573 };
569 574
570 warn $@ if $@; 575 if ($@) {
576 $ignore{$pkg}++;
577 warn $@;
578 }
571} 579}
572 580
573hook_entersub; 581hook_entersub;
574 582
5751; 5831;
581Perl will check much less often for asynchronous signals in 589Perl will check much less often for asynchronous signals in
582Faster-compiled code. It tries to check on every function call, loop 590Faster-compiled code. It tries to check on every function call, loop
583iteration and every I/O operator, though. 591iteration and every I/O operator, though.
584 592
585The following things will disable Faster. If you manage to enable them at 593The following things will disable Faster. If you manage to enable them at
586runtime, bad things will happen. 594runtime, bad things will happen. Enabling them at startup will be fine,
595though.
587 596
588 enabled tainting 597 enabled tainting
589 enabled debugging 598 enabled debugging
590 599
591This will dramatically reduce Faster's performance: 600Thread-enabled builds of perl will dramatically reduce Faster's
601performance, but you don't care about speed if you enable threads anyway.
592 602
593 threads (but you don't care about speed if you use threads anyway)
594
595These constructs will force the use of the interpreter as soon as they are 603These constructs will force the use of the interpreter for the currently
596being executed, for the rest of the currently executed: 604executed function as soon as they are being encountered during execution.
597 605
598 .., ... (flipflop operators)
599 goto 606 goto
600 next, redo (but not well-behaved last's) 607 next, redo (but not well-behaved last's)
601 eval 608 eval
602 require 609 require
603 any use of formats 610 any use of formats
611 .., ... (flipflop operators)
604 612
605=head1 AUTHOR 613=head1 AUTHOR
606 614
607 Marc Lehmann <schmorp@schmorp.de> 615 Marc Lehmann <schmorp@schmorp.de>
608 http://home.schmorp.de/ 616 http://home.schmorp.de/

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines