4-Tutorial Part 2 - Creating A Trivial Machine Code Function - Libgccjit 13.2.0 Documentation
4-Tutorial Part 2 - Creating A Trivial Machine Code Function - Libgccjit 13.2.0 Documentation
0 ( ) documentation » Tutorial » Tutorial part 2: Creating a trivial machine code function previous | next | index
Show Source
gcc_jit_context *ctxt;
ctxt = gcc_jit_context_acquire (); Quick search
The JIT library has a system of types. It is statically-typed: every expression is of a specific type, Go
fixed at compile-time. In our example, all of the expressions are of the C int type, so let’s
obtain this from the context, as a gcc_jit_type*, using gcc_jit_context_get_type():
gcc_jit_type *int_type =
gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
gcc_jit_type* is an example of a “contextual” object: every entity in the API is associated with a
gcc_jit_context*.
Memory management is easy: all such “contextual” objects are automatically cleaned up for you
when the context is released, using gcc_jit_context_release():
gcc_jit_context_release (ctxt);
so you don’t need to manually track and cleanup all objects, just the contexts.
Although the API is C-based, there is a form of class hierarchy, which looks like this:
+- gcc_jit_object
+- gcc_jit_location
+- gcc_jit_type
+- gcc_jit_struct
+- gcc_jit_field
+- gcc_jit_function
+- gcc_jit_block
+- gcc_jit_rvalue
+- gcc_jit_lvalue
+- gcc_jit_param
There are casting methods for upcasting from subclasses to parent classes. For example,
gcc_jit_type_as_object():
One thing you can do with a gcc_jit_object* is to ask it for a human-readable description,
using gcc_jit_object_get_debug_string():
obj: int
Let’s create the function. To do so, we first need to construct its single parameter, specifying its
type and giving it a name, using gcc_jit_context_new_param():
gcc_jit_param *param_i =
gcc_jit_context_new_param (ctxt, NULL, int_type, "i");
gcc_jit_function *func =
gcc_jit_context_new_function (ctxt, NULL,
GCC_JIT_FUNCTION_EXPORTED,
int_type,
"square",
1, ¶m_i,
0);
To define the code within the function, we must create basic blocks containing statements.
Every basic block contains a list of statements, eventually terminated by a statement that either
returns, or jumps to another basic block.
Our basic block is relatively simple: it immediately terminates by returning the value of an
expression.
gcc_jit_rvalue *expr =
gcc_jit_context_new_binary_op (
ctxt, NULL,
GCC_JIT_BINARY_OP_MULT, int_type,
gcc_jit_param_as_rvalue (param_i),
gcc_jit_param_as_rvalue (param_i));
expr: i * i
Creating the expression in itself doesn’t do anything; we have to add this expression to a
statement within the block. In this case, we use it to build a return statement, which terminates
the basic block:
OK, we’ve populated the context. We can now compile it using gcc_jit_context_compile():
gcc_jit_result *result;
result = gcc_jit_context_compile (ctxt);
At this point we’re done with the context; we can release it:
gcc_jit_context_release (ctxt);
We can now use gcc_jit_result_get_code() to look up a specific machine code routine within
the result, in this case, the function we created above.
We can now cast the pointer to an appropriate function pointer type, and then call it:
result: 25
Once we’re done with the code, we can release the result:
gcc_jit_result_release (result);
Error-handling
Various kinds of errors are possible when using the API, such as mismatched types in an
assignment. You can only compile and get code from a context if no errors occur.
Errors are printed on stderr; they typically contain the name of the API entrypoint where the error
occurred, and pertinent information on the problem:
The API is designed to cope with errors without crashing, so you can get away with having a
single error-handling check in your code:
For more information, see the error-handling guide within the Topic eference.
Options
To get more information on what’s going on, you can set debugging flags on the context using
gcc_jit_context_set_bool_option().
gcc_jit_context_set_bool_option (
ctxt,
GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE,
1);
result = gcc_jit_context_compile (ctxt);
entry:
D.260 = i * i;
return D.260;
}
We can see the generated machine code in assembler form (on stderr) by setting
GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE on the context before compiling:
gcc_jit_context_set_bool_option (
ctxt,
GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE,
1);
result = gcc_jit_context_compile (ctxt);
.file "fake.c"
.text
.globl square
.type square, @function
square:
.LFB6:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
movl %edi, -4(%rbp)
.L14:
movl -4(%rbp), %eax
imull -4(%rbp), %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE6:
.size square, .-square
.ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2)"
.section .note.GNU-stack,"",@progbits
By default, no optimizations are performed, the equivalent of GCC’s -O0 option. We can turn
things up to e.g. -O3 by calling gcc_jit_context_set_int_option() with
GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL:
gcc_jit_context_set_int_option (
ctxt,
GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL,
3);
.file "fake.c"
.text
.p2align 4,,15
.globl square
.type square, @function
square:
.LFB7:
.cfi_startproc
.L16:
movl %edi, %eax
imull %edi, %eax
ret
.cfi_endproc
.LFE7:
.size square, .-square
.ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2)"
.section .note.GNU-stack,"",@progbits
Full example
Here’s what the above looks like as a complete program:
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include <libgccjit.h>
#include <stdlib.h>
#include <stdio.h>
void
create_code (gcc_jit_context *ctxt)
{
/* Let's try to inject the equivalent of:
gcc_jit_rvalue *expr =
gcc_jit_context_new_binary_op (
ctxt, NULL,
GCC_JIT_BINARY_OP_MULT, int_type,
gcc_jit_param_as_rvalue (param_i),
gcc_jit_param_as_rvalue (param_i));
int
main (int argc, char **argv)
{
gcc_jit_context *ctxt = NULL;
gcc_jit_result *result = NULL;
error:
if (ctxt)
gcc_jit_context_release (ctxt);
if (result)
gcc_jit_result_release (result);
return 0;
}
$ gcc \
tut02-square.c \
-o tut02-square \
-lgccjit
libgccjit 13.2.0 ( ) documentation » Tutorial » Tutorial part 2: Creating a trivial machine code function previous | next | index
© Copyright 2014-2023 Free Software Foundation, Inc.. Created using Sphinx 5.3.0.