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
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 . . .
[examples omitted]
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.
Yes. The problem is that "fib" is recursive, so must have multiple
active invocations, each of which needs a separate active handler.
FWIW, for non-recursive functions, this approach ought to work if the
set_addr were done unconditionally:
$P15 = get_global 'fib_eh'
unless null $P15 goto have_fib_eh
new $P15, 'ExceptionHandler'
$P15."handle_types"(58)
set_global 'fib_eh', $P15
have_fib_eh:
set_addr $P15, control_14
push_eh $P15
However, this code returns the same answer when it ought to get stuck in
an unbounded loop, always returning to the *last* invocation. I am
guessing that this is because set_addr has changed since I last looked;
I can't see where the return context is established.
But in any case, to apply this optimization, the compiler would have
to prove that the sub could never have multiple simultaneously active
invocations. This would be difficult at best for a dynamic language,
and a complete nonstarter in the presence of coroutines.
Sorry for not delivering the bad news sooner, but this line of
reasoning didn't occur to me until I read your analysis.
-- Bob Rogers
http://www.rgrjr.com/
_______________________________________________
http://lists.parrot.org/mailman/listinfo/parrot-dev