[LLVMdev] ocaml+llvm

Gordon Henriksen gordonhenriksen at mac.com
Sun Aug 12 11:30:53 PDT 2007


I'm hacking on an llvm backend for the ocaml language.

    http://caml.inria.fr/ocaml/

I'd like to solicit some advice regarding the constant data
structures that ocaml's runtime requires. Rewriting its runtime is
a non-goal.


The biggest problem is a data structure called the frame table, a
simple structure for which LLVM seems ill-prepared. For each call
site in the program, ocaml emits an entry into this table:

     key   : the return address of the call site
     value : the stack offset of every variable live after return

The garbage collector uses this when walking the stack to find
live objects.

                      -- frame table example --
This program will create 3 call sites. 2 are not interesting, but
the other will have 1 live root:

     example.ml:
     let keeplive x = "";;

     let heapobject = "hello " ^ "world" in
       print_endline heapobject;
       keeplive heapobject

The interesting call is print_endline heapobject. After the return
of this call, the root heapobject is still live, so the collector
must trace it. To instruct the runtime to do so, the ocaml
compiler emits the following assembly:

     from the program text in example.s:
         movl    %eax, 0(%esp)  ; save heapobject at 0(%esp)
         call    _camlPervasives__print_endline_298
     L103:                      ; mark call's return address

     from the frame table in example.s:
         .long    L103  ; "entry is for return address L103"
         .word    16    ; "entry is 16 bytes in length"
         .word    1     ; "1 gcroot follows"
         .word    0     ; "find a gcroot at 0(%esp)"

                              -- end --

The major challenges posed by the frame table are:

  1. Making a constant reference to a return address.
     - Labels are not global values, so ocamlopt's approach is a
       no-go.
     - Is llvm.pcmarker in any way useful? It couldn't present the
       correct address for a caller-save calling convention.
     - A constant expression like OFFSET + (intptr_t) FUNCTION can
       get the job done, but this requires instruction size
       computations which I can only find for ARM (in support of
       the constant island pass).

  2. Computing the static stack offset of the GC roots.
     - I'm not yet sure whether this is trivial or not.

Additionally:

  3. Identifying the roots live after each call site.
     - Trivially correct: include all llvm.gcroot'd allocas in the
       function.
     - Harder: analyze load/store dominance information vs. the
       call site to eliminate those roots which are not live.

  4. Identifying all return addresses.
     - I think trivial on the MachineFunction representation, but
       is it possible to relate the machine instructions back to
       the LLVM IR, where the liveness analysis must be performed?

  5. Update the frame table constant with information only
     available during codegen.
     - I'm thinking I should populate an on-the-side data structure
       during compilation, and only convert it to a Constant* and
       emit it during an epilogue pass using
       AsmPrinter::EmitGlobalConstant.

That's the doozy. Unfortunately, I think the runtime requirements
will preclude the use of the basic LLVM toolchain in conjunction
with LLVM ocamlopt, which is unfortunate.


The simpler problem is that each ocaml object exports symbols
bracketing its code and data. An ocaml object absent these symbols
cannot be linked into an executable.

     prologue from example.s:
         .data
         .globl  _camlExample__data_begin
     _camlExample__data_begin:
         .text
         .globl  _camlExample__code_begin
     _camlExample__code_begin:
         .data
         ; data follows
         .text
         ; code follows

     epilogue from example.s:
         .text
         .globl  _camlExample__code_end
     _camlExample__code_end:
         .data
         .globl  _camlExample__data_end
     _camlExample__data_end:
         .long 0

I think I should simply write prologue- and epilogue emitter
passes for this boilerplate. That avoids attempting to model these
quirky symbols in the LLVM IR, but again breaks llc compatibility.
Is there a better way?


Thanks,
Gordon


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20070812/b763945c/attachment.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: example.ml
Type: application/octet-stream
Size: 112 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20070812/b763945c/attachment.obj>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20070812/b763945c/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: example.s
Type: application/octet-stream
Size: 1493 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20070812/b763945c/attachment-0001.obj>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20070812/b763945c/attachment-0002.html>


More information about the llvm-dev mailing list