Version française
Home     About     Download     Resources     Contact us    
Browse thread
[Caml-list] Freeing dynamically loaded code
[ Home ] [ Index: by date | by threads ]
[ Search: ]

[ Message by date: previous | next ] [ Message in thread: previous | next ] [ Thread: previous | next ]
Date: -- (:)
From: Nuutti Kotivuori <naked+caml@n...>
Subject: Re: [Caml-list] Freeing dynamically loaded code
Alain Frisch wrote:
> I propose to avoid creating "bad" closures that points to the loaded
> code.  We simulate a bad closure by a closure to a generic stub. In
> addition to the normal environment, we put two additional slots into
> this closure: a pointer to the code block and an offset. This
> closure is fully under the control of the GC. The stub is made of a
> single instruction (or maybe two, see next paragraph), say a new
> bytecode CALL_DYN, which computes the real destination from the
> closure and jumps to it. We also need a new CLOSURE_DYN bytecode
> that behaves as CLOSURE but create a faked closure instead of a bad
> one. Dynlink changes CLOSURE to CLOSURE_DYN when it loads a
> module. We need to take care of GRAB, RESTART and CLOSUREREC
> similarly.

Okay, now I finally think I know what you mean. I'll try to spell it
out in a verbose manner here.

So, if a normal closure would be something like this:

                +-----+--------+-----------------+
  static alloc  | ... | <code> | ...             |
                +-----+--------+-----------------+
                          ^
                      ,---'
                      |                       
  +-------------+----------+----------+----------+
  | Closure_tag | Code_val | Field(1) | Field(2) |
  +-------------+----------+----------+----------+

This would be transformed to:

  +-------------+-----+--------+-----------------+  +--------------+
  | String_tag  | ... | <code> | ...             |  | CALLDYN...   |
  +-------------+-----+--------+-----------------+  +--------------+
        ^`-------+-------'                                 ^
        |        |                                         |
        |        `-----------------------------------------|-----.
        |             ,------------------------------------'     |
        `-------------|-------------------------------.       <offset>
                      |                               |          |
  +-------------+----------+----------+----------+----------+----------+
  | Closure_tag | Code_val | Field(1) | Field(2) | Field(3) | Field(4) |
  +-------------+----------+----------+----------+----------+----------+

And this CALLDYN instruction would do something like:

  pc = Field(env, Wosize_val(env) - 2) +
       Field(env, Wosize_val(env) - 1);
  /* env already points correctly */
  goto check_stacks;

Am I on the right track?

Now I have no idea what GRAB and RESTART do, and what would be need to
fix them. They look scary. On CLOSUREREC I have an idea what it's
supposed to do, but I have no idea how the Infix headers really work
there - and what we'd need to do to fix that.

Now, to get the file to produce these kind of wacky closures, we would
need to do a few things. First of all we'd obviously need the CALLDYN
parts allocated somewhere statically. Then, before reifying the
bytecode, we'd wish to substitute all calls to CLOSURE with
CLOSUREDYN, right? And GRABDYN, RESTARTDYN, CLOSURERECDYN if
necessary. This would mean that we would have to go through the
bytecode, pretty much like dumpobj.ml does - so we know how many
parameters each instruction takes so we can find the correct
instructions to modify. If we don't have to touch the parameters on
anything, we don't need to recalculate any jump offsets, which is
good. One thing which I am pondering though when thinking about
CLOSUREDYN calls. All the information it has is the offset of the code
to be referenced from the current position. How do we pass the
knowledge where the start of the block is where this code resides, so
it can place that pointer in the env and calculate the offset there as
well? And it can't be a global variable temporarily or anything since
every CLOSUREDYN instruction needs to create references to the
codeblock that CLOSUREDYN instruction resides in. Well, I hope this
can be solved with some hackery.

Now the open questions that I am thinking about are that is anything
bothered by having two more variables in it's env than it is expecting
to have? Does anyone look at the size of the env block? Can we
implement all of CLOSUREDYN, CLOSURERECDYN, GRABDYN and RESTARTDYN so
that they produce compatible results? What about OFFSETCLOSURE and
friends, did they do anything evil?

Though sometimes I wonder how nice it could be if *all* code would
have this calling convention. No more hacking with these special
instructions, APPLY and friends would already do it. Though the cost
would be one more word in size for closures everywhere.

-- Naked

-------------------
To unsubscribe, mail caml-list-request@inria.fr Archives: http://caml.inria.fr
Bug reports: http://caml.inria.fr/bin/caml-bugs FAQ: http://caml.inria.fr/FAQ/
Beginner's list: http://groups.yahoo.com/group/ocaml_beginners