=head1 NAME Faster - do some things faster =head1 SYNOPSIS use Faster; =head1 DESCRIPTION =over 4 =cut package Faster; use strict; use Config; use B (); use Digest::MD5 (); use DynaLoader (); BEGIN { our $VERSION = '0.01'; require XSLoader; XSLoader::load __PACKAGE__, $VERSION; } my $COMPILE = "$Config{cc} -c -I$Config{archlibexp}/CORE $Config{optimize} $Config{ccflags} $Config{cccdlflags}"; my $LINK = "$Config{ld} $Config{ldflags} $Config{lddlflags} $Config{ccdlflags}"; my $LIBS = "$Config{libs}"; my $_o = $Config{_o}; my $_so = ".so"; our $source; our $label_next; our $label_last; our $label_redo; my %flag; for (split /\n/, <next}) { $source .= " nextop = (OP *)${$op->next}L;\n"; $source .= " goto op_${$op->next};\n"; } else { $source .= " return 0;\n"; } } sub callop { my ($op) = @_; my $name = $op->name; $name eq "entersub" ? "(PL_ppaddr [OP_ENTERSUB]) (aTHX)" : $name eq "mapstart" ? "Perl_pp_grepstart (aTHX)" : "Perl_pp_$name (aTHX)" } sub op_nextstate { my ($op) = @_; $source .= " PL_curcop = (COP *)nextop;\n"; $source .= " PL_stack_sp = PL_stack_base + cxstack[cxstack_ix].blk_oldsp;\n"; $source .= " FREETMPS;\n"; out_next $op; } sub op_pushmark { my ($op) = @_; $source .= " PUSHMARK (PL_stack_sp);\n"; out_next $op; } sub op_const { my ($op) = @_; $source .= " { dSP; XPUSHs ((SV *)${$op->sv}L); PUTBACK; }\n"; out_next $op; } *op_gv = \&op_const; sub op_stringify { my ($op) = @_; $source .= " { dSP; dTARGET; sv_copypv (TARG, TOPs); SETTARG; }\n"; out_next $op; } sub op_and { my ($op) = @_; $source .= <other}L; goto op_${$op->other}; } } EOF out_next $op; } sub op_padsv { my ($op) = @_; my $flags = $op->flags; my $target = $op->targ; $source .= <flags & B::OPf_MOD) { if ($op->private & B::OPpLVAL_INTRO) { $source .= " SAVECLEARSV (PAD_SVl ((PADOFFSET)$target));\n"; } elsif ($op->private & B::OPpDEREF) { my $deref = $op->private & B::OPpDEREF; $source .= " Perl_vivify_ref (PAD_SVl ((PADOFFSET)$target), $deref);\n"; } } $source .= <START; while (my $op = shift @todo) { for (; $$op; $op = $op->next) { last if $opsseen{$$op}++; push @ops, $op; my $name = $op->name; if (B::class($op) eq "LOGOP") { push @todo, $op->other; } elsif ($name eq "subst" and ${ $op->pmreplstart }) { push @todo, $op->pmreplstart; } elsif ($name =~ /^enter(loop|iter)$/) { # if ($] > 5.009) { # $labels{${$op->nextop}} = "NEXT"; # $labels{${$op->lastop}} = "LAST"; # $labels{${$op->redoop}} = "REDO"; # } else { # $labels{$op->nextop->seq} = "NEXT"; # $labels{$op->lastop->seq} = "LAST"; # $labels{$op->redoop->seq} = "REDO"; # } } } } local $source = < #include "EXTERN.h" #include "perl.h" #include "XSUB.h" /*typedef OP *(*PPFUNC)(pTHX);*/ OP *%%%FUNC%%% (pTHX) { register OP *nextop = (OP *)${$ops[0]}L; EOF for my $op (@ops) { my $name = $op->name; my $ppaddr = ppaddr $op->type; $source .= "op_$$op: /* $name */\n"; #$source .= "fprintf (stderr, \"$$op in op $name\\n\");\n";#d# #$source .= "{ dSP; sv_dump (TOPs); }\n";#d# unless (exists $flag{noasync}{$name}) { $source .= " PERL_ASYNC_CHECK ();\n"; } if (my $can = __PACKAGE__->can ("op_$name")) { $can->($op); } elsif (exists $flag{unsafe}{$name}) { $source .= " assert ((\"$name\", nextop == (OP *)$$op));\n";#d# $source .= " PL_op = nextop; return " . (callop $op) . ";\n"; } elsif ("LOGOP" eq B::class $op or exists $flag{otherop}{$name}) { $source .= " assert ((\"$name\", nextop == (OP *)$$op));\n";#d# $source .= " PL_op = nextop; nextop = " . (callop $op) . ";\n"; $source .= " if (nextop == (OP *)${$op->other}L) goto op_${$op->other};\n"; $source .= " assert ((\"$name\", nextop == (OP *)${$op->next}));\n";#d# $source .= ${$op->next} ? " goto op_${$op->next};\n" : " return 0;\n"; } else { $source .= " assert ((\"$name\", nextop == (OP *)$$op));\n";#d# $source .= " PL_op = nextop; nextop = " . (callop $op) . ";\n"; if ($name eq "entersub") { $source .= <next}) { PERL_ASYNC_CHECK (); PL_op = nextop; nextop = (PL_op->op_ppaddr)(aTHX); } EOF } $source .= " assert ((\"$name\", nextop == (OP *)${$op->next}));\n";#d# $source .= ${$op->next} ? " goto op_${$op->next};\n" : " return 0;\n"; } } $source .= "}\n"; #warn $source; $source } sub source2ptr { my ($source) = @_; my $md5 = Digest::MD5::md5_hex $source; $source =~ s/%%%FUNC%%%/Faster_$md5/; my $stem = "/tmp/$md5"; unless (-e "$stem$_so") { open FILE, ">:raw", "$stem.c"; print FILE $source; close FILE; system "$COMPILE -o $stem$_o $stem.c"; system "$LINK -o $stem$_so $stem$_o $LIBS"; } # warn $source; my $so = DynaLoader::dl_load_file "$stem$_so" or die "$stem$_so: $!"; DynaLoader::dl_find_symbol $so, "Faster_$md5" or die "Faster_$md5: $!" } sub entersub { my ($cv) = @_; eval { my $source = cv2c $cv; my $ptr = source2ptr $source; patch_cv $cv, $ptr; }; warn $@ if $@; } hook_entersub; 1; =back =head1 LIMITATIONS Tainting and debugging will disable Faster. =head1 AUTHOR Marc Lehmann http://home.schmorp.de/ =cut