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.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
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 ();
45use File::Temp ();
22 46
23BEGIN { 47BEGIN {
24 our $VERSION = '0.01'; 48 our $VERSION = '0.01';
25 49
26 require XSLoader; 50 require XSLoader;
31my $LINK = "$Config{ld} $Config{ldflags} $Config{lddlflags} $Config{ccdlflags}"; 55my $LINK = "$Config{ld} $Config{ldflags} $Config{lddlflags} $Config{ccdlflags}";
32my $LIBS = "$Config{libs}"; 56my $LIBS = "$Config{libs}";
33my $_o = $Config{_o}; 57my $_o = $Config{_o};
34my $_so = ".so"; 58my $_so = ".so";
35 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;
66
36our $source; 67our $source;
37our $label_next;
38our $label_last;
39our $label_redo;
40 68
41my @ops; 69our @ops;
42my $op; 70our $op;
43my $op_name; 71our $op_name;
72our @op_loop;
73our %op_regcomp;
44 74
45my %flag; 75my %f_unsafe = map +($_ => undef), qw(
76 leavesub leavesublv return
77 goto last redo next
78 eval flip leaveeval entertry
79 formline grepstart mapstart
80 substcont entereval require
81);
46 82
47for (split /\n/, <<EOF) { 83# pushmark extend=0
48 leavesub unsafe
49 leavesublv unsafe
50 return unsafe
51 flip unsafe
52 goto unsafe
53 last unsafe
54 redo unsafe
55 next unsafe
56 eval unsafe
57 leaveeval unsafe
58 entertry unsafe
59 substconst unsafe
60 formline unsafe
61 grepstart unsafe
62 require unsafe
63 match unsafe noasync todo
64 subst unsafe noasync todo
65 entereval unsafe noasync todo
66 mapstart unsafe noasync todo
67
68 mapwhile noasync
69 grepwhile noasync
70
71 seq noasync
72 pushmark noasync
73 padsv noasync extend=1 84# padsv extend=1
74 padav noasync extend=1 85# padav extend=1
75 padhv noasync extend=1 86# padhv extend=1
76 padany noasync extend=1 87# padany extend=1
77 entersub noasync 88# const extend=1
78 aassign noasync 89
79 sassign noasync 90my %f_noasync = map +($_ => undef), qw(
80 rv2av noasync 91 mapstart grepstart match entereval
81 rv2cv noasync 92 enteriter entersub leaveloop
82 rv2gv noasync 93
83 rv2hv noasync 94 pushmark nextstate
84 refgen noasync 95
85 nextstate noasync 96 const stub unstack
86 gv noasync 97 last next redo seq
87 gvsv noasync 98 padsv padav padhv padany
88 add noasync 99 aassign sassign orassign
89 subtract noasync 100 rv2av rv2cv rv2gv rv2hv refgen
90 multiply noasync 101 gv gvsv
91 divide noasync 102 add subtract multiply divide
92 complement noasync 103 complement cond_expr and or not
93 cond_expr noasync 104 defined
94 and noasync
95 or noasync
96 not noasync
97 defined noasync
98 method_named noasync 105 method_named
99 preinc noasync 106 preinc postinc predec postdec
100 postinc noasync 107 aelem aelemfast helem delete exists
101 predec noasync 108 pushre subst list join split concat
102 postdec noasync 109 length substr stringify ord
103 stub noasync 110 push pop shift unshift
104 unstack noasync 111 eq ne gt lt ge le
105 leaveloop noasync 112 regcomp regcreset regcmaybe
106 aelem noasync 113);
107 aelemfast noasync
108 helem noasync
109 pushre noasync
110 const noasync extend=1
111 list noasync
112 join noasync
113 split noasync
114 concat noasync
115 push noasync
116 pop noasync
117 shift noasync
118 unshift noasync
119 require noasync
120 length noasync
121 substr noasync
122 stringify noasync
123 eq noasync
124 ne noasync
125 gt noasync
126 lt noasync
127 ge noasync
128 le noasync
129 enteriter noasync
130 114
131 iter async 115my %callop = (
132EOF 116 entersub => "(PL_ppaddr [OP_ENTERSUB]) (aTHX)",
133 my (undef, $op, @flags) = split /\s+/; 117 mapstart => "Perl_pp_grepstart (aTHX)",
134 118);
135 undef $flag{$_}{$op}
136 for ("known", @flags);
137}
138 119
139sub callop { 120sub callop {
140 $op_name eq "entersub" 121 $callop{$op_name} || "Perl_pp_$op_name (aTHX)"
141 ? "(PL_ppaddr [OP_ENTERSUB]) (aTHX)" 122}
142 : $op_name eq "mapstart" 123
143 ? "Perl_pp_grepstart (aTHX)" 124sub assert {
144 : "Perl_pp_$op_name (aTHX)" 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";
145} 144}
146 145
147sub out_next { 146sub out_next {
148 if (${$op->next}) {
149 $source .= " nextop = (OP *)${$op->next}L;\n"; 147 $source .= " nextop = (OP *)${$op->next}L;\n";
150 $source .= " assert ((\"$op_name\", nextop == (OP *)${$op->next}));\n"; 148
151 $source .= " goto op_${$op->next};\n"; 149 out_jump_next;
152 } else {
153 $source .= " return 0;\n";
154 }
155} 150}
156 151
157sub out_linear { 152sub out_linear {
158 $source .= " assert ((\"$op_name\", nextop == (OP *)$$op));\n";#d# 153 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; 154 out_jump_next;
171} 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;
172 164
173sub op_nextstate { 165sub op_nextstate {
174 $source .= " PL_curcop = (COP *)nextop;\n"; 166 $source .= " PL_curcop = (COP *)nextop;\n";
175 $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";
176 $source .= " FREETMPS;\n"; 168 $source .= " FREETMPS;\n";
241 233
242 out_next; 234 out_next;
243 }; 235 };
244} 236}
245 237
238# does kill Crossfire/res2pm
246sub op_stringify { 239sub op_stringify {
247 $source .= " { dSP; dTARGET; sv_copypv (TARG, TOPs); SETTARG; }\n"; 240 my $targ = $op->targ;
241
242 $source .= <<EOF;
243 {
244 dSP;
245 SV *targ = PAD_SV ((PADOFFSET)$targ);
246 sv_copypv (TARG, TOPs);
247 SETTARG;
248 PUTBACK;
249 }
250EOF
248 251
249 out_next; 252 out_next;
250} 253}
251 254
252sub op_and { 255sub op_and {
285 out_next; 288 out_next;
286} 289}
287 290
288sub op_padsv { 291sub op_padsv {
289 my $flags = $op->flags; 292 my $flags = $op->flags;
290 my $target = $op->targ; 293 my $targ = $op->targ;
291 294
292 $source .= <<EOF; 295 $source .= <<EOF;
293 { 296 {
294 dSP; 297 dSP;
295 XPUSHs (PAD_SV ((PADOFFSET)$target)); 298 XPUSHs (PAD_SV ((PADOFFSET)$targ));
296 PUTBACK; 299 PUTBACK;
297EOF 300EOF
298 if ($op->flags & B::OPf_MOD) { 301 if ($op->flags & B::OPf_MOD) {
299 if ($op->private & B::OPpLVAL_INTRO) { 302 if ($op->private & B::OPpLVAL_INTRO) {
300 $source .= " SAVECLEARSV (PAD_SVl ((PADOFFSET)$target));\n"; 303 $source .= " SAVECLEARSV (PAD_SVl ((PADOFFSET)$targ));\n";
301 } elsif ($op->private & B::OPpDEREF) { 304 } elsif ($op->private & B::OPpDEREF) {
302 my $deref = $op->private & B::OPpDEREF; 305 my $deref = $op->private & B::OPpDEREF;
303 $source .= " Perl_vivify_ref (PAD_SVl ((PADOFFSET)$target), $deref);\n"; 306 $source .= " Perl_vivify_ref (aTHX_ PAD_SVl ((PADOFFSET)$targ), $deref);\n";
304 } 307 }
305 } 308 }
306 $source .= <<EOF; 309 $source .= <<EOF;
307 } 310 }
308EOF 311EOF
317# pattern const method_named 320# pattern const method_named
318sub op_method_named { 321sub op_method_named {
319 $source .= <<EOF; 322 $source .= <<EOF;
320 { 323 {
321 static HV *last_stash; 324 static HV *last_stash;
322 static SV *last_res; 325 static SV *last_cv;
326 static U32 last_sub_generation;
323 327
324 SV *obj = *(PL_stack_base + TOPMARK + 1); 328 SV *obj = *(PL_stack_base + TOPMARK + 1);
325 329
326 if (SvROK (obj) && SvOBJECT (SvRV (obj))) 330 if (!SvGMAGICAL (obj) && SvROK (obj) && SvOBJECT (SvRV (obj)))
327 { 331 {
328 dSP; 332 dSP;
329 HV *stash = SvSTASH (SvRV (obj)); 333 HV *stash = SvSTASH (SvRV (obj));
330 334
331 /* simple "polymorphic" inline cache */ 335 /* simple "polymorphic" inline cache */
332 if (stash == last_stash) 336 if (stash == last_stash
337 && PL_sub_generation == last_sub_generation)
333 { 338 {
334 XPUSHs (last_res); 339 XPUSHs (last_cv);
335 PUTBACK; 340 PUTBACK;
336 } 341 }
337 else 342 else
338 { 343 {
339 PL_op = nextop;
340 nextop = Perl_pp_method_named (aTHX); 344 PL_op = nextop; nextop = Perl_pp_method_named (aTHX);
341 345
342 SPAGAIN; 346 SPAGAIN;
347 last_sub_generation = PL_sub_generation;
343 last_stash = stash; 348 last_stash = stash;
344 last_res = TOPs; 349 last_cv = TOPs;
345 } 350 }
346 } 351 }
347 else 352 else
348 { 353 {
349 /* error case usually */ 354 /* error case usually */
350 PL_op = nextop;
351 nextop = Perl_pp_method_named (aTHX); 355 PL_op = nextop; nextop = Perl_pp_method_named (aTHX);
352 } 356 }
353 } 357 }
354EOF 358EOF
355 359
356 out_next; 360 out_next;
361}
362
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;
357} 400}
358 401
359sub cv2c { 402sub cv2c {
360 my ($cv) = @_; 403 my ($cv) = @_;
404
405 local @ops;
406 local @op_loop;
407 local %op_regcomp;
361 408
362 my %opsseen; 409 my %opsseen;
363 my @todo = $cv->START; 410 my @todo = $cv->START;
364 411
365 while (my $op = shift @todo) { 412 while (my $op = shift @todo) {
366 for (; $$op; $op = $op->next) { 413 for (; $$op; $op = $op->next) {
367 last if $opsseen{$$op}++; 414 last if $opsseen{$$op}++;
368 push @ops, $op; 415 push @ops, $op;
416
369 my $name = $op->name; 417 my $name = $op->name;
418 my $class = B::class $op;
419
370 if (B::class($op) eq "LOGOP") { 420 if ($class eq "LOGOP") {
371 push @todo, $op->other; 421 unshift @todo, $op->other; # unshift vs. push saves jumps
372 } elsif ($name eq "subst" and ${ $op->pmreplstart }) { 422
373 push @todo, $op->pmreplstart; 423 # regcomp/o patches ops at runtime, lets expect that
374 } elsif ($name =~ /^enter(loop|iter)$/) { 424 $op_regcomp{${$op->first}} = $op->next
375# if ($] > 5.009) { 425 if $name eq "regcomp" && $op->other->pmflags & B::PMf_KEEP;
376# $labels{${$op->nextop}} = "NEXT"; 426
377# $labels{${$op->lastop}} = "LAST"; 427 } elsif ($class eq "PMOP") {
378# $labels{${$op->redoop}} = "REDO"; 428 unshift @todo, $op->pmreplstart if ${$op->pmreplstart};
379# } else { 429
380# $labels{$op->nextop->seq} = "NEXT"; 430 } elsif ($class eq "LOOP") {
381# $labels{$op->lastop->seq} = "LAST"; 431 push @op_loop, [$op->nextop, $op->lastop->next, $op->redoop->next];
382# $labels{$op->redoop->seq} = "REDO"; 432 push @todo, $op->nextop, $op->lastop->next, $op->redoop->next;
383# }
384 } 433 }
385 } 434 }
386 } 435 }
387 436
388 local $source = <<EOF; 437 local $source = <<EOF;
438OP *%%%FUNC%%% (pTHX)
439{
440 register OP *nextop = (OP *)${$ops[0]}L;
441EOF
442
443 while (@ops) {
444 $op = shift @ops;
445 $op_name = $op->name;
446
447 my $class = B::class $op;
448
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#
452
453 $source .= " PERL_ASYNC_CHECK ();\n"
454 unless exists $f_noasync{$op_name};
455
456 if (my $can = __PACKAGE__->can ("op_$op_name")) {
457 # handcrafted replacement
458 $can->($op);
459
460 } elsif (exists $f_unsafe{$op_name}) {
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
477 } else {
478 # normal operator, linear execution
479 out_linear;
480 }
481 }
482
483 $op_name = "func exit"; assert (0);
484
485 $source .= <<EOF;
486op_0:
487 return 0;
488}
489EOF
490 #warn $source;
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";
503 print FILE <<EOF;
389#define PERL_NO_GET_CONTEXT 504#define PERL_NO_GET_CONTEXT
390 505
391//#define NDEBUG 1
392#include <assert.h> 506#include <assert.h>
393 507
394#include "EXTERN.h" 508#include "EXTERN.h"
395#include "perl.h" 509#include "perl.h"
396#include "XSUB.h" 510#include "XSUB.h"
397 511
398OP *%%%FUNC%%% (pTHX) 512#define RUNOPS_TILL(op) \\
399{ 513while (nextop != (op)) \\
400 register OP *nextop = (OP *)${$ops[0]}L; 514 { \\
401EOF 515 PERL_ASYNC_CHECK (); \\
516 PL_op = nextop; nextop = (PL_op->op_ppaddr)(aTHX); \\
517 }
402 518
403 while (@ops) { 519EOF
404 $op = shift @ops; 520 for (@source) {
405 $op_name = $op->name; 521 my $func = $uid++;
406 522 $_ =~ s/%%%FUNC%%%/$func/g;
407 $source .= "op_$$op: /* $op_name */\n"; 523 print FILE $_;
408 #$source .= "fprintf (stderr, \"$$op in op $op_name\\n\");\n";#d# 524 $_ = $func;
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 } 525 }
430 526
431 $source .= "}\n";
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;
448 close FILE; 527 close FILE;
449 system "$COMPILE -o $stem$_o $stem.c"; 528 system "$COMPILE -o $stem$_o $stem.c";
529 #d#unlink "$stem.c";
450 system "$LINK -o $stem$_so $stem$_o $LIBS"; 530 system "$LINK -o $stem$_so $stem$_o $LIBS";
451 } 531 unlink "$stem$_o";
452 532
453# warn $source;
454 my $so = DynaLoader::dl_load_file "$stem$_so" 533 my $so = DynaLoader::dl_load_file "$stem$_so"
455 or die "$stem$_so: $!"; 534 or die "$stem$_so: $!";
456 535
457 DynaLoader::dl_find_symbol $so, "Faster_$md5" 536 #unlink "$stem$_so";
458 or die "Faster_$md5: $!" 537
538 map +(DynaLoader::dl_find_symbol $so, $_), @source
459} 539}
540
541my %ignore;
460 542
461sub entersub { 543sub entersub {
462 my ($cv) = @_; 544 my ($cv) = @_;
463 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
464 eval { 553 eval {
465 my $source = cv2c $cv; 554 my @cv;
555 my @cv_source;
466 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
467 my $ptr = source2ptr $source; 573 my @ptr = source2ptr @cv_source;
468 574
575 for (0 .. $#cv) {
469 patch_cv $cv, $ptr; 576 patch_cv $cv[$_], $ptr[$_];
577 }
470 }; 578 };
471 579
472 warn $@ if $@; 580 if ($@) {
581 $ignore{$pkg}++;
582 warn $@;
583 }
473} 584}
474 585
475hook_entersub; 586hook_entersub;
476 587
4771; 5881;
478 589
479=back 590=back
480 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
481=head1 LIMITATIONS 623=head1 BUGS/LIMITATIONS
482 624
483Tainting 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)
484 648
485=head1 AUTHOR 649=head1 AUTHOR
486 650
487 Marc Lehmann <schmorp@schmorp.de> 651 Marc Lehmann <schmorp@schmorp.de>
488 http://home.schmorp.de/ 652 http://home.schmorp.de/

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines