Skip to content

Instantly share code, notes, and snippets.

@niner
Created February 16, 2022 07:27
Show Gist options
  • Save niner/05f73c3288e450b7712e723288c8d0f6 to your computer and use it in GitHub Desktop.
Save niner/05f73c3288e450b7712e723288c8d0f6 to your computer and use it in GitHub Desktop.
diff --git a/src/Perl6/Optimizer.nqp b/src/Perl6/Optimizer.nqp
index b19ba877a..874980cde 100644
--- a/src/Perl6/Optimizer.nqp
+++ b/src/Perl6/Optimizer.nqp
@@ -45,6 +45,7 @@ my class Symbols {
has $!Seq;
has $!AST;
has $!LoweredAwayLexical;
+ has $!Positional;
# Top routine, for faking it when optimizing post-inline.
has $!fake_top_routine;
@@ -83,6 +84,7 @@ my class Symbols {
$!Seq := self.find_in_setting('Seq');
$!AST := self.find_in_setting('AST');
$!LoweredAwayLexical := self.find_symbol(['Rakudo', 'Internals', 'LoweredAwayLexical']);
+ $!Positional := self.find_in_setting('Positional');
nqp::pop(@!block_stack);
}
@@ -136,6 +138,7 @@ my class Symbols {
method Seq() { $!Seq }
method AST() { $!AST }
method LoweredAwayLexical() { $!LoweredAwayLexical }
+ method Positional() { $!Positional }
# The following function is a nearly 1:1 copy of World.find_symbol.
# Finds a symbol that has a known value at compile time from the
@@ -3095,7 +3098,7 @@ class Perl6::Optimizer {
}
method optimize_array_variable_initialization($op) {
- my $Positional := $!symbols.find_in_setting('Positional');
+ my $Positional := $!symbols.Positional;
if $op[0].returns =:= $Positional {
# Turns the @var.STORE(infix:<,>(...)) into:
# nqp::getattr(
@@ -3352,6 +3355,55 @@ class Perl6::Optimizer {
:protoguilt($ct_result_proto == -1)
) unless $*NO-COMPILE-TIME-THROWAGE;
}
+ else {
+ # Can't directly chose a multi candidate, but maybe still simplify refs
+ note(" checking for simplify refs") if $!debug;
+ my $call := $op;
+ my @args := $call.list;
+ my int $i := $call.name eq '' ?? 1 !! 0;
+ my int $n := nqp::elems(@args);
+ my int $p := 0;
+ #my @candidates := nqp::getattr(nqp::decont($obj), $!symbols.Routine, '@!dispatchees');
+ my @candidates := $obj.possible_candidates(@types, @flags);
+ note(" found " ~ nqp::elems(@candidates) ~ " candidates") if $!debug;
+ while $i < $n {
+ my $arg := @args[$i];
+ unless $arg.named || $arg.flat {
+ if nqp::istype($arg, QAST::Var) {
+ my str $scope := $arg.scope;
+ my int $lref := $scope eq 'lexicalref';
+ my int $aref := $scope eq 'attributeref';
+ if $lref || $aref {
+ my $lower := 1;
+ for @candidates -> $cand {
+ my $sig := $cand.signature;
+ #my $ct_result_cand := nqp::p6trialbind($sig, @types, @flags);
+ #next if $ct_result_cand == -1;
+ if nqp::iseq_n($sig.arity, $sig.count) {
+ my $param := nqp::getattr($sig, $!symbols.Signature, '@!params')[$p];
+ if nqp::can($param, 'rw') && $param.rw {
+ note("not lowering $i because rw") if $!debug;
+ $lower := 0;
+ last;
+ }
+ }
+ else {
+ note("not lowering $i because arity " ~ $sig.arity ~ " != " ~ $sig.count) if $!debug;
+ $lower := 0;
+ last;
+ }
+ }
+ if $lower {
+ note("Lowering arg $i") if $!debug;
+ $arg.scope($lref ?? 'lexical' !! 'attribute');
+ }
+ }
+ }
+ $p++;
+ }
+ $i++;
+ }
+ }
}
if $op.op eq 'chain' { $!chain_depth := $!chain_depth - 1 }
}
@@ -3477,6 +3529,45 @@ class Perl6::Optimizer {
}
}
+ elsif nqp::istype($var,QAST::Call) && $var.name eq '&postcircumfix:<[ ]>' {
+ if nqp::istype($var[0],QAST::Var) {
+ my $returns := $var[0].returns;
+ if nqp::istype($returns, $!symbols.Positional) && nqp::can($returns.HOW, 'role_arguments') {
+ my $slot_type := $returns.HOW.role_arguments($returns)[0];
+ if ($primspec := nqp::objprimspec($slot_type)) {
+ if $primspec == 1 {
+ my $tmp := QAST::Node.unique: 'array_inc_';
+ return QAST::Stmts.new:
+ QAST::Op.new(:op<bind>,
+ QAST::Var.new(:name($tmp), :scope<local>, :decl<var>, :returns($slot_type)),
+ QAST::Op.new(:op('atpos_i'), $var[0], $var[1]),
+ ),
+ QAST::Op.new(:op<bindpos_i>, $var[0], $var[1],
+ QAST::Op.new: :op('add_i'),
+ QAST::Var.new(:name($tmp), :scope<local>, :returns($slot_type)),
+ QAST::IVal.new: :value(1)
+ ),
+ QAST::Var.new(:name($tmp), :scope<local>, :returns($slot_type));
+ }
+ elsif $primspec == 10 {
+ my $tmp := QAST::Node.unique: 'array_inc_';
+ return QAST::Stmts.new:
+ QAST::Op.new(:op<bind>,
+ QAST::Var.new(:name($tmp), :scope<local>, :decl<var>, :returns($slot_type)),
+ QAST::Op.new(:op('atpos_u'), $var[0], $var[1]),
+ ),
+ QAST::Op.new(:op<bindpos_u>, $var[0], $var[1],
+ QAST::Op.new: :op('add_i'),
+ QAST::Var.new(:name($tmp), :scope<local>, :returns($slot_type)),
+ QAST::IVal.new: :value(1)
+ ),
+ QAST::Var.new(:name($tmp), :scope<local>, :returns($slot_type));
+ }
+ }
+ }
+ }
+ }
+
# XXX TODO: my tests show the opt below makes things 25% slower.
# Even without the temp var business, and unoptimized version:
# my $i = 1; my $z; { for ^10000_000 { $z = 1 + ++$i }; say now - ENTER now }
@@ -4457,6 +4548,7 @@ class Perl6::Optimizer {
# if we find them check if the expectation is for an non-rw argument.
method simplify_refs($call, $sig) {
if nqp::iseq_n($sig.arity, $sig.count) {
+ my @params := nqp::getattr($sig, $!symbols.Signature, '@!params');
my @args := $call.list;
my int $i := $call.name eq '' ?? 1 !! 0;
my int $n := nqp::elems(@args);
@@ -4469,7 +4561,7 @@ class Perl6::Optimizer {
my int $lref := $scope eq 'lexicalref';
my int $aref := $scope eq 'attributeref';
if $lref || $aref {
- my $param := nqp::getattr($sig, $!symbols.Signature, '@!params')[$p];
+ my $param := @params[$p];
if nqp::can($param, 'rw') {
unless $param.rw {
$arg.scope($lref ?? 'lexical' !! 'attribute');
diff --git a/src/Perl6/bootstrap.c/BOOTSTRAP.nqp b/src/Perl6/bootstrap.c/BOOTSTRAP.nqp
index 226e401ee..5e226ab99 100644
--- a/src/Perl6/bootstrap.c/BOOTSTRAP.nqp
+++ b/src/Perl6/bootstrap.c/BOOTSTRAP.nqp
@@ -1098,11 +1098,17 @@ my class Binder {
nqp::getattr($param, Parameter, '@!post_constraints')) {
next
}
+ if $flags +& $SIG_ELEM_IS_CAPTURE
+ && nqp::isnull(
+ nqp::getattr($param, Parameter, '@!post_constraints')) {
+ return $TRIAL_BIND_OK;
+ }
if $flags +& nqp::bitneg_i(
$SIG_ELEM_MULTI_INVOCANT +| $SIG_ELEM_IS_RAW +|
$SIG_ELEM_IS_COPY +| $SIG_ELEM_ARRAY_SIGIL +|
$SIG_ELEM_HASH_SIGIL +| $SIG_ELEM_NATIVE_VALUE +|
- $SIG_ELEM_IS_OPTIONAL) || $flags +& $SIG_ELEM_IS_RW {
+ $SIG_ELEM_IS_OPTIONAL +| $SIG_ELEM_DEFINED_ONLY +| $SIG_ELEM_UNDEFINED_ONLY) || $flags +& $SIG_ELEM_IS_RW {
+ note(" bad flags") if $*DEBUG;
return $TRIAL_BIND_NOT_SURE;
}
unless nqp::isnull(nqp::getattr($param, Parameter, '@!named_names')) {
@@ -3434,6 +3440,181 @@ BEGIN {
# Otherwise, dunno...we'll have to find out at runtime.
return [$MD_CT_NOT_SURE, NQPMu];
}));
+ Routine.HOW.add_method(Routine, 'possible_candidates', nqp::getstaticcode(sub ($self, @args, @flags) {
+ # Compile time dispatch result.
+ my $MD_CT_NOT_SURE := 0; # Needs a runtime dispatch.
+ my $MD_CT_DECIDED := 1; # Worked it out; see result.
+ my $MD_CT_NO_WAY := -1; # Proved it'd never manage to dispatch.
+
+ # Other constants we need.
+ my int $DEFCON_DEFINED := 1;
+ my int $DEFCON_UNDEFINED := 2;
+ my int $DEFCON_MASK := $DEFCON_DEFINED +| $DEFCON_UNDEFINED;
+ my int $TYPE_NATIVE_INT := 4;
+ my int $TYPE_NATIVE_NUM := 8;
+ my int $TYPE_NATIVE_STR := 16;
+ my int $TYPE_NATIVE_UINT := 32;
+ my int $TYPE_NATIVE_MASK := $TYPE_NATIVE_INT +| $TYPE_NATIVE_UINT +| $TYPE_NATIVE_NUM +| $TYPE_NATIVE_STR;
+ my int $BIND_VAL_OBJ := 0;
+ my int $BIND_VAL_INT := 1;
+ my int $BIND_VAL_UINT := 10;
+ my int $BIND_VAL_NUM := 2;
+ my int $BIND_VAL_STR := 3;
+ my int $ARG_IS_LITERAL := 32;
+
+ # Count arguments.
+ my int $num_args := nqp::elems(@args);
+
+ # Get list and number of candidates, triggering a sort if there are none.
+ my $dcself := nqp::decont($self);
+ my @candidates := nqp::getattr($dcself, Routine, '@!dispatch_order');
+ if nqp::isnull(@candidates) {
+ nqp::scwbdisable();
+ @candidates := $dcself.'!sort_dispatchees_internal'();
+ nqp::bindattr($dcself, Routine, '@!dispatch_order', @candidates);
+ nqp::scwbenable();
+ }
+
+ # Look through the candidates. If we see anything that needs a bind
+ # check or a definedness check, we can't decide it at compile time,
+ # so bail out immediately.
+ my int $all_native := 1;
+ my int $cur_idx := 0;
+ my int $seen_all := 0;
+ my int $arity_possible := 0;
+ my int $type_possible := 0;
+ my int $used_defcon;
+ my int $type_mismatch;
+ my int $type_check_count;
+ my int $type_match_possible;
+ my int $i;
+ my $cur_candidate;
+ my @possible;
+ my &note := nqp::gethllsym('nqp', 'note');
+ while 1 {
+ $cur_candidate := nqp::atpos(@candidates, $cur_idx);
+ $used_defcon := 0;
+
+ # Did we reach the end of a tied group? If so, note we can only
+ # consider the narrowest group, *unless* they are all natively
+ # typed candidates in which case we can look a bit further.
+ # We also exit if we found something.
+ unless nqp::isconcrete($cur_candidate) {
+ ++$cur_idx;
+ if nqp::isconcrete(nqp::atpos(@candidates, $cur_idx))
+ && $all_native {
+ next;
+ }
+ else {
+ $seen_all := !nqp::isconcrete(nqp::atpos(@candidates, $cur_idx));
+ last;
+ }
+ }
+ note("candidate $cur_idx file " ~ $cur_candidate<sub>.file ~ ":" ~ $cur_candidate<sub>.line) if $*DEBUG;
+
+ # Check if it's admissible by arity.
+ if $num_args < nqp::atkey($cur_candidate, 'min_arity')
+ || $num_args > nqp::atkey($cur_candidate, 'max_arity') {
+ ++$cur_idx;
+ note(" wrong arity") if $*DEBUG;
+ next;
+ }
+
+ # If we got this far, something at least matched on arity.
+ $arity_possible := 1;
+
+ # Check if it's admissible by type.
+ $type_check_count := nqp::atkey($cur_candidate, 'num_types') > $num_args
+ ?? $num_args
+ !! nqp::atkey($cur_candidate, 'num_types');
+ $type_mismatch := 0;
+ $type_match_possible := 1;
+ $i := -1;
+ while ++$i < $type_check_count {
+ my int $type_flags := nqp::atpos_i(nqp::atkey($cur_candidate, 'type_flags'), $i);
+ my int $got_prim := nqp::atpos(@flags, $i) +& 0xF;
+ if $type_flags +& $TYPE_NATIVE_MASK {
+ # Looking for a natively typed value. Did we get one?
+ if $got_prim == $BIND_VAL_OBJ {
+ # Object; won't do.
+ note(" arg $i got obj for native") if $*DEBUG;
+ $type_mismatch := 1;
+ $type_match_possible := 0;
+ last;
+ }
+
+ # Yes, but does it have the right type? Also look at rw-ness for literals.
+ my int $literal := nqp::atpos(@flags, $i) +& $ARG_IS_LITERAL;
+ if (($type_flags +& $TYPE_NATIVE_INT) && $got_prim != $BIND_VAL_INT)
+ || (($type_flags +& $TYPE_NATIVE_UINT) && $got_prim != $BIND_VAL_UINT)
+ || (($type_flags +& $TYPE_NATIVE_NUM) && $got_prim != $BIND_VAL_NUM)
+ || (($type_flags +& $TYPE_NATIVE_STR) && $got_prim != $BIND_VAL_STR)
+ || ($literal && nqp::atpos_i(nqp::atkey($cur_candidate, 'rwness'), $i)) {
+ # Mismatch.
+ note(" arg $i wrong native") if $*DEBUG;
+ $type_mismatch := 1;
+ $type_match_possible := 0;
+ last;
+ }
+ }
+ else {
+ my $type_obj := nqp::atpos(nqp::atkey($cur_candidate, 'types'), $i);
+
+ # Work out parameter.
+ my $param :=
+ $got_prim == $BIND_VAL_OBJ ?? nqp::atpos(@args, $i) !!
+ $got_prim == $BIND_VAL_INT ?? Int !!
+ $got_prim == $BIND_VAL_UINT ?? Int !!
+ $got_prim == $BIND_VAL_NUM ?? Num !!
+ Str;
+
+ # If we're here, it's a non-native.
+ $all_native := 0;
+
+ # A literal won't work with rw parameter.
+ my int $literal := nqp::atpos(@flags, $i) +& $ARG_IS_LITERAL;
+ if $literal && nqp::atpos_i(nqp::atkey($cur_candidate, 'rwness'), $i) {
+ note(" arg $i wrong rwness") if $*DEBUG;
+ $type_mismatch := 1;
+ $type_match_possible := 0;
+ last;
+ }
+ }
+ }
+ if $type_match_possible {
+ note(" type possible") if $*DEBUG;
+ $type_possible := 1;
+ }
+ if $type_mismatch {
+ note(" type mismatch") if $*DEBUG;
+ ++$cur_idx;
+ next;
+ }
+ if ($used_defcon) {
+ note(" used defcon") if $*DEBUG;
+ nqp::push(@possible, nqp::atkey($cur_candidate, 'sub'));
+ ++$cur_idx;
+ next;
+ }
+
+ # If it's possible but needs a bind check, we're not going to be
+ # able to decide it.
+ if nqp::existskey($cur_candidate, 'bind_check') {
+ note(" used bind_check") if $*DEBUG;
+ nqp::push(@possible, nqp::atkey($cur_candidate, 'sub'));
+ ++$cur_idx;
+ next;
+ }
+
+ # If we get here, it's the result. Well, unless we already had one,
+ # in which case we're in bother 'cus we don't know how to disambiguate
+ # at compile time.
+ note(" adding") if $*DEBUG;
+ nqp::push(@possible, nqp::atkey($cur_candidate, 'sub'));
+ ++$cur_idx;
+ }
+ return @possible;
+ }));
Routine.HOW.add_method(Routine, 'set_flag', nqp::getstaticcode(sub ($self, $bit) {
my $dcself := nqp::decont($self);
nqp::bindattr_i($dcself, Routine, '$!flags',
diff --git a/src/core.c/Int.pm6 b/src/core.c/Int.pm6
index 89b6c793f..2f389d479 100644
--- a/src/core.c/Int.pm6
+++ b/src/core.c/Int.pm6
@@ -313,14 +313,20 @@ multi sub abs(int $a --> int) {
multi sub infix:<+>(Int:D $a, Int:D $b --> Int:D) { nqp::add_I($a,$b,Int) }
multi sub infix:<+>(int $a, int $b --> int) { nqp::add_i($a,$b) }
+multi sub infix:<+>(int $a, uint $b --> int) { nqp::add_i($a,$b) }
+multi sub infix:<+>(uint $a, int $b --> int) { nqp::add_i($a,$b) }
multi sub infix:<+>(uint $a, uint $b --> uint) { nqp::add_i($a,$b) }
multi sub infix:<->(Int:D $a, Int:D $b --> Int:D) { nqp::sub_I($a,$b,Int) }
multi sub infix:<->(int $a, int $b --> int) { nqp::sub_i($a,$b) }
+multi sub infix:<->(int $a, uint $b --> int) { nqp::sub_i($a,$b) }
+multi sub infix:<->(uint $a, int $b --> int) { nqp::sub_i($a,$b) }
multi sub infix:<->(uint $a, uint $b --> uint) { nqp::sub_i($a,$b) }
multi sub infix:<*>(Int:D $a, Int:D $b --> Int:D) { nqp::mul_I($a,$b,Int) }
multi sub infix:<*>(int $a, int $b --> int) { nqp::mul_i($a,$b) }
+multi sub infix:<*>(int $a, uint $b --> int) { nqp::mul_i($a,$b) }
+multi sub infix:<*>(uint $a, int $b --> int) { nqp::mul_i($a,$b) }
multi sub infix:<*>(uint $a, uint $b --> uint) { nqp::mul_i($a,$b) }
multi sub infix:<eqv>(Int:D $a, Int:D $b --> Bool:D) {
@@ -443,6 +449,9 @@ multi sub infix:«<=»(Int:D $a, Int:D $b --> Bool:D) {
multi sub infix:«<=»(int $a, int $b --> Bool:D) {
nqp::hllbool(nqp::isle_i($a,$b))
}
+multi sub infix:«<=»(uint $a, uint $b --> Bool:D) {
+ nqp::hllbool(nqp::isle_i($a,$b))
+}
multi sub infix:«>»(Int:D $a, Int:D $b --> Bool:D) {
nqp::hllbool(nqp::isgt_I($a,$b))
diff --git a/src/core.c/native_array.pm6 b/src/core.c/native_array.pm6
index 387cb798d..d59250459 100644
--- a/src/core.c/native_array.pm6
+++ b/src/core.c/native_array.pm6
@@ -4883,9 +4883,15 @@ multi sub postcircumfix:<[ ]>(array:D \SELF, Range:D \range ) is raw {
}
#- start of postcircumfix candidates of strarray -------------------------------
-#- Generated on 2022-02-03T19:03:07+01:00 by tools/build/makeNATIVE_CANDIDATES.raku
+#- Generated on 2022-02-06T14:35:26+01:00 by tools/build/makeNATIVE_CANDIDATES.raku
#- PLEASE DON'T CHANGE ANYTHING BELOW THIS LINE
+multi sub postcircumfix:<[ ]>(
+ array::strarray:D \SELF, uint $pos
+) is raw {
+ nqp::atposref_s(nqp::decont(SELF),$pos)
+}
+
multi sub postcircumfix:<[ ]>(
array::strarray:D \SELF, Int:D $pos
) is raw {
@@ -5135,9 +5141,15 @@ multi sub infix:<cmp>(array::strarray:D \a, array::strarray:D \b) {
#- end of postcircumfix candidates of strarray ---------------------------------
#- start of postcircumfix candidates of numarray -------------------------------
-#- Generated on 2022-02-03T19:03:07+01:00 by tools/build/makeNATIVE_CANDIDATES.raku
+#- Generated on 2022-02-06T14:35:26+01:00 by tools/build/makeNATIVE_CANDIDATES.raku
#- PLEASE DON'T CHANGE ANYTHING BELOW THIS LINE
+multi sub postcircumfix:<[ ]>(
+ array::numarray:D \SELF, uint $pos
+) is raw {
+ nqp::atposref_n(nqp::decont(SELF),$pos)
+}
+
multi sub postcircumfix:<[ ]>(
array::numarray:D \SELF, Int:D $pos
) is raw {
@@ -5387,9 +5399,15 @@ multi sub infix:<cmp>(array::numarray:D \a, array::numarray:D \b) {
#- end of postcircumfix candidates of numarray ---------------------------------
#- start of postcircumfix candidates of intarray -------------------------------
-#- Generated on 2022-02-03T19:03:07+01:00 by tools/build/makeNATIVE_CANDIDATES.raku
+#- Generated on 2022-02-06T14:35:26+01:00 by tools/build/makeNATIVE_CANDIDATES.raku
#- PLEASE DON'T CHANGE ANYTHING BELOW THIS LINE
+multi sub postcircumfix:<[ ]>(
+ array::intarray:D \SELF, uint $pos
+) is raw {
+ nqp::atposref_i(nqp::decont(SELF),$pos)
+}
+
multi sub postcircumfix:<[ ]>(
array::intarray:D \SELF, Int:D $pos
) is raw {
@@ -5639,9 +5657,15 @@ multi sub infix:<cmp>(array::intarray:D \a, array::intarray:D \b) {
#- end of postcircumfix candidates of intarray ---------------------------------
#- start of postcircumfix candidates of uintarray -------------------------------
-#- Generated on 2022-02-03T19:03:07+01:00 by tools/build/makeNATIVE_CANDIDATES.raku
+#- Generated on 2022-02-06T14:35:26+01:00 by tools/build/makeNATIVE_CANDIDATES.raku
#- PLEASE DON'T CHANGE ANYTHING BELOW THIS LINE
+multi sub postcircumfix:<[ ]>(
+ array::uintarray:D \SELF, uint $pos
+) is raw {
+ nqp::atposref_u(nqp::decont(SELF),$pos)
+}
+
multi sub postcircumfix:<[ ]>(
array::uintarray:D \SELF, Int:D $pos
) is raw {
diff --git a/tools/build/makeNATIVE_CANDIDATES.raku b/tools/build/makeNATIVE_CANDIDATES.raku
index f11361e58..0d2d5db9c 100755
--- a/tools/build/makeNATIVE_CANDIDATES.raku
+++ b/tools/build/makeNATIVE_CANDIDATES.raku
@@ -53,6 +53,12 @@ while @lines {
# spurt the candidates
say Q:to/SOURCE/.subst(/ '#' (\w+) '#' /, -> $/ { %mapper{$0} }, :g).chomp;
+multi sub postcircumfix:<[ ]>(
+ array::#type#array:D \SELF, uint $pos
+) is raw {
+ nqp::atposref_#postfix#(nqp::decont(SELF),$pos)
+}
+
multi sub postcircumfix:<[ ]>(
array::#type#array:D \SELF, Int:D $pos
) is raw {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment