For the past couple of weeks, chromatic and I have been exploring
various mechanisms to create a single ExceptionHandler instance
for a Sub that can be shared across multiple invocations of that Sub.
Looks like that approach is not going to work at all -- at least
not with the current ExceptionHandler implementation. The long
demonstration of this is below, but the short answer is that
ExceptionHandler PMCs are actually Continuations, and thus
the instance of the ExceptionHandler PMC is tied to the
context in which it was created, and when invoked returns to
that context.
To demonstrate this, I'll use the following NQP code:
$ cat fib-nqp-return.nqp
sub fib($n) {
return ($n < 2) ?? $n !! fib($n-1) + fib($n-2);
}
my @ARGS := pir::getinterp__P()[2];
my $N := @ARGS[1] || 28;
Q:PIR { time $N0 };
my $r := fib($N);
Q:PIR {
time $N1
$N1 -= $N0
};
pir::say("fib($N) = $r " ~ Q:PIR { %r = box $N1 });
Converting the above to PIR and running it works fine, albeit
a bit slowly (the source files used for this are attached to this
message):
$ ./parrot-nqp --target=pir fib-nqp-return.nqp >fib-nqp-return-1.pir
$ ./parrot fib-nqp-return-1.pir
fib(28) = 317811 20.2946200370789
The code that creates the ExceptionHandler for 'fib' is at the
beginning of 'fib' (fib-nqp-return-1.pir:78):
.sub "fib" :subid("11_1261172486.60629") :outer("10_1261172486.60629")
.param pmc param_16
.annotate "line", 1
new $P15, 'ExceptionHandler'
set_addr $P15, control_14
$P15."handle_types"(58)
push_eh $P15
...
We can manually edit this so that we get a single ExceptionHandler
object that is re-used on subsequent invocations -- i.e., the
optimization that chromatic and I have been working towards:
.sub "fib" :subid("11_1261172486.60629") :outer("10_1261172486.60629")
.param pmc param_16
.annotate "line", 1
$P15 = get_global 'fib_eh'
unless null $P15 goto have_fib_eh
new $P15, 'ExceptionHandler'
set_addr $P15, control_14
$P15."handle_types"(58)
set_global 'fib_eh', $P15
have_fib_eh:
push_eh $P15
...
Unfortunately, that change causes the code to no longer
work at all:
$ cp fib-nqp-return-1.pir fib-nqp-return-2.pir
$ vi fib-nqp-return-2.pir # make changes here
$ ./parrot fib-nqp-return-2.pir
fib(28) = 1 0.000290870666503906
I'm guessing that when the return exception is thrown, the ExceptionHandler
is invoked and returns to the context in which it was created (skipping
all of the intermediate contexts that attempted to (re)use the
ExceptionHandler object). Thus we recurse 27 times, throw a
return exception with a value of 1, that jumps back to the
first invocation of fib(), and 1 gets returned as our result.
I can envision a few ways in which we might be able to
create shared ExceptionHandler PMCs, but all of them feel
like fairly deep changes to the entire exception (and perhaps
continuation) subsystems, so for now I'm just posting this
analysis and will let other Parrot designers decide where to
take things from here.
Pm
sub fib($n) {
return ($n < 2) ?? $n !! fib($n-1) + fib($n-2);
}
my @ARGS := pir::getinterp__P()[2];
my $N := @ARGS[1] || 28;
Q:PIR { time $N0 };
my $r := fib($N);
Q:PIR {
time $N1
$N1 -= $N0
};
pir::say("fib($N) = $r " ~ Q:PIR { %r = box $N1 });
.namespace []
.sub "_block11" :anon :subid("10_1261172486.60629")
.annotate "line", 0
.const 'Sub' $P13 = "11_1261172486.60629"
capture_lex $P13
.annotate "line", 1
.const 'Sub' $P13 = "11_1261172486.60629"
capture_lex $P13
.lex "fib", $P13
.annotate "line", 5
new $P32, "ResizablePMCArray"
.lex "@ARGS", $P32
.annotate "line", 6
new $P33, "Undef"
.lex "$N", $P33
.annotate "line", 9
new $P34, "Undef"
.lex "$r", $P34
.annotate "line", 1
find_lex $P35, "fib"
.annotate "line", 5
getinterp $P36
set $P37, $P36[2]
unless_null $P37, vivify_12
new $P37, "Undef"
vivify_12:
store_lex "@ARGS", $P37
.annotate "line", 6
find_lex $P40, "@ARGS"
unless_null $P40, vivify_13
new $P40, "ResizablePMCArray"
vivify_13:
set $P41, $P40[1]
unless_null $P41, vivify_14
new $P41, "Undef"
vivify_14:
unless $P41, unless_39
set $P38, $P41
goto unless_39_end
unless_39:
new $P42, "Integer"
assign $P42, 28
set $P38, $P42
unless_39_end:
store_lex "$N", $P38
.annotate "line", 8
time $N0
.annotate "line", 9
find_lex $P43, "$N"
$P44 = "fib"($P43)
store_lex "$r", $P44
.annotate "line", 10
time $N1
$N1 -= $N0
.annotate "line", 14
new $P45, 'String'
set $P45, "fib("
find_lex $P46, "$N"
concat $P47, $P45, $P46
concat $P48, $P47, ") = "
find_lex $P49, "$r"
concat $P50, $P48, $P49
concat $P51, $P50, " "
$P52 = box $N1
concat $P53, $P51, $P52
say $P53
.annotate "line", 1
.return ()
.end
.namespace []
.sub "fib" :subid("11_1261172486.60629") :outer("10_1261172486.60629")
.param pmc param_16
.annotate "line", 1
new $P15, 'ExceptionHandler'
set_addr $P15, control_14
$P15."handle_types"(58)
push_eh $P15
.lex "$n", param_16
.annotate "line", 2
new $P17, "Exception"
set $P17['type'], 58
find_lex $P20, "$n"
set $N21, $P20
islt $I22, $N21, 2.0
if $I22, if_19
find_lex $P24, "$n"
sub $P25, $P24, 1
$P26 = "fib"($P25)
find_lex $P27, "$n"
sub $P28, $P27, 2
$N29 = "fib"($P28)
add $P30, $P26, $N29
set $P18, $P30
goto if_19_end
if_19:
find_lex $P23, "$n"
set $P18, $P23
if_19_end:
setattribute $P17, 'payload', $P18
throw $P17
.annotate "line", 1
.return ()
control_14:
.local pmc exception
.get_results (exception)
getattribute $P31, exception, "payload"
.return ($P31)
.end
.namespace []
.sub "_block11" :anon :subid("10_1261172486.60629")
.annotate "line", 0
.const 'Sub' $P13 = "11_1261172486.60629"
capture_lex $P13
.annotate "line", 1
.const 'Sub' $P13 = "11_1261172486.60629"
capture_lex $P13
.lex "fib", $P13
.annotate "line", 5
new $P32, "ResizablePMCArray"
.lex "@ARGS", $P32
.annotate "line", 6
new $P33, "Undef"
.lex "$N", $P33
.annotate "line", 9
new $P34, "Undef"
.lex "$r", $P34
.annotate "line", 1
find_lex $P35, "fib"
.annotate "line", 5
getinterp $P36
set $P37, $P36[2]
unless_null $P37, vivify_12
new $P37, "Undef"
vivify_12:
store_lex "@ARGS", $P37
.annotate "line", 6
find_lex $P40, "@ARGS"
unless_null $P40, vivify_13
new $P40, "ResizablePMCArray"
vivify_13:
set $P41, $P40[1]
unless_null $P41, vivify_14
new $P41, "Undef"
vivify_14:
unless $P41, unless_39
set $P38, $P41
goto unless_39_end
unless_39:
new $P42, "Integer"
assign $P42, 28
set $P38, $P42
unless_39_end:
store_lex "$N", $P38
.annotate "line", 8
time $N0
.annotate "line", 9
find_lex $P43, "$N"
$P44 = "fib"($P43)
store_lex "$r", $P44
.annotate "line", 10
time $N1
$N1 -= $N0
.annotate "line", 14
new $P45, 'String'
set $P45, "fib("
find_lex $P46, "$N"
concat $P47, $P45, $P46
concat $P48, $P47, ") = "
find_lex $P49, "$r"
concat $P50, $P48, $P49
concat $P51, $P50, " "
$P52 = box $N1
concat $P53, $P51, $P52
say $P53
.annotate "line", 1
.return ()
.end
.namespace []
.sub "fib" :subid("11_1261172486.60629") :outer("10_1261172486.60629")
.param pmc param_16
.annotate "line", 1
$P15 = get_global 'fib_eh'
unless null $P15 goto have_fib_eh
new $P15, 'ExceptionHandler'
set_addr $P15, control_14
$P15."handle_types"(58)
set_global 'fib_eh', $P15
have_fib_eh:
push_eh $P15
.lex "$n", param_16
.annotate "line", 2
new $P17, "Exception"
set $P17['type'], 58
find_lex $P20, "$n"
set $N21, $P20
islt $I22, $N21, 2.0
if $I22, if_19
find_lex $P24, "$n"
sub $P25, $P24, 1
$P26 = "fib"($P25)
find_lex $P27, "$n"
sub $P28, $P27, 2
$N29 = "fib"($P28)
add $P30, $P26, $N29
set $P18, $P30
goto if_19_end
if_19:
find_lex $P23, "$n"
set $P18, $P23
if_19_end:
setattribute $P17, 'payload', $P18
throw $P17
.annotate "line", 1
.return ()
control_14:
.local pmc exception
.get_results (exception)
getattribute $P31, exception, "payload"
.return ($P31)
.end
_______________________________________________
http://lists.parrot.org/mailman/listinfo/parrot-dev