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

Comparing Faster/Faster.pm (file contents):
Revision 1.9 by root, Fri Mar 10 01:55:12 2006 UTC vs.
Revision 1.22 by root, Fri Mar 10 22:41:47 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)"
142 : $op_name eq "mapstart"
143 ? "Perl_pp_grepstart (aTHX)"
144 : "Perl_pp_$op_name (aTHX)"
145} 122}
146 123
124sub assert {
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
147sub out_gotonext { 138sub out_jump_next {
148 if (${$op->next}) { 139 out_cond_jump $op_regcomp{$$op}
149 $source .= " assert ((\"$op_name\", nextop == (OP *)${$op->next}));\n"; 140 if $op_regcomp{$$op};
141
142 assert "nextop == (OP *)${$op->next}";
150 $source .= " goto op_${$op->next};\n"; 143 $source .= " goto op_${$op->next};\n";
151 } else {
152 $source .= " return 0;\n";
153 }
154} 144}
155 145
156sub out_next { 146sub out_next {
157 $source .= " nextop = (OP *)${$op->next}L;\n"; 147 $source .= " nextop = (OP *)${$op->next}L;\n";
158 148
159 out_gotonext; 149 out_jump_next;
160} 150}
161 151
162sub out_linear { 152sub out_linear {
163 $source .= " assert ((\"$op_name\", nextop == (OP *)$$op));\n";#d# 153 out_callop;
164 $source .= " PL_op = nextop; nextop = " . (callop $op) . ";\n"; 154 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} 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;
177 164
178sub op_nextstate { 165sub op_nextstate {
179 $source .= " PL_curcop = (COP *)nextop;\n"; 166 $source .= " PL_curcop = (COP *)nextop;\n";
180 $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";
181 $source .= " FREETMPS;\n"; 168 $source .= " FREETMPS;\n";
246 233
247 out_next; 234 out_next;
248 }; 235 };
249} 236}
250 237
238# does kill Crossfire/res2pm
251sub op_stringify { 239sub op_stringify {
252 $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
253 251
254 out_next; 252 out_next;
255} 253}
256 254
257sub op_and { 255sub op_and {
290 out_next; 288 out_next;
291} 289}
292 290
293sub op_padsv { 291sub op_padsv {
294 my $flags = $op->flags; 292 my $flags = $op->flags;
295 my $target = $op->targ; 293 my $targ = $op->targ;
296 294
297 $source .= <<EOF; 295 $source .= <<EOF;
298 { 296 {
299 dSP; 297 dSP;
300 XPUSHs (PAD_SV ((PADOFFSET)$target)); 298 XPUSHs (PAD_SV ((PADOFFSET)$targ));
301 PUTBACK; 299 PUTBACK;
302EOF 300EOF
303 if ($op->flags & B::OPf_MOD) { 301 if ($op->flags & B::OPf_MOD) {
304 if ($op->private & B::OPpLVAL_INTRO) { 302 if ($op->private & B::OPpLVAL_INTRO) {
305 $source .= " SAVECLEARSV (PAD_SVl ((PADOFFSET)$target));\n"; 303 $source .= " SAVECLEARSV (PAD_SVl ((PADOFFSET)$targ));\n";
306 } elsif ($op->private & B::OPpDEREF) { 304 } elsif ($op->private & B::OPpDEREF) {
307 my $deref = $op->private & B::OPpDEREF; 305 my $deref = $op->private & B::OPpDEREF;
308 $source .= " Perl_vivify_ref (PAD_SVl ((PADOFFSET)$target), $deref);\n"; 306 $source .= " Perl_vivify_ref (aTHX_ PAD_SVl ((PADOFFSET)$targ), $deref);\n";
309 } 307 }
310 } 308 }
311 $source .= <<EOF; 309 $source .= <<EOF;
312 } 310 }
313EOF 311EOF
322# pattern const method_named 320# pattern const method_named
323sub op_method_named { 321sub op_method_named {
324 $source .= <<EOF; 322 $source .= <<EOF;
325 { 323 {
326 static HV *last_stash; 324 static HV *last_stash;
327 static SV *last_res; 325 static SV *last_cv;
326 static U32 last_sub_generation;
328 327
329 SV *obj = *(PL_stack_base + TOPMARK + 1); 328 SV *obj = *(PL_stack_base + TOPMARK + 1);
330 329
331 if (SvROK (obj) && SvOBJECT (SvRV (obj))) 330 if (!SvGMAGICAL (obj) && SvROK (obj) && SvOBJECT (SvRV (obj)))
332 { 331 {
333 dSP; 332 dSP;
334 HV *stash = SvSTASH (SvRV (obj)); 333 HV *stash = SvSTASH (SvRV (obj));
335 334
336 /* simple "polymorphic" inline cache */ 335 /* simple "polymorphic" inline cache */
337 if (stash == last_stash) 336 if (stash == last_stash
337 && PL_sub_generation == last_sub_generation)
338 { 338 {
339 XPUSHs (last_res); 339 XPUSHs (last_cv);
340 PUTBACK; 340 PUTBACK;
341 } 341 }
342 else 342 else
343 { 343 {
344 PL_op = nextop;
345 nextop = Perl_pp_method_named (aTHX); 344 PL_op = nextop; nextop = Perl_pp_method_named (aTHX);
346 345
347 SPAGAIN; 346 SPAGAIN;
347 last_sub_generation = PL_sub_generation;
348 last_stash = stash; 348 last_stash = stash;
349 last_res = TOPs; 349 last_cv = TOPs;
350 } 350 }
351 } 351 }
352 else 352 else
353 { 353 {
354 /* error case usually */ 354 /* error case usually */
355 PL_op = nextop;
356 nextop = Perl_pp_method_named (aTHX); 355 PL_op = nextop; nextop = Perl_pp_method_named (aTHX);
357 } 356 }
358 } 357 }
359EOF 358EOF
360 359
361 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;
362} 400}
363 401
364sub cv2c { 402sub cv2c {
365 my ($cv) = @_; 403 my ($cv) = @_;
404
405 local @ops;
406 local @op_loop;
407 local %op_regcomp;
366 408
367 my %opsseen; 409 my %opsseen;
368 my @todo = $cv->START; 410 my @todo = $cv->START;
369 411
370 while (my $op = shift @todo) { 412 while (my $op = shift @todo) {
371 for (; $$op; $op = $op->next) { 413 for (; $$op; $op = $op->next) {
372 last if $opsseen{$$op}++; 414 last if $opsseen{$$op}++;
373 push @ops, $op; 415 push @ops, $op;
416
374 my $name = $op->name; 417 my $name = $op->name;
418 my $class = B::class $op;
419
375 if (B::class($op) eq "LOGOP") { 420 if ($class eq "LOGOP") {
376 push @todo, $op->other; 421 unshift @todo, $op->other; # unshift vs. push saves jumps
377 } elsif ($name eq "subst" and ${ $op->pmreplstart }) { 422
378 push @todo, $op->pmreplstart; 423 # regcomp/o patches ops at runtime, lets expect that
379 } elsif ($name =~ /^enter(loop|iter)$/) { 424 $op_regcomp{${$op->first}} = $op->next
380# if ($] > 5.009) { 425 if $name eq "regcomp" && $op->other->pmflags & B::PMf_KEEP;
381# $labels{${$op->nextop}} = "NEXT"; 426
382# $labels{${$op->lastop}} = "LAST"; 427 } elsif ($class eq "PMOP") {
383# $labels{${$op->redoop}} = "REDO"; 428 unshift @todo, $op->pmreplstart if ${$op->pmreplstart};
384# } else { 429
385# $labels{$op->nextop->seq} = "NEXT"; 430 } elsif ($class eq "LOOP") {
386# $labels{$op->lastop->seq} = "LAST"; 431 push @op_loop, [$op->nextop, $op->lastop->next, $op->redoop->next];
387# $labels{$op->redoop->seq} = "REDO"; 432 push @todo, $op->nextop, $op->lastop->next, $op->redoop->next;
388# }
389 } 433 }
390 } 434 }
391 } 435 }
392 436
393 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 $source .= "op_$$op: /* $op_name */\n";
448 #$source .= "fprintf (stderr, \"$$op in op $op_name\\n\");\n";#d#
449 #$source .= "{ dSP; sv_dump (TOPs); }\n";#d#
450
451 $source .= " PERL_ASYNC_CHECK ();\n"
452 unless exists $f_noasync{$op_name};
453
454 if (my $can = __PACKAGE__->can ("op_$op_name")) {
455 # handcrafted replacement
456 $can->($op);
457
458 } elsif (exists $f_unsafe{$op_name}) {
459 # unsafe, return to interpreter
460 assert "nextop == (OP *)$$op";
461 $source .= " return nextop;\n";
462
463 } elsif ("LOGOP" eq B::class $op) {
464 # logical operation with optionaö branch
465 out_callop;
466 out_cond_jump $op->other;
467 out_jump_next;
468
469 } elsif ("PMOP" eq B::class $op) {
470 # regex-thingy
471 out_callop;
472 out_cond_jump $op->pmreplroot if ${$op->pmreplroot};
473 out_jump_next;
474
475 } else {
476 # normal operator, linear execution
477 out_linear;
478 }
479 }
480
481 $op_name = "func exit"; assert (0);
482
483 $source .= <<EOF;
484op_0:
485 return 0;
486}
487EOF
488 #warn $source;
489
490 $source
491}
492
493my $uid = "aaaaaaa0";
494
495sub source2ptr {
496 my (@source) = @_;
497
498 my $stem = "/tmp/Faster-$$-" . $uid++;
499
500 open FILE, ">:raw", "$stem.c";
501 print FILE <<EOF;
394#define PERL_NO_GET_CONTEXT 502#define PERL_NO_GET_CONTEXT
395 503
396//#define NDEBUG 1
397#include <assert.h> 504#include <assert.h>
398 505
399#include "EXTERN.h" 506#include "EXTERN.h"
400#include "perl.h" 507#include "perl.h"
401#include "XSUB.h" 508#include "XSUB.h"
402 509
403OP *%%%FUNC%%% (pTHX) 510#define RUNOPS_TILL(op) \\
404{ 511while (nextop != (op)) \\
405 register OP *nextop = (OP *)${$ops[0]}L; 512 { \\
406EOF 513 PERL_ASYNC_CHECK (); \\
514 PL_op = nextop; nextop = (PL_op->op_ppaddr)(aTHX); \\
515 }
407 516
408 while (@ops) { 517EOF
409 $op = shift @ops; 518 for (@source) {
410 $op_name = $op->name; 519 my $func = $uid++;
411 520 $_ =~ s/%%%FUNC%%%/$func/g;
412 $source .= "op_$$op: /* $op_name */\n"; 521 print FILE $_;
413 #$source .= "fprintf (stderr, \"$$op in op $op_name\\n\");\n";#d# 522 $_ = $func;
414 #$source .= "{ dSP; sv_dump (TOPs); }\n";#d#
415
416 unless (exists $flag{noasync}{$op_name}) {
417 $source .= " PERL_ASYNC_CHECK ();\n";
418 }
419
420 if (my $can = __PACKAGE__->can ("op_$op_name")) {
421 $can->($op);
422 } elsif (exists $flag{unsafe}{$op_name}) {
423 $source .= " assert ((\"$op_name\", nextop == (OP *)$$op));\n";
424 $source .= " return nextop;\n";
425 } elsif ("LOGOP" eq B::class $op or exists $flag{otherop}{$op_name}) {
426 $source .= " assert ((\"$op_name\", nextop == (OP *)$$op));\n";
427 $source .= " PL_op = nextop; nextop = " . (callop $op) . ";\n";
428 $source .= " if (nextop == (OP *)${$op->other}L) goto op_${$op->other};\n";
429 $source .= " assert ((\"$op_name\", nextop == (OP *)${$op->next}));\n";
430 $source .= ${$op->next} ? " goto op_${$op->next};\n" : " return 0;\n";
431 } else {
432 out_linear;
433 }
434 } 523 }
435 524
436 $source .= "}\n";
437 #warn $source;
438
439 $source
440}
441
442sub source2ptr {
443 my ($source) = @_;
444
445 my $md5 = Digest::MD5::md5_hex $source;
446 $source =~ s/%%%FUNC%%%/Faster_$md5/;
447
448 my $stem = "/tmp/$md5";
449
450 unless (-e "$stem$_so") {
451 open FILE, ">:raw", "$stem.c";
452 print FILE $source;
453 close FILE; 525 close FILE;
454 system "$COMPILE -o $stem$_o $stem.c"; 526 system "$COMPILE -o $stem$_o $stem.c";
527 #d#unlink "$stem.c";
455 system "$LINK -o $stem$_so $stem$_o $LIBS"; 528 system "$LINK -o $stem$_so $stem$_o $LIBS";
456 } 529 unlink "$stem$_o";
457 530
458# warn $source;
459 my $so = DynaLoader::dl_load_file "$stem$_so" 531 my $so = DynaLoader::dl_load_file "$stem$_so"
460 or die "$stem$_so: $!"; 532 or die "$stem$_so: $!";
461 533
462 DynaLoader::dl_find_symbol $so, "Faster_$md5" 534 #unlink "$stem$_so";
463 or die "Faster_$md5: $!" 535
536 map +(DynaLoader::dl_find_symbol $so, $_), @source
464} 537}
538
539my %ignore;
465 540
466sub entersub { 541sub entersub {
467 my ($cv) = @_; 542 my ($cv) = @_;
468 543
544 my $pkg = $cv->STASH->NAME;
545
546 return if $ignore{$pkg};
547
548 warn "compiling ", $cv->STASH->NAME, "\n"
549 if $verbose;
550
469 eval { 551 eval {
470 my $source = cv2c $cv; 552 my @cv;
553 my @cv_source;
471 554
555 # always compile the whole stash
556 my %stash = $cv->STASH->ARRAY;
557 while (my ($k, $v) = each %stash) {
558 $v->isa (B::GV::)
559 or next;
560
561 my $cv = $v->CV;
562
563 if ($cv->isa (B::CV::)
564 && ${$cv->START}
565 && $cv->START->name ne "null") {
566 push @cv, $cv;
567 push @cv_source, cv2c $cv;
568 }
569 }
570
472 my $ptr = source2ptr $source; 571 my @ptr = source2ptr @cv_source;
473 572
573 for (0 .. $#cv) {
474 patch_cv $cv, $ptr; 574 patch_cv $cv[$_], $ptr[$_];
575 }
475 }; 576 };
476 577
477 warn $@ if $@; 578 if ($@) {
579 $ignore{$pkg}++;
580 warn $@;
581 }
478} 582}
479 583
480hook_entersub; 584hook_entersub;
481 585
4821; 5861;
483 587
484=back 588=back
485 589
590=head1 ENVIRONMENT VARIABLES
591
592The following environment variables influence the behaviour of Faster:
593
594=over 4
595
596=item FASTER_VERBOSE
597
598Faster will output more informational messages when set to values higher
599than C<0>. Currently, C<1> outputs which packages are being compiled.
600
601=item FASTER_DEBUG
602
603Add debugging code when set to values higher than C<0>. Currently, this
604adds 1-3 C<assert>'s per perl op, to ensure that opcode order and C
605execution order are compatible.
606
607=item FASTER_CACHE
608
609NOT YET IMPLEMENTED
610
611Set a persistent cache directory that caches compiled code
612fragments. Normally, code compiled by Faster will be deleted immediately,
613and every restart will recompile everything. Setting this variable to a
614directory makes Faster cache the generated files for re-use.
615
616This directory will always grow in contents, so you might need to erase it
617from time to time.
618
619=back
620
486=head1 LIMITATIONS 621=head1 BUGS/LIMITATIONS
487 622
488Tainting and debugging will disable Faster. 623Perl will check much less often for asynchronous signals in
624Faster-compiled code. It tries to check on every function call, loop
625iteration and every I/O operator, though.
626
627The following things will disable Faster. If you manage to enable them at
628runtime, bad things will happen. Enabling them at startup will be fine,
629though.
630
631 enabled tainting
632 enabled debugging
633
634Thread-enabled builds of perl will dramatically reduce Faster's
635performance, but you don't care about speed if you enable threads anyway.
636
637These constructs will force the use of the interpreter for the currently
638executed function as soon as they are being encountered during execution.
639
640 goto
641 next, redo (but not well-behaved last's)
642 eval
643 require
644 any use of formats
645 .., ... (flipflop operators)
489 646
490=head1 AUTHOR 647=head1 AUTHOR
491 648
492 Marc Lehmann <schmorp@schmorp.de> 649 Marc Lehmann <schmorp@schmorp.de>
493 http://home.schmorp.de/ 650 http://home.schmorp.de/

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines