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

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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines