Skip to content

Commit

Permalink
Add a spesh plugin for return value decont
Browse files Browse the repository at this point in the history
Which both reduces the generated bytecode size for this operation, as
well as allowing us to provide optimized variants for a number of the
different cases that show up.
  • Loading branch information
jnthn committed Jul 9, 2018
1 parent 523ffae commit 8d65276
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 48 deletions.
123 changes: 75 additions & 48 deletions src/Perl6/Actions.nqp
Expand Up @@ -807,69 +807,96 @@ register_op_desugar('p6var', -> $qast {
)
)
});
register_op_desugar('p6decontrv_internal', sub ($qast) {
my $result := QAST::Node.unique('result');
my $Scalar := QAST::WVal.new( :value(nqp::gethllsym('perl6', 'Scalar')) );
my $Iterable := QAST::WVal.new( :value(nqp::gethllsym('perl6', 'Iterable')) );
QAST::Stmt.new(
QAST::Op.new(
:op('bind'),
QAST::Var.new( :name($result), :scope('local'), :decl('var') ),
QAST::Op.new( :op('wantdecont'), $qast[0] )
),
QAST::Op.new(
# If it's a container...
:op('if'),
QAST::Op.new(
:op('if'),
{
my $is_moar;
register_op_desugar('p6decontrv_internal', -> $qast {
unless nqp::isconcrete($is_moar) {
$is_moar := nqp::getcomp('perl6').backend.name eq 'moar';
}
if $is_moar {
my $result := QAST::Node.unique('result');
QAST::Stmt.new(
QAST::Op.new(
:op('isconcrete_nd'),
QAST::Var.new( :name($result), :scope('local') )
:op('bind'),
QAST::Var.new( :name($result), :scope('local'), :decl('var') ),
QAST::Op.new( :op('wantdecont'), $qast[0] )
),
QAST::Op.new(
:op('iscont'),
QAST::Var.new( :name($result), :scope('local') )
:op('call'),
QAST::Op.new(
:op('speshresolve'),
QAST::SVal.new( :value('decontrv') ),
QAST::Var.new( :name($result), :scope('local') )
),
QAST::Var.new( :name($result), :scope('local') ),
)
),
# It's a container; is it an rw one?
QAST::Op.new(
:op('if'),
)
}
else {
my $result := QAST::Node.unique('result');
my $Scalar := QAST::WVal.new( :value(nqp::gethllsym('perl6', 'Scalar')) );
my $Iterable := QAST::WVal.new( :value(nqp::gethllsym('perl6', 'Iterable')) );
QAST::Stmt.new(
QAST::Op.new(
:op('isrwcont'),
QAST::Var.new( :name($result), :scope('local') )
:op('bind'),
QAST::Var.new( :name($result), :scope('local'), :decl('var') ),
QAST::Op.new( :op('wantdecont'), $qast[0] )
),
# Yes; does it contain an Iterable? If so, rewrap it. If
# not, strip it.
QAST::Op.new(
# If it's a container...
:op('if'),
QAST::Op.new(
:op('istype'),
QAST::Var.new( :name($result), :scope('local') ),
$Iterable
),
QAST::Op.new(
:op('p6bindattrinvres'),
QAST::Op.new( :op('create'), $Scalar ),
$Scalar,
QAST::SVal.new( :value('$!value') ),
:op('if'),
QAST::Op.new(
:op('decont'),
:op('isconcrete_nd'),
QAST::Var.new( :name($result), :scope('local') )
),
QAST::Op.new(
:op('iscont'),
QAST::Var.new( :name($result), :scope('local') )
)
),
# It's a container; is it an rw one?
QAST::Op.new(
:op('decont'),
:op('if'),
QAST::Op.new(
:op('isrwcont'),
QAST::Var.new( :name($result), :scope('local') )
),
# Yes; does it contain an Iterable? If so, rewrap it. If
# not, strip it.
QAST::Op.new(
:op('if'),
QAST::Op.new(
:op('istype'),
QAST::Var.new( :name($result), :scope('local') ),
$Iterable
),
QAST::Op.new(
:op('p6bindattrinvres'),
QAST::Op.new( :op('create'), $Scalar ),
$Scalar,
QAST::SVal.new( :value('$!value') ),
QAST::Op.new(
:op('decont'),
QAST::Var.new( :name($result), :scope('local') )
)
),
QAST::Op.new(
:op('decont'),
QAST::Var.new( :name($result), :scope('local') )
)
),
# Not rw, so leave container in place.
QAST::Var.new( :name($result), :scope('local') )
)
),
# Not rw, so leave container in place.
QAST::Var.new( :name($result), :scope('local') )
),
# Not a container, so just hand back value
QAST::Var.new( :name($result), :scope('local') )
)
)
});
),
# Not a container, so just hand back value
QAST::Var.new( :name($result), :scope('local') )
)
)
}
});
}
{
my $is_moar;
register_op_desugar('p6assign', -> $qast {
Expand Down
77 changes: 77 additions & 0 deletions src/vm/moar/spesh-plugins.nqp
Expand Up @@ -44,6 +44,83 @@ nqp::speshreg('perl6', 'maybemeth', -> $obj, str $name {
!! &discard-and-nil
});

## Return value decontainerization plugin

# Often we have nothing at all to do, in which case we can make it a no-op.
# Other times, we need a decont. In a few, we need to re-wrap it.

{
# We look up Iterable when the plugin is used.
my $Iterable := NQPMu;

sub identity($obj) { $obj }
sub decont($obj) { nqp::decont($obj) }
sub recont($obj) {
my $rc := nqp::create(Scalar);
nqp::bindattr($rc, Scalar, '$!value', nqp::decont($obj));
$rc
}
sub decontrv($cont) {
if nqp::isrwcont($cont) {
# It's an RW container, so we really need to decont it.
my $rv := nqp::decont($cont);
if nqp::istype($rv, $Iterable) {
my $rc := nqp::create(Scalar);
nqp::bindattr($rc, Scalar, '$!value', $rv);
$rc
}
else {
$rv
}
}
else {
# A read-only container, so just return it.
$cont
}
}

nqp::speshreg('perl6', 'decontrv', sub ($rv) {
$Iterable := nqp::gethllsym('perl6', 'Iterable');
nqp::speshguardtype($rv, nqp::what_nd($rv));
if nqp::isconcrete_nd($rv) && nqp::iscont($rv) {
# Guard that it's concrete, so this only applies for container
# instances.
nqp::speshguardconcrete($rv);

# If it's a Scalar container then we can optimize further.
if nqp::eqaddr(nqp::what_nd($rv), Scalar) {
# Grab the descriptor.
my $desc := nqp::speshguardgetattr($rv, Scalar, '$!descriptor');
if nqp::isconcrete($desc) {
# Descriptor, so `rw`. Guard on type of value. If it's
# Iterable, re-containerize. If not, just decont.
nqp::speshguardconcrete($desc);
my $value := nqp::speshguardgetattr($rv, Scalar, '$!value');
nqp::speshguardtype($value, nqp::what_nd($value));
return nqp::istype($value, $Iterable) ?? &recont !! &decont;
}
else {
# No descriptor, so it's already readonly. Identity.
nqp::speshguardtypeobj($desc);
return &identity;
}
}

# Otherwise, full decont.
return &decontrv;
}
else {
# No decontainerization to do, so just produce identity.
unless nqp::isconcrete($rv) {
# Needed as a container's type object is not a container, but a
# concrete instance would be.
nqp::speshguardtypeobj($rv);
}
return &identity;
}
});
}

## Assignment plugin

# We case-analyze assignments and provide these optimized paths for a range of
Expand Down

0 comments on commit 8d65276

Please sign in to comment.