Creating and using functions — libgccjit 16.0.0 (experimental ) documentation (original) (raw)
Params¶
type gcc_jit_param¶
A gcc_jit_param represents a parameter to a function.
gcc_jit_param *gcc_jit_context_new_param(gcc_jit_context *ctxt, gcc_jit_location *loc, gcc_jit_type *type, const char *name)¶
In preparation for creating a function, create a new parameter of the given type and name.
The parameter type
must be non-void.
The parameter name
must be non-NULL. The call takes a copy of the underlying string, so it is valid to pass in a pointer to an on-stack buffer.
Parameters are lvalues, and thus are also rvalues (and objects), so the following upcasts are available:
gcc_jit_lvalue *gcc_jit_param_as_lvalue(gcc_jit_param *param)¶
Upcasting from param to lvalue.
gcc_jit_rvalue *gcc_jit_param_as_rvalue(gcc_jit_param *param)¶
Upcasting from param to rvalue.
gcc_jit_object *gcc_jit_param_as_object(gcc_jit_param *param)¶
Upcasting from param to object.
Functions¶
type gcc_jit_function¶
A gcc_jit_function represents a function - either one that we’re creating ourselves, or one that we’re referencing.
gcc_jit_function *gcc_jit_context_new_function(gcc_jit_context *ctxt, gcc_jit_location *loc, enum gcc_jit_function_kind kind, gcc_jit_type *return_type, const char *name, int num_params, gcc_jit_param **params, int is_variadic)¶
Create a gcc_jit_function with the given name and parameters.
enum gcc_jit_function_kind¶
This enum controls the kind of function created, and has the following values:
GCC_JIT_FUNCTION_EXPORTED¶
Function is defined by the client code and visible by name outside of the JIT.
This value is required if you want to extract machine code for this function from a gcc_jit_result viagcc_jit_result_get_code().
GCC_JIT_FUNCTION_INTERNAL¶
Function is defined by the client code, but is invisible outside of the JIT. Analogous to a “static” function.
GCC_JIT_FUNCTION_IMPORTED¶
Function is not defined by the client code; we’re merely referring to it. Analogous to using an “extern” function from a header file.
GCC_JIT_FUNCTION_ALWAYS_INLINE¶
Function is only ever inlined into other functions, and is invisible outside of the JIT.
Analogous to prefixing with
inline
and adding__attribute__((always_inline))
Inlining will only occur when the optimization level is above 0; when optimization is off, this is essentially the same as GCC_JIT_FUNCTION_INTERNAL.
The parameter name
must be non-NULL. The call takes a copy of the underlying string, so it is valid to pass in a pointer to an on-stack buffer.
gcc_jit_function *gcc_jit_context_get_builtin_function(gcc_jit_context *ctxt, const char *name)¶
Get the gcc_jit_function for the built-in function with the given name. For example:
gcc_jit_function *fn = gcc_jit_context_get_builtin_function (ctxt, "__builtin_memcpy");
Note
Due to technical limitations with how libgccjit interacts with the insides of GCC, not all built-in functions are supported. More precisely, not all types are supported for parameters of built-in functions from libgccjit. Attempts to get a built-in function that uses such a parameter will lead to an error being emitted within the context.
gcc_jit_function *gcc_jit_context_get_target_builtin_function(gcc_jit_context *ctxt, const char *name)¶
Get the gcc_jit_function for the built-in function (sometimes called intrinsic functions) with the given name. For example:
gcc_jit_function *fn = gcc_jit_context_get_target_builtin_function (ctxt, "__builtin_ia32_pmuldq512_mask");
Note
Due to technical limitations with how libgccjit interacts with the insides of GCC, not all built-in functions are supported. More precisely, not all types are supported for parameters of built-in functions from libgccjit. Attempts to get a built-in function that uses such a parameter will lead to an error being emitted within the context.
gcc_jit_object *gcc_jit_function_as_object(gcc_jit_function *func)¶
Upcasting from function to object.
gcc_jit_param *gcc_jit_function_get_param(gcc_jit_function *func, int index)¶
Get the param of the given index (0-based).
void gcc_jit_function_dump_to_dot(gcc_jit_function *func, const char *path)¶
Emit the function in graphviz format to the given path.
gcc_jit_lvalue *gcc_jit_function_new_local(gcc_jit_function *func, gcc_jit_location *loc, gcc_jit_type *type, const char *name)¶
Create a new local variable within the function, of the given type and name.
The parameter type
must be non-void.
The parameter name
must be non-NULL. The call takes a copy of the underlying string, so it is valid to pass in a pointer to an on-stack buffer.
gcc_jit_lvalue *gcc_jit_function_new_temp(gcc_jit_function *func, gcc_jit_location *loc, gcc_jit_type *type)¶
Create a new local variable within the function, of the given type. This function is similar to gcc_jit_function_new_local(), but it is to be used for compiler-generated variables (as opposed to user-defined variables in the language to be compiled) and these variables won’t show up in the debug info.
The parameter type
must be non-void.
This entrypoint was added in LIBGCCJIT_ABI_33; you can test for its presence using
#ifdef LIBGCCJIT_HAVE_gcc_jit_function_new_temp
size_t gcc_jit_function_get_param_count(gcc_jit_function *func)¶
Get the number of parameters of the function.
gcc_jit_type *gcc_jit_function_get_return_type(gcc_jit_function *func)¶
Get the return type of the function.
The API entrypoints relating to getting info about parameters and return types:
were added in LIBGCCJIT_ABI_16; you can test for their presence using
#ifdef LIBGCCJIT_HAVE_REFLECTION
type gcc_jit_case¶
void gcc_jit_function_add_attribute (gcc_jit_function *func,
enum gcc_jit_fn_attribute attribute)
Add an attribute
attribute
to a functionfunc
.This is equivalent to the following code:
attribute((always_inline))
This entrypoint was added in :ref:LIBGCCJIT_ABI_26
; you can test for
its presence using
.. code-block:: c
#ifdef LIBGCCJIT_HAVE_ATTRIBUTES
void gcc_jit_function_add_string_attribute (gcc_jit_function *func,
enum gcc_jit_fn_attribute attribute,
const char *value)
Add a string attribute
attribute
with valuevalue
to a functionfunc
.This is equivalent to the following code:
attribute ((alias ("xxx")))
This entrypoint was added in :ref:LIBGCCJIT_ABI_26
; you can test for
its presence using
.. code-block:: c
#ifdef LIBGCCJIT_HAVE_ATTRIBUTES
void gcc_jit_function_add_integer_array_attribute (gcc_jit_function *func,
enum gcc_jit_fn_attribute attribute,
const int *value,
size_t length)
Add an attribute
attribute
withlength
integer valuesvalue
to a functionfunc
. The integer values must be the same as you would write them in a C code.This is equivalent to the following code:
attribute ((nonnull (1, 2)))
This entrypoint was added in :ref:LIBGCCJIT_ABI_26
; you can test for
its presence using
.. code-block:: c
#ifdef LIBGCCJIT_HAVE_ATTRIBUTES
Blocks¶
type gcc_jit_block¶
A gcc_jit_block represents a basic block within a function i.e. a sequence of statements with a single entry point and a single exit point.
The first basic block that you create within a function will be the entrypoint.
Each basic block that you create within a function must be terminated, either with a conditional, a jump, a return, or a switch.
It’s legal to have multiple basic blocks that return within one function.
gcc_jit_block *gcc_jit_function_new_block(gcc_jit_function *func, const char *name)¶
Create a basic block of the given name. The name may be NULL, but providing meaningful names is often helpful when debugging: it may show up in dumps of the internal representation, and in error messages. It is copied, so the input buffer does not need to outlive the call; you can pass in a pointer to an on-stack buffer, e.g.:
for (pc = 0; pc < fn->fn_num_ops; pc++) { char buf[16]; sprintf (buf, "instr%i", pc); state.op_blocks[pc] = gcc_jit_function_new_block (state.fn, buf); }
gcc_jit_object *gcc_jit_block_as_object(gcc_jit_block *block)¶
Upcast from block to object.
gcc_jit_function *gcc_jit_block_get_function(gcc_jit_block *block)¶
Which function is this block within?
Statements¶
void gcc_jit_block_add_eval(gcc_jit_block *block, gcc_jit_location *loc, gcc_jit_rvalue *rvalue)¶
Add evaluation of an rvalue, discarding the result (e.g. a function call that “returns” void).
This is equivalent to this C code:
void gcc_jit_block_add_assignment(gcc_jit_block *block, gcc_jit_location *loc, gcc_jit_lvalue *lvalue, gcc_jit_rvalue *rvalue)¶
Add evaluation of an rvalue, assigning the result to the given lvalue.
This is roughly equivalent to this C code:
void gcc_jit_block_add_assignment_op(gcc_jit_block *block, gcc_jit_location *loc, gcc_jit_lvalue *lvalue, enum gcc_jit_binary_op op, gcc_jit_rvalue *rvalue)¶
Add evaluation of an rvalue, using the result to modify an lvalue.
This is analogous to “+=” and friends:
lvalue += rvalue; lvalue *= rvalue; lvalue /= rvalue;
etc. For example:
/* "i++" */ gcc_jit_block_add_assignment_op ( loop_body, NULL, i, GCC_JIT_BINARY_OP_PLUS, gcc_jit_context_one (ctxt, int_type));
Add a no-op textual comment to the internal representation of the code. It will be optimized away, but will be visible in the dumps seen via GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE
and GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE
, and thus may be of use when debugging how your project’s internal representation gets converted to the libgccjit IR.
The parameter text
must be non-NULL. It is copied, so the input buffer does not need to outlive the call. For example:
char buf[100]; snprintf (buf, sizeof (buf), "op%i: %s", pc, opcode_names[op->op_opcode]); gcc_jit_block_add_comment (block, loc, buf);
void gcc_jit_block_end_with_conditional(gcc_jit_block *block, gcc_jit_location *loc, gcc_jit_rvalue *boolval, gcc_jit_block *on_true, gcc_jit_block *on_false)¶
Terminate a block by adding evaluation of an rvalue, branching on the result to the appropriate successor block.
This is roughly equivalent to this C code:
if (boolval) goto on_true; else goto on_false;
block, boolval, on_true, and on_false must be non-NULL.
void gcc_jit_block_end_with_jump(gcc_jit_block *block, gcc_jit_location *loc, gcc_jit_block *target)¶
Terminate a block by adding a jump to the given target block.
This is roughly equivalent to this C code:
void gcc_jit_block_end_with_return(gcc_jit_block *block, gcc_jit_location *loc, gcc_jit_rvalue *rvalue)¶
Terminate a block by adding evaluation of an rvalue, returning the value.
This is roughly equivalent to this C code:
void gcc_jit_block_end_with_void_return(gcc_jit_block *block, gcc_jit_location *loc)¶
Terminate a block by adding a valueless return, for use within a function with “void” return type.
This is equivalent to this C code:
void gcc_jit_block_end_with_switch(gcc_jit_block *block, gcc_jit_location *loc, gcc_jit_rvalue *expr, gcc_jit_block *default_block, int num_cases, gcc_jit_case **cases)¶
Terminate a block by adding evalation of an rvalue, then performing a multiway branch.
This is roughly equivalent to this C code:
switch (expr) { default: goto default_block;
case C0.min_value ... C0.max_value: goto C0.dest_block;
case C1.min_value ... C1.max_value: goto C1.dest_block;
...etc...
case C[N - 1].min_value ... C[N - 1].max_value: goto C[N - 1].dest_block; }
block
, expr
, default_block
and cases
must all be non-NULL.
expr
must be of the same integer type as all of the min_value
and max_value
within the cases.
num_cases
must be >= 0.
The ranges of the cases must not overlap (or have duplicate values).
The API entrypoints relating to switch statements and cases:
were added in LIBGCCJIT_ABI_3; you can test for their presence using
#ifdef LIBGCCJIT_HAVE_SWITCH_STATEMENTS
type gcc_jit_case¶
A gcc_jit_case represents a case within a switch statement, and is created within a particular gcc_jit_context usinggcc_jit_context_new_case().
Each case expresses a multivalued range of integer values. You can express single-valued cases by passing in the same value for both min_value and max_value.
gcc_jit_case *gcc_jit_context_new_case(gcc_jit_context *ctxt, gcc_jit_rvalue *min_value, gcc_jit_rvalue *max_value, gcc_jit_block *dest_block)¶
Create a new gcc_jit_case instance for use in a switch statement.min_value and max_value must be constants of an integer type, which must match that of the expression of the switch statement.
dest_block must be within the same function as the switch statement.
gcc_jit_object *gcc_jit_case_as_object(gcc_jit_case *case_)¶
Upcast from a case to an object.
Here’s an example of creating a switch statement:
void create_code (gcc_jit_context *ctxt, void user_data) { / Let's try to inject the equivalent of: int test_switch (int x) { switch (x) { case 0 ... 5: return 3;
case 25 ... 27: return 4;
case -42 ... -17: return 83;
case 40: return 8;
default: return 10; } } */ gcc_jit_type *t_int = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); gcc_jit_type *return_type = t_int; gcc_jit_param *x = gcc_jit_context_new_param (ctxt, NULL, t_int, "x"); gcc_jit_param *params[1] = {x}; gcc_jit_function *func = gcc_jit_context_new_function (ctxt, NULL, GCC_JIT_FUNCTION_EXPORTED, return_type, "test_switch", 1, params, 0);
gcc_jit_block *b_initial = gcc_jit_function_new_block (func, "initial");
gcc_jit_block *b_default = gcc_jit_function_new_block (func, "default"); gcc_jit_block *b_case_0_5 = gcc_jit_function_new_block (func, "case_0_5"); gcc_jit_block *b_case_25_27 = gcc_jit_function_new_block (func, "case_25_27"); gcc_jit_block *b_case_m42_m17 = gcc_jit_function_new_block (func, "case_m42_m17"); gcc_jit_block *b_case_40 = gcc_jit_function_new_block (func, "case_40");
gcc_jit_case *cases[4] = { gcc_jit_context_new_case ( ctxt, gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 0), gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 5), b_case_0_5), gcc_jit_context_new_case ( ctxt, gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 25), gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 27), b_case_25_27), gcc_jit_context_new_case ( ctxt, gcc_jit_context_new_rvalue_from_int (ctxt, t_int, -42), gcc_jit_context_new_rvalue_from_int (ctxt, t_int, -17), b_case_m42_m17), gcc_jit_context_new_case ( ctxt, gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 40), gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 40), b_case_40) }; gcc_jit_block_end_with_switch ( b_initial, NULL, gcc_jit_param_as_rvalue (x), b_default, 4, cases);
gcc_jit_block_end_with_return ( b_case_0_5, NULL, gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 3)); gcc_jit_block_end_with_return ( b_case_25_27, NULL, gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 4)); gcc_jit_block_end_with_return ( b_case_m42_m17, NULL, gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 83)); gcc_jit_block_end_with_return ( b_case_40, NULL, gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 8)); gcc_jit_block_end_with_return ( b_default, NULL, gcc_jit_context_new_rvalue_from_int (ctxt, t_int, 10)); }
See also gcc_jit_extended_asm for entrypoints for adding inline assembler statements to a function.