Frequently it is desirable to have more control over how code is generated or how memory is used during jit generation or execution.
To aid in complete control of memory allocation and deallocation
GNU lightning provides wrappers that default to standard malloc
,
realloc
and free
. These are loosely based on the
GNU GMP counterparts, with the difference that they use the same
prototype of the system allocation functions, that is, no size
for free
or old_size
for realloc
.
void
jit_set_memory_functions (
void *(*alloc_func_ptr) (size_t),
void *(*realloc_func_ptr) (void *, size_t),
void (*free_func_ptr) (void *))
¶GNU lightning guarantees that memory is only allocated or released using these wrapped functions, but you must note that if lightning was linked to GNU binutils, malloc is probably will be called multiple times from there when initializing the disassembler.
Because init_jit
may call memory functions, if you need to call
jit_set_memory_functions
, it must be called before init_jit
,
otherwise, when calling finish_jit
, a pointer allocated with the
previous or default wrappers will be passed.
void
jit_get_memory_functions (
void *(**alloc_func_ptr) (size_t),
void *(**realloc_func_ptr) (void *, size_t),
void (**free_func_ptr) (void *))
¶Get the current memory allocation function. Also, unlike the GNU GMP
counterpart, it is an error to pass NULL
pointers as arguments.
Unless an alternate code buffer is used (see below), jit_emit
set the access protections that the code buffer’s memory can be read and
executed, but not modified. One can use the following functions after
jit_emit
but before jit_clear
to temporarily lift the
protection:
void
jit_unprotect ()
¶Changes the access protection that the code buffer’s memory can be read and
modified. Before the emitted code can be invoked, jit_protect
has to be called to reset the change.
This procedure has no effect when an alternate code buffer (see below) is used.
void
jit_protect ()
¶Changes the access protection that the code buffer’s memory can be read and executed.
This procedure has no effect when an alternate code buffer (see below) is used.
To instruct GNU lightning to use an alternate code buffer it is required
to call jit_realize
before jit_emit
, and then query states
and customize as appropriate.
void
jit_realize ()
¶Must be called once, before jit_emit
, to instruct GNU lightning
that no other jit_xyz
call will be made.
jit_pointer_t
jit_get_code (jit_word_t *code_size)
¶Returns NULL or the previous value set with jit_set_code
, and
sets the code_size argument to an appropriate value.
If jit_get_code
is called before jit_emit
, the
code_size argument is set to the expected amount of bytes
required to generate code.
If jit_get_code
is called after jit_emit
, the
code_size argument is set to the exact amount of bytes used
by the code.
void
jit_set_code (jit_ponter_t code, jit_word_t size)
¶Instructs GNU lightning to output to the code argument and
use size as a guard to not write to invalid memory. If during
jit_emit
GNU lightning finds out that the code would not fit
in size bytes, it halts code emit and returns NULL
.
A simple example of a loop using an alternate buffer is:
jit_uint8_t *code; int *(func)(int); /* function pointer */ jit_word_t code_size; jit_word_t real_code_size; ... jit_realize(); /* ready to generate code */ jit_get_code(&code_size); /* get expected code size */ code_size = (code_size + 4095) & -4096; do (;;) { code = mmap(NULL, code_size, PROT_EXEC | PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); jit_set_code(code, code_size); if ((func = jit_emit()) == NULL) { munmap(code, code_size); code_size += 4096; } } while (func == NULL); jit_get_code(&real_code_size); /* query exact size of the code */
The first call to jit_get_code
should return NULL
and set
the code_size
argument to the expected amount of bytes required
to emit code.
The second call to jit_get_code
is after a successful call to
jit_emit
, and will return the value previously set with
jit_set_code
and set the real_code_size
argument to the
exact amount of bytes used to emit the code.
Sometimes it may be desirable to customize how, or to prevent GNU lightning from using an extra buffer for constants or debug annotation. Usually when also using an alternate code buffer.
jit_pointer_t
jit_get_data (jit_word_t *data_size, jit_word_t *note_size)
¶Returns NULL
or the previous value set with jit_set_data
,
and sets the data_size argument to how many bytes are required
for the constants data buffer, and note_size to how many bytes
are required to store the debug note information.
Note that it always preallocate one debug note entry even if
jit_name
or jit_note
are never called, but will return
zero in the data_size argument if no constant is required;
constants are only used for the float
and double
operations
that have an immediate argument, and not in all GNU lightning ports.
void
jit_set_data (jit_pointer_t data, jit_word_t size, jit_word_t flags)
¶data can be NULL if disabling constants and annotations, otherwise,
a valid pointer must be passed. An assertion is done that the data will
fit in size bytes (but that is a noop if GNU lightning was built
with -DNDEBUG
).
size tells the space in bytes available in data.
flags can be zero to tell to just use the alternate data buffer,
or a composition of JIT_DISABLE_DATA
and JIT_DISABLE_NOTE
JIT_DISABLE_DATA
¶Instructs GNU lightning to not use a constant table, but to use an alternate method to synthesize those, usually with a larger code sequence using stack space to transfer the value from a GPR to a FPR register.
JIT_DISABLE_NOTE
¶Instructs GNU lightning to not store file or function name, and line numbers in the constant buffer.
A simple example of a preventing usage of a data buffer is:
... jit_realize(); /* ready to generate code */ jit_get_data(NULL, NULL); jit_set_data(NULL, 0, JIT_DISABLE_DATA | JIT_DISABLE_NOTE); ...
Or to only use a data buffer, if required:
jit_uint8_t *data; jit_word_t data_size; ... jit_realize(); /* ready to generate code */ jit_get_data(&data_size, NULL); if (data_size) data = malloc(data_size); else data = NULL; jit_set_data(data, data_size, JIT_DISABLE_NOTE); ... if (data) free(data); ...