=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 @ops; my $op; my $op_name; my %flag; for (split /\n/, <next}) { $source .= " assert ((\"$op_name\", nextop == (OP *)${$op->next}));\n"; $source .= " goto op_${$op->next};\n"; } else { $source .= " return 0;\n"; } } sub out_next { $source .= " nextop = (OP *)${$op->next}L;\n"; out_gotonext; } sub out_linear { $source .= " assert ((\"$op_name\", nextop == (OP *)$$op));\n";#d# $source .= " PL_op = nextop; nextop = " . (callop $op) . ";\n"; if ($op_name eq "entersub") { $source .= <next}L) { PERL_ASYNC_CHECK (); PL_op = nextop; nextop = (PL_op->op_ppaddr)(aTHX); } EOF } out_gotonext; } sub op_nextstate { $source .= " PL_curcop = (COP *)nextop;\n"; $source .= " PL_stack_sp = PL_stack_base + cxstack[cxstack_ix].blk_oldsp;\n"; $source .= " FREETMPS;\n"; out_next; } sub op_pushmark { $source .= " PUSHMARK (PL_stack_sp);\n"; out_next; } if ($Config{useithreads} ne "define") { # disable optimisations on ithreads *op_const = sub { $source .= " { dSP; XPUSHs ((SV *)${$op->sv}L); PUTBACK; }\n"; out_next; }; *op_gv = \&op_const; *op_aelemfast = sub { my $targ = $op->targ; my $private = $op->private; $source .= " {\n"; if ($op->flags & B::OPf_SPECIAL) { $source .= " AV *av = (AV*)PAD_SV((PADOFFSET)$targ);\n"; } else { $source .= " AV *av = GvAV ((GV *)${$op->sv}L);\n"; } if ($op->flags & B::OPf_MOD) { $source .= " SV *sv = *av_fetch (av, $private, 1);\n"; } else { $source .= " SV **svp = av_fetch (av, $private, 0); SV *sv = svp ? *svp : &PL_sv_undef;\n"; } if (!($op->flags & B::OPf_MOD)) { $source .= " if (SvGMAGICAL (sv)) sv = sv_mortalcopy (sv);\n"; } $source .= " dSP;\n"; $source .= " XPUSHs (sv);\n"; $source .= " PUTBACK;\n"; $source .= " }\n"; out_next; }; *op_gvsv = sub { $source .= " {\n"; $source .= " dSP;\n"; $source .= " EXTEND (SP, 1);\n"; if ($op->private & B::OPpLVAL_INTRO) { $source .= " PUSHs (save_scalar ((GV *)${$op->sv}L));\n"; } else { $source .= " PUSHs (GvSV ((GV *)${$op->sv}L));\n"; } $source .= " PUTBACK;\n"; $source .= " }\n"; out_next; }; } sub op_stringify { $source .= " { dSP; dTARGET; sv_copypv (TARG, TOPs); SETTARG; }\n"; out_next; } sub op_and { $source .= <other}L; goto op_${$op->other}; } } EOF out_next; } sub op_or { $source .= <other}L; goto op_${$op->other}; } } EOF out_next; } sub op_padsv { 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" OP *%%%FUNC%%% (pTHX) { register OP *nextop = (OP *)${$ops[0]}L; EOF while (@ops) { $op = shift @ops; $op_name = $op->name; $source .= "op_$$op: /* $op_name */\n"; #$source .= "fprintf (stderr, \"$$op in op $op_name\\n\");\n";#d# #$source .= "{ dSP; sv_dump (TOPs); }\n";#d# unless (exists $flag{noasync}{$op_name}) { $source .= " PERL_ASYNC_CHECK ();\n"; } if (my $can = __PACKAGE__->can ("op_$op_name")) { $can->($op); } elsif (exists $flag{unsafe}{$op_name}) { $source .= " assert ((\"$op_name\", nextop == (OP *)$$op));\n"; $source .= " return nextop;\n"; } elsif ("LOGOP" eq B::class $op or exists $flag{otherop}{$op_name}) { $source .= " assert ((\"$op_name\", nextop == (OP *)$$op));\n"; $source .= " PL_op = nextop; nextop = " . (callop $op) . ";\n"; $source .= " if (nextop == (OP *)${$op->other}L) goto op_${$op->other};\n"; $source .= " assert ((\"$op_name\", nextop == (OP *)${$op->next}));\n"; $source .= ${$op->next} ? " goto op_${$op->next};\n" : " return 0;\n"; } else { out_linear; } } $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