Skip to content

Commit aadea88

Browse files
committed
Add nextdispatcherfor/takenextdispatcher ops
Provide support for Raku chained dispatchers. Their purpose is to pass information to the downstream dispatcher about what upstream dispatcher must take over the control next when downstream exhausts. For example, when one wraps a candidate in a multi, the `WrapDispatcher` must know about the instance of `MultiDispatcher` to switch back to it when all wrappers are done and the candidate calls one of `{next|call}{same|with}`. Technically, both ops are clones for `setdispatcherfor`/`takedispatcher` except that they operate with `next_dispatcher` and `next_dispatcher_for` members of `ThreadContext`; and their purpose differs too. Requires MoarVM/MoarVM#1252.
1 parent 7c261f2 commit aadea88

File tree

3 files changed

+56
-2
lines changed

3 files changed

+56
-2
lines changed

src/NQP/Optimizer.nqp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,8 @@ class NQP::Optimizer {
170170
my %opt_n_i := nqp::hash('add', 1, 'sub', 1, 'mul', 1, 'mod', 1, 'neg', 1, 'abs', 1, 'iseq', 1, 'isne', 1,
171171
'islt', 1, 'isle', 1, 'isgt', 1, 'isge', 1, 'cmp', 1);
172172

173-
my %op_poisons_lowering := nqp::hash('ctx', 1, 'curlexpad', 1, 'takedispatcher', 1, 'getlexouter', 1);
173+
my %op_poisons_lowering := nqp::hash('ctx', 1, 'curlexpad', 1, 'takedispatcher', 1, 'takenextdispatcher', 1,
174+
'getlexouter', 1);
174175

175176
method visit_op($op) {
176177
# Handle op needs special handling.

src/vm/moar/QAST/QASTOperationsMAST.nqp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3018,6 +3018,7 @@ QAST::MASTOperations.add_core_moarop_mapping('setcodename', 'setcodename', 0);
30183018
QAST::MASTOperations.add_core_moarop_mapping('forceouterctx', 'forceouterctx', 0);
30193019
QAST::MASTOperations.add_core_moarop_mapping('setdispatcher', 'setdispatcher', 0);
30203020
QAST::MASTOperations.add_core_moarop_mapping('setdispatcherfor', 'setdispatcherfor', 0);
3021+
QAST::MASTOperations.add_core_moarop_mapping('nextdispatcherfor', 'nextdispatcherfor', 0);
30213022
QAST::MASTOperations.add_core_op('takedispatcher', -> $qastcomp, $op {
30223023
my $regalloc := $*REGALLOC;
30233024
unless nqp::istype($op[0], QAST::SVal) {
@@ -3038,7 +3039,28 @@ QAST::MASTOperations.add_core_op('takedispatcher', -> $qastcomp, $op {
30383039
$regalloc.release_register($isnull_reg, $MVM_reg_int64);
30393040
MAST::InstructionList.new(MAST::VOID, $MVM_reg_void)
30403041
});
3042+
QAST::MASTOperations.add_core_op('takenextdispatcher', -> $qastcomp, $op {
3043+
my $regalloc := $*REGALLOC;
3044+
unless nqp::istype($op[0], QAST::SVal) {
3045+
nqp::die("The 'takenextdispatcher' op must have a single QAST::SVal child, got " ~ $op[0].HOW.name($op[0]));
3046+
}
3047+
my @ops;
3048+
my $disp_reg := $regalloc.fresh_register($MVM_reg_obj);
3049+
my $isnull_reg := $regalloc.fresh_register($MVM_reg_int64);
3050+
my $done_lbl := MAST::Label.new();
3051+
%core_op_generators{'takenextdispatcher'}($disp_reg);
3052+
%core_op_generators{'isnull'}($isnull_reg, $disp_reg);
3053+
%core_op_generators{'if_i'}($isnull_reg, $done_lbl);
3054+
if $*BLOCK.lexical($op[0].value) -> $lex {
3055+
%core_op_generators{'bindlex'}($lex, $disp_reg);
3056+
}
3057+
$*MAST_FRAME.add-label($done_lbl);
3058+
$regalloc.release_register($disp_reg, $MVM_reg_obj);
3059+
$regalloc.release_register($isnull_reg, $MVM_reg_int64);
3060+
MAST::InstructionList.new(MAST::VOID, $MVM_reg_void)
3061+
});
30413062
QAST::MASTOperations.add_core_moarop_mapping('cleardispatcher', 'takedispatcher');
3063+
QAST::MASTOperations.add_core_moarop_mapping('clearnextdispatcher', 'takenextdispatcher');
30423064
QAST::MASTOperations.add_core_moarop_mapping('freshcoderef', 'freshcoderef');
30433065
QAST::MASTOperations.add_core_moarop_mapping('iscoderef', 'iscoderef');
30443066
QAST::MASTOperations.add_core_moarop_mapping('markcodestatic', 'markcodestatic');

t/nqp/100-dispatcher.t

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
plan(10);
1+
plan(16);
22
my $closure;
33
{
44
nqp::setdispatcher(100);
@@ -35,6 +35,18 @@ nqp::setdispatcherfor(300, &bar);
3535
foo();
3636
bar();
3737

38+
sub baz() {
39+
my $baz := 100;
40+
nqp::takenextdispatcher('$baz');
41+
is($baz, 400);
42+
$baz := 200;
43+
nqp::takenextdispatcher('$baz');
44+
is($baz, 200);
45+
}
46+
47+
nqp::nextdispatcherfor(400, &baz);
48+
baz();
49+
3850
sub wraped1() {
3951
my $foo := 100;
4052
nqp::takedispatcher('$foo');
@@ -43,8 +55,11 @@ sub wraped1() {
4355

4456
sub wraped2() {
4557
my $foo := 100;
58+
my $bar := 100;
4659
nqp::takedispatcher('$foo');
60+
nqp::takenextdispatcher('$bar');
4761
is($foo, 400);
62+
is($bar, 420);
4863
}
4964

5065
class Wrap {
@@ -56,6 +71,7 @@ my $wraped1 := Wrap.new(code_ref => &wraped1);
5671
my $wraped2 := Wrap.new(code_ref => &wraped2);
5772

5873
nqp::setdispatcherfor(400, $wraped2);
74+
nqp::nextdispatcherfor(420, $wraped2);
5975

6076
$wraped1();
6177
$wraped2();
@@ -75,3 +91,18 @@ is(take_or_clear(1), 400);
7591
nqp::setdispatcherfor(400, &take_or_clear);
7692
is(take_or_clear(0), 100);
7793
is(take_or_clear(1), 100);
94+
95+
sub take_or_clear_next($take) {
96+
my $foo := 100;
97+
if $take {
98+
nqp::takenextdispatcher('$foo');
99+
} else {
100+
nqp::clearnextdispatcher();
101+
}
102+
$foo;
103+
}
104+
nqp::nextdispatcherfor(400, &take_or_clear_next);
105+
is(take_or_clear_next(1), 400);
106+
nqp::nextdispatcherfor(400, &take_or_clear_next);
107+
is(take_or_clear_next(0), 100);
108+
is(take_or_clear_next(1), 100);

0 commit comments

Comments
 (0)