Skip to content

Commit

Permalink
Do some better job optimizing Junction on RHS
Browse files Browse the repository at this point in the history
- do pure typematching on `Junction` type object and don't bother
  autothreading over a concrete junction on LHS
- let the raku-smartmatch dispatcher use BOOLIFY-ACCEPTS shortcut over
  concrete junctions on RHS
  • Loading branch information
vrurg committed Jan 30, 2022
1 parent ba4d233 commit 4995584
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 11 deletions.
30 changes: 22 additions & 8 deletions src/Perl6/Optimizer.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -1110,7 +1110,7 @@ my class Operand {

# $!localizable can be set only once.
method localizable($set = nqp::null()) {
$!localizable := nqp::istrue($set) if nqp::isnull($!localizable);
$!localizable := nqp::istrue($set) if nqp::isnull($!localizable) && !nqp::isnull($set);
$!localizable
}

Expand Down Expand Up @@ -1222,6 +1222,14 @@ my class Operand {
$!value.HOW.name($!value)
}

method is-type-object($type = nqp::null(), :$strict = 0) {
self.value-analyze;
$!value-kind == $OPERAND_VALUE_CONST
&& !nqp::isconcrete($!value)
&& (nqp::isnull($type)
|| ($strict ?? nqp::eqaddr($!value, $type) !! nqp::istype($!value, $type)))
}

method str() {
self.value-analyze;
if $!value-kind == $OPERAND_VALUE_CONST {
Expand Down Expand Up @@ -1519,6 +1527,8 @@ my class SmartmatchOptimizer {

method maybe_respect_junctions($lhs, $rhs, $optimized_ast, :$negated = 0) {
return $optimized_ast unless $lhs.can-be-junction;
# typematching against a Junction doesn't need a fallback
return $optimized_ast if $optimized_ast.ann('sm_typematch') && $rhs.is-type-object($!symbols.Junction);
my $fallback_ast;
if $rhs.has-value && $rhs.value-kind == $OPERAND_VALUE_CONST && $rhs.is-ACCEPTS-default {
# When we can guarantee that RHS uses CORE's ACCEPTS then instead of doing .ACCEPTS.Bool chain where all
Expand Down Expand Up @@ -1552,14 +1562,15 @@ my class SmartmatchOptimizer {
method maybe_typematch($lhs, $rhs, :$in-when = 0, :$negated = 0) {
my $sm_type;
# Don't try if RHS is not a compile-time known type object or it has user-defined ACCEPTS method. In the latter
# case we can't guarantee that method behavior is independent of run-tim conditions, contrary to core-provided
# case we can't guarantee that method behavior is independent of run-time conditions, contrary to core-provided
# versions of it.
return nqp::null()
unless $rhs.value-kind == $OPERAND_VALUE_CONST
&& !nqp::isconcrete($sm_type := $rhs.value)
unless $rhs.is-type-object
&& $rhs.is-ACCEPTS-default(:u-invocant)
&& ($in-when || !$lhs.is-junction);

$sm_type := $rhs.value;

# We wouldn't be able to shortcut typematching for generics and it is barely possible against most of subsets.
return nqp::null()
if $sm_type.HOW.archetypes.generic
Expand Down Expand Up @@ -1603,7 +1614,9 @@ my class SmartmatchOptimizer {
# For 'when' statement it is sufficient to get native 0 or 1. Otherwise we need to boolify.
$sm_ast := QAST::Op.new( :op<istype>, $lhs.ast, $rhs.ast );
$sm_ast := QAST::Op.new( :op<not_i>, $sm_ast ) if $negated;
$in-when ?? $sm_ast !! QAST::Op.new( :op<hllbool>, $sm_ast )
($in-when
?? $sm_ast
!! QAST::Op.new( :op<hllbool>, $sm_ast )).annotate_self('sm_typematch', 1)
}

method maybe_literal($lhs, $rhs, :$in-when = 0, :$negated) {
Expand Down Expand Up @@ -2038,14 +2051,14 @@ my class SmartmatchOptimizer {
my $lhs := Operand.new($op[0], $!optimizer, $!symbols, :name<LHS>);
my $rhs := Operand.new($op[1], $!optimizer, $!symbols, :name<RHS>);

$lhs.localizable($lhs.can-be-junction);
$lhs.localizable($lhs.can-be-junction && !$rhs.is-type-object($!symbols.Junction));

note("- LHS:\n", $lhs.dump(2), "- RHS:\n", $rhs.dump(2)) if $!debug;

my $sm_ast;
my $result := nqp::null();

if $lhs.is-junction {
if $lhs.is-junction && !$rhs.is-type-object($!symbols.Junction) {
$result := QAST::Op.new(
:op<callmethod>,
:name<BOOLIFY-ACCEPTS>,
Expand All @@ -2064,6 +2077,7 @@ my class SmartmatchOptimizer {
note(" - LHS can be a junction? ", $lhs.can-be-junction,
", is junction? ", $lhs.is-junction,
", type: ", $lhs.value-type-name);
note(" - RHS is a Junction type object? ", $rhs.is-type-object($!symbols.Junction));
note($lhs.dump(2));
}
$result := self.maybe_respect_junctions($lhs, $rhs, $sm_ast, :$negated);
Expand Down Expand Up @@ -2137,7 +2151,7 @@ my class SmartmatchOptimizer {
my $negated := $sm_accepts.ann('smartmatch_negated');
my $boolified := $op[0][2].ann('smartmatch_boolified');

$lhs.localizable($lhs.can-be-junction);
$lhs.localizable($lhs.can-be-junction && !$rhs.is-type-object($!symbols.Junction));

note("Topicalized smartmatch:\n TOPIC:\n", $lhs.dump(11), "\n RHS:\n", $rhs.dump(11)) if $!debug;

Expand Down
11 changes: 8 additions & 3 deletions src/vm/moar/dispatchers.nqp
Original file line number Diff line number Diff line change
Expand Up @@ -3516,7 +3516,7 @@ nqp::dispatch('boot-syscall', 'dispatcher-register', 'raku-isinvokable', -> $cap
# The dispatch receives:
# - lhs with containerization preserved
# - rhs with containerization preserved
# - boolification flag)
# - boolification flag
# boolification flag can either be -1 to negate, 0 to return as-is, 1 to boolify
# Note that boolification flag is not guarded because it is expected to be invariant over call site.
my $Match := find-core-symbol('Match', :ctx(nqp::ctxcaller(nqp::ctx())));
Expand Down Expand Up @@ -3587,10 +3587,15 @@ nqp::dispatch('boot-syscall', 'dispatcher-register', 'raku-isinvokable', -> $cap
$explicit-accepts := 0;
}
}
# Delegate to Junction.BOOLIFY-ACCEPTS if possible and makes sense.
# - Junction type object on RHS is always a type match and we can pass it to the typematching branch
# - when boolifying over a concrete Regex ACCEPTS fallback is required too
# - BOOLIFY-ACCEPTS only makes sense when ACCEPTS doesn't handle junctions in a special way. For now this can
# only be guaranteed for CORE's ACCEPTS only.
elsif nqp::istype_nd($lhs, Junction)
&& nqp::isconcrete_nd($lhs)
&& !(nqp::isconcrete_nd($rhs) && (nqp::istype_nd($rhs, Junction)
|| ($boolification == 1 && nqp::istype_nd($rhs, Regex))))
&& (nqp::isconcrete_nd($rhs) || !nqp::istype($rhs, Junction))
&& !(nqp::isconcrete_nd($rhs) && $boolification == 1 && nqp::istype_nd($rhs, Regex))
&& is-method-setting-only($rhs, 'ACCEPTS', :D)
{
# nqp::dispatch('boot-syscall', 'dispatcher-guard-literal', $track-boolification);
Expand Down

0 comments on commit 4995584

Please sign in to comment.