Skip to content
Permalink
Browse files

More aggressive unused $_ deletion

Now that $_ is lexical (at least, under 6.d), we can in most cases
better understand its usage, and delete it when we see there is no use
of it at all. This recovers the performance we lost in the lowering of
$_, caused by the fact that the reliance on lazy lexical initialization
went away, and we were thus always initializing it.
  • Loading branch information...
jnthn committed Jan 4, 2019
1 parent 11884e0 commit 87fcda819b74de7b3b75c191ea357af220a3b5e5
Showing with 46 additions and 37 deletions.
  1. +46 −37 src/Perl6/Optimizer.nqp
@@ -561,52 +561,61 @@ my class BlockVarOptimizer {
}
}

method delete_unused_magicals($block) {
# Magicals are contextual, so if we call anything when we're done for.
return 0 if $!calls || $!poisoned || $!uses_bindsig;
method delete_unused_magicals($block, $can_lower_topic) {
# Can't if we're poisoned or have to use the slow-path binder.
return 0 if $!poisoned || $!uses_bindsig || $*DYNAMICALLY_COMPILED;

# Otherwise see if there's any we can kill.
# We handle $_ first, because it is lexical rather than dynamic. We
# need to ensure topic lowering is OK; if it's not, there might be a
# late-bound access.
my %kill;
if nqp::existskey(%!decls, '$/') {
if !nqp::existskey(%!usages_flat, '$/') && !nqp::existskey(%!usages_inner, '$/') {
%kill<$/> := 1;
nqp::deletekey(%!decls, '$/');
}
}
if nqp::existskey(%!decls, '$!') {
if !nqp::existskey(%!usages_flat, '$!') && !nqp::existskey(%!usages_inner, '$!') {
%kill<$!> := 1;
nqp::deletekey(%!decls, '$!');
}
}
if nqp::existskey(%!decls, '') {
if !nqp::existskey(%!usages_flat, '') && !nqp::existskey(%!usages_inner, '') {
%kill<> := 1;
nqp::deletekey(%!decls, '');
}
}
if nqp::existskey(%!decls, '$_') {
my str $decl := %!decls<$_>.decl;
if $decl eq 'var' || $decl eq 'contvar' {
if !nqp::existskey(%!usages_flat, '$_') && !nqp::existskey(%!usages_inner, '$_') {
if !@!getlexouter_usages {
%kill<$_> := 1;
nqp::deletekey(%!decls, '$_');
}
elsif nqp::elems(@!getlexouter_usages) == 1 {
my $glob := @!getlexouter_usages[0];
if nqp::istype($glob, QAST::Op) && $glob[0].name eq '$_' &&
$glob[1][0].value eq '$_' {
$glob.op('null');
$glob.shift(); $glob.shift();
if $can_lower_topic && !$!topic_poisoned {
if nqp::existskey(%!decls, '$_') {
my str $decl := %!decls<$_>.decl;
if $decl eq 'var' || $decl eq 'contvar' {
unless nqp::existskey(%!usages_flat, '$_') || nqp::existskey(%!usages_inner, '$_') {
if !@!getlexouter_usages {
%kill<$_> := 1;
nqp::deletekey(%!decls, '$_');
}
elsif nqp::elems(@!getlexouter_usages) == 1 {
my $glob := @!getlexouter_usages[0];
if nqp::istype($glob, QAST::Op) && $glob[0].name eq '$_' &&
$glob[1][0].value eq '$_' {
$glob.op('null');
$glob.shift(); $glob.shift();
%kill<$_> := 1;
nqp::deletekey(%!decls, '$_');
}
}
}
}
}
}

# Other magicals are contextual, so only consider those if we've no
# calls (will be true in really simple builtins).
unless $!calls {
if nqp::existskey(%!decls, '$/') {
if !nqp::existskey(%!usages_flat, '$/') && !nqp::existskey(%!usages_inner, '$/') {
%kill<$/> := 1;
nqp::deletekey(%!decls, '$/');
}
}
if nqp::existskey(%!decls, '$!') {
if !nqp::existskey(%!usages_flat, '$!') && !nqp::existskey(%!usages_inner, '$!') {
%kill<$!> := 1;
nqp::deletekey(%!decls, '$!');
}
}
if nqp::existskey(%!decls, '') {
if !nqp::existskey(%!usages_flat, '') && !nqp::existskey(%!usages_inner, '') {
%kill<> := 1;
nqp::deletekey(%!decls, '');
}
}
}

# If we found things to eliminate, do so.
if %kill {
my @setups := @($block[0]);
@@ -1071,7 +1080,7 @@ class Perl6::Optimizer {
# We might be able to delete some of the magical variables when they
# are trivially unused, and also simplify takedispatcher.
if $!level >= 1 {
$vars_info.delete_unused_magicals($block);
$vars_info.delete_unused_magicals($block, $!can_lower_topic);
$vars_info.delete_unused_autoslurpy();
$vars_info.simplify_takedispatcher();
}

0 comments on commit 87fcda8

Please sign in to comment.
You can’t perform that action at this time.