Skip to content

Commit

Permalink
More aggressive unused $_ deletion
Browse files Browse the repository at this point in the history
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 87fcda8
Showing 1 changed file with 46 additions and 37 deletions.
83 changes: 46 additions & 37 deletions src/Perl6/Optimizer.nqp
Expand Up @@ -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]);
Expand Down Expand Up @@ -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();
}
Expand Down

0 comments on commit 87fcda8

Please sign in to comment.