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

Comparing Faster/Faster.pm (file contents):
Revision 1.4 by root, Thu Mar 9 22:32:17 2006 UTC vs.
Revision 1.17 by root, Fri Mar 10 18:58:35 2006 UTC

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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines