[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [epsilon-devel] [Patch] Integrating Jitter as a sub-package in Poke:
From: |
Jose E. Marchesi |
Subject: |
Re: [epsilon-devel] [Patch] Integrating Jitter as a sub-package in Poke: first iteration |
Date: |
Mon, 23 Sep 2019 18:52:41 +0200 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/26.1 (gnu/linux) |
Hi Luca!
[Sorry for this long delay in replying, but as you know I'm travelling
for conferences, and last week has been complicated, in many ways.]
I am taking the liberty of also including some background which will be
obvious to you in this response, in the hope that others will be
interested.
On 2019-09-16 at 03:28 +0200, Jose E. Marchesi wrote:
> Hm, so now you have routines and "executable routines" as separated
> concepts. You can go from the first to the second, and there is a link
> back:
>
> routine >>> executable routine
> ^ |
> | |
> +---------------+
Right. The circular structure is for easy freeing; it can also makes
data structures such as closures simpler, by letting you only include
one pointer instead of two.
> Something doesn't feel right here... isn't being executable (and
> therefore not editable) a property of a routine? If so, why not
> exposing it as a property, instead of adding a new separated (but
> coupled, the weird "link back") abstraction?
Non-executable routines are good for code generation -- and they will
support some optimization as well; for example it is easy to eliminate
some jumps. Non-executable routines need data structures handling, for
example, labels. There are dynamically growing buffers, and hash
tables. Moreover, rewriting happens on non-executable routines: the
last few generated VM instructions may be transparently replaced by
alternatives, while the user keeps generating more. This involves a few
complications and some memory overhead.
Allright, all that are internal details.
Executable routines are purely for executing, and much simpler. In
direct-threaded code you have an array containing pointers to native
code and residual VM instruction arguments; in no-threading an
executable routine is even simpler: one contiguous block of executable
memory containing hardware instructions.
Ditto.
I also toy with more radical optimizations in my mind, such as using an
obstack style of allocation for all the data structures in
non-executable routines, to make them easy to destroy in an instant and
more cache-friendly, and to allocate the structs for executable routines
directly in mmapped memory.
I decided to separate non-executable routines from executable routines
essentially to be able to free one without the other: at execution you
do not need non-executable routines, unless you want to debug. The
printing of VM code works with non-executable routines only, while
disassembly relies on executable routines but works in a more precise
way when the companion non-executable routine exists as well: for each
VM instruction you get its name and precise boundaries, so that you see
which hardware instructions implement which VM instruction.
That can be achieved by having a "strip" operation on jitter routines.
> This way you don't need to expose two different types, and I don't need
> to mess with that weird one-directional link from "executable routine"
> to "routine" like in:
>
> - pvm_destroy_program (argv[i].val.prog);
> + {
> + pvm_routine rout = argv[i].val.erout->routine;
> + if (rout != NULL)
> + pvm_destroy_routine (rout);
> + pvm_destroy_executable_routine (argv[i].val.erout);
> + }
>
> Having both pvm_destroy_routine and pvm_destroy_executable_routine,
> why?? I want to destroy a routine, period.
I believe I addressed the point above, but I am perfectly willing to add
helper functions for your use case.
Thanks. From the client interface perspective, I really would like to
see only one kind of Jitter routine, and being able to alter their
properties using suitable functions.
So, to repeat the conversation on IRC of which you may have missed the
last past:
You want to only ever work with non-executable routines. For this you
would like:
1) a way of executing a non-executable routine;
2) a way of destroying both routines, given only the non-executable one.
This is essentially all that you require, and it can be achieved with
simple wrapper functions. Apart from the ugly name I anticipate for 2)
I see no problem.
No. What I want is to work with just _one_ kind of jitter routine,
which starts as non-executable and becomes executable at some point.
Much like the present-day specialized vs non-specialized jitter program.
Once the routine becomes executable, you cannot edit it unless you turn
it back into a non-executable routine. If an executable routine is
stripped, then it can't be made non-executable anymore.
I don't really care how this is implemented internally: with two
different data types, or three or four.
But I really think that the interface should offer a single routine
abstraction. You don't want to expose things like the circular
structure: it complicates the interface for no benefit.
I would even give you two versions of 1), one automatically making an
executable version on the fly if none is present; and another, unsafe,
avoiding the check.
Would this be a satisfactory solution?
From the point of view of the API, it should be indistinguishable from
your proposal, with the advantage of allowing more efficient solutions
as well.
> I'm not sure what you mean with "the build system code selecting a
> Jitter dispatch". Do you mean --with-debug?
Yes. The Automake conditional POKE_DEBUG used in src/Makefile.am does
what now you can do by passing options to Jitter's configure through
Poke's configure:
[poke]$ ./configure --enable-dispatch-no-threading
will just work, relaying the option to jitter/configure .
Minimal-threading and no-threading are disabled by default now.
I prefer to abstract the poke user from jitter's execution models.
Also, --enable-debug may imply more things, not just selecting a
specific jitter configuration.