Re: discussion of thunks

chase@centerline.com (David Chase)
Fri, 3 Mar 1995 16:22:04 GMT

          From comp.compilers

Related articles
Re: Q: Intermediate code for interpreting & compiling? Dave@occl-cam.demon.co.uk (Dave Lloyd) (1995-02-24)
Re: Q: Intermediate code for interpreting & compiling? davidm@Rational.COM (1995-02-28)
Re: discussion of thunks chase@centerline.com (1995-03-03)
| List of all articles for this month |

Newsgroups: comp.compilers
From: chase@centerline.com (David Chase)
Keywords: code, optimize, design
Organization: CenterLine Software
References: 95-02-183 95-03-010
Date: Fri, 3 Mar 1995 16:22:04 GMT

> One also has to be careful about saying that one can
> implement thunks as function pointers. The thunk
> executes in the context of the calling procedure.


One excellent stunt for implementing thunks as function pointers is
to generate code on the fly. I think this is old news to Lisp
implementors, and I learned about it back in 1988. For machines on
which this might be costly (i.e., with split I/D caches), you instead
allocate your thunks from a pool of paired code and data chunks -- to
create a thunk, you write (into the data half) the address of the
bound context and the address of the function that you which to have
this context passed to. Then, you return the code half. (Read on to
the end for a third alternative.)


I implemented this (posted it to the net before, even) for Sparc, and
tested it pretty throughly on a machine with split I and D caches.
There's no reason why you could not also do this for other popular
architectures -- there's a finite number of them, and the code
required for the glue is minimal.


Here's the interface:


PFoIrI -- Pointer to function of int returning int.


PFoIrI thunk_alloc(PFoIrI f, int data1, int data2,
                                      char * name).
    Obtains a new thunk, which, when called with
    some parameters (in this implementation, any
    number) will transfer control to "f" after
    loading the values in data1 and data2 into
    global registers %g2 and %g3.
    (Obviously, the choice of registers for this
    data is machine dependent.)


void thunk_free(PFoIrI f)
    Return an allocated thunk (it checks) to the pool
    for later reuse.


int thunk_valid(PFoIrI f)
    Is this function pointer an allocated thunk?


get_g2()
get_g3()
    With the actually called routine, these may be used
    to retrieve the data associated with this thunk.


In practice, the actual interface may need to vary from architecture
to architecture -- on the 680x0, for instance, it is easier to
prepend parameters to the list than it is to pass them in "global"
registers. This is probably also true for the 80x86 architectures.


On RISC architectures, it is probably easier to abuse some unused (in
C) register for this purpose. For example, on the PowerPC (based on
a cursory reading of their ABI) it appears that R11 and perhaps R12
could be used for this purpose if you were very crafty. In fact, if
only one register is desired, then the existing PowerOpen ABI already
provides the ability to do this -- merely clone a copy of f's
function descriptor, with the value to be passed in R11 placed at
offset 8 (the "environment pointer", which is what we were talking
about in the first place). A direct (statically bound) call to a
very tiny routine should suffice to return the environment pointer to
the calling C.


MIPS and HP, I don't know about, but my assumption is that someone
who knew the gory details could figure it out.


David Chase, speaking for myself.
--


Post a followup to this message

Return to the comp.compilers page.
Search the comp.compilers archives again.