On Thu, 2013-10-03 at 21:42 +0000, Joseph S. Myers wrote:
> On Wed, 2 Oct 2013, David Malcolm wrote:
> 
> > The idea is that GCC is configured with a special --enable-host-shared
> > option, which leads to it being built as position-independent code. You
> > would configure it with host==target, given that the generated machine
> > code will be executed within the same process (the whole point of JIT).
> 
> Configuring with a different target also makes sense - consider JIT 
> generation of code for a GPU (for example).  (Of course lots of other 
> cleanups would be needed to be able to generate code for multiple targets 
> within the same process / same copy of libgccjit.so, but the idea seems 
> useful.  There are probably tricks available involving building multiple 
> copies with different SONAMEs / symbol names.  Anyway, it may make sense 
> now to consider defining the interfaces in a way that would support 
> choosing the target for which you get a context.)

Right now all you get back from the result is a "void*" which you're
meant to cast to machine code for the CPU.   I guess we could add an
option on the gcc_jit_context for setting which ISA you want code for.
Then hopefully everything should "just work" - you'd make one context
for the CPU, and another for the GPU, and perhaps you could use the same
code-creation callback for both, and the library would handle all of the
differences between ISAs in terms of code-generation.   Maybe.  So I
*think* the structure of the API is sane for this use-case... though
this seems to me like a version 2 feature.

> >   * There are some grotesque kludges in internal-api.c, especially in
> > how we go from .s assembler files to a DSO (grep for "gross hack" ;) )
> 
> Apart from making the assembler into a shared library itself, it would 
> also be nice to avoid needing those files at all (via an API for writing 
> assembler text that doesn't depend on a FILE * pointer, although on GNU 
> hosts you can always use in-memory files via open_memstream, which would 
> make the internal changes much smaller).  But in the absence of such a 
> cleanup, running the assembler via the driver should work, although 
> inefficient.

(nods)   Note that all of the kludgery (if that's a word) is hidden
inside the library, so we can fix it all up without affecting client
code: the client-facing API doesn't expose any of this.

FWIW I added timevars for measuring the invocation of the driver; that
kludge makes up about 50% of the overall time taken.  I haven't yet
broken that down into assembler vs linker vs fork/exec overhead, but
clearly this is something that could use optimization - leading to
(very) vague thoughts involving turning parts of binutils into libraries
also.

However, given that it's hidden from client code - we can fix this
without breaking API - I'd rather treat this as optimization for a later
release, and focus instead on things like having an easy-to-use API that
can have ABI stability, getting it to build cleanly, fixing bugs, etc
etc.

> What do you do about errors and warnings (other than hope that they don't 
> get generated)?  Again, one step is to avoid anything that directly uses 
> stderr / stdout (explicitly, or implicitly through functions such as 
> printf), so allowing FILE * pointers to instead be something opened with 
> open_memstream, while a larger cleanup would narrow the interface to 
> writing such text as far as possible (to well-defined diagnostic and 
> dumping interfaces that don't directly take a FILE *, eliminating all ad 
> hoc calls to fprintf etc.).

Right now it's the "hope they don't get generated" approach, followed by
running the code under gdb when they do.

I'm thinking of an API something like this:

typedef int (*gcc_jit_diagnostic_cb) (const char *,
                                      enum gcc_jit_diagnostic_level,
                                      void *user_data);
extern void
gcc_jit_context_set_diagnostic_callback (gcc_jit_context *ctxt,
                                         gcc_jit_diagnostic_cb cb,
                                         void *user_data);

where the callback is invoked each time an error/warning occurs, and
have the default behavior to be to print to stderr (if you haven't set a
callback, or pass NULL as the cb).

That way clients can register their own handlers, but don't need to.

Perhaps the client returns a value from the callback to signify whether
they want compilation to continue (hence the int return value) so that
it can choose between bailing out immediately on the first error, or
trying to list all errors, etc, since both are valid choices.

Dave

Reply via email to