cify
Folders and files
| Name | Name | Last commit date | ||
|---|---|---|---|---|
parent directory.. | ||||
The "cify" compiler takes a linklet with no imports as produced by "schemify" and compiles it to C code suitable for including as part of the Racket virtual machine's implementation. The schemify compiler is lightly customized by a `for-cify?` flag to make it produce output friendlier for cify. In cify mode, schemify avoids obscuring structure-type creation, and it lifts all constants to the top. Compilation by cify is UNSAFE. For example: * cify assumes that `car` is applied to a pair, `vector-ref` is applied to a vector with an in-bounds argument, a struct selector is applied to an instance of the struct, and so on. * cify assumes that a variable is not referenced before its definition. * cify assumes that a function defined within the linklet is called within the linklet with the right number of arguments. The cify pass relies on the schemify pass lift functions to avoid closure allocation whenever possible. The schemify pass also performs some basic inlining, constant propagation, and copy propagation --- but it's designed to defer significantly to the back end to perform more of that. So, cify includes some copy propagation support, especially for bindings that are outside of any `lambda` form. (There's not much extra inling, so that's a direction for improvement.) When a function A tail-calls a known function B, cify generates a single "vehicle" C function to hold A and B, so the tail cal can be implemented as a `goto`. The same vehicle holds anything else that tail-calls A and B or any other function that A or B tail-calls. That strategy can generate a big function. For tail cals to unknown functions or non-immediate primitives, cify uses the Racket virtual machine's protocol for trampolined tail calls. The output of cify uses the same Racket-value stack (internally called "runstack") as the virtual machine's bytecode interpreter and JIT. Along the same lines as the bytecode compiler, cify must determine the liveness of a binding and clear the stack slot of any dead variable before a potential GC. If a value's lifetime doesn't span a GC boundary, then cify tries to keep it in a C variable (so the C compiler can allocate it to a register, etc.). Most of cify's complexity is to put variables in the right place and take advantage of global runstack invariants to minimize value shuffling.