diff options
Diffstat (limited to 'gcc/jit/docs/intro/tutorial02.rst')
-rw-r--r-- | gcc/jit/docs/intro/tutorial02.rst | 349 |
1 files changed, 349 insertions, 0 deletions
diff --git a/gcc/jit/docs/intro/tutorial02.rst b/gcc/jit/docs/intro/tutorial02.rst new file mode 100644 index 00000000000..f52499ea0aa --- /dev/null +++ b/gcc/jit/docs/intro/tutorial02.rst @@ -0,0 +1,349 @@ +.. Copyright (C) 2014 Free Software Foundation, Inc. + Originally contributed by David Malcolm <dmalcolm@redhat.com> + + This is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see + <http://www.gnu.org/licenses/>. + +.. default-domain:: c + +Tutorial part 2: Creating a trivial machine code function +--------------------------------------------------------- + +Consider this C function: + +.. code-block:: c + + int square (int i) + { + return i * i; + } + +How can we construct this at run-time using libgccjit? + +First we need to include the relevant header: + +.. code-block:: c + + #include <libgccjit.h> + +All state associated with compilation is associated with a +:c:type:`gcc_jit_context *`. + +Create one using :c:func:`gcc_jit_context_acquire`: + +.. code-block:: c + + gcc_jit_context *ctxt; + ctxt = gcc_jit_context_acquire (); + +The JIT library has a system of types. It is statically-typed: every +expression is of a specific type, 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 :c:type:`gcc_jit_type *`, using +:c:func:`gcc_jit_context_get_type`: + +.. code-block:: c + + gcc_jit_type *int_type = + gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); + +:c:type:`gcc_jit_type *` is an example of a "contextual" object: every +entity in the API is associated with a :c:type:`gcc_jit_context *`. + +Memory management is easy: all such "contextual" objects are automatically +cleaned up for you when the context is released, using +:c:func:`gcc_jit_context_release`: + +.. code-block:: c + + 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, :c:func:`gcc_jit_type_as_object`: + +.. code-block:: c + + gcc_jit_object *obj = gcc_jit_type_as_object (int_type); + +One thing you can do with a :c:type:`gcc_jit_object *` is +to ask it for a human-readable description, using +:c:func:`gcc_jit_object_get_debug_string`: + +.. code-block:: c + + printf ("obj: %s\n", gcc_jit_object_get_debug_string (obj)); + +giving this text on stdout: + +.. code-block:: bash + + obj: int + +This is invaluable when debugging. + +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 :c:func:`gcc_jit_context_new_param`: + +.. code-block:: c + + gcc_jit_param *param_i = + gcc_jit_context_new_param (ctxt, NULL, int_type, "i"); + +Now we can create the function, using +:c:func:`gcc_jit_context_new_function`: + +.. code-block:: c + + 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 function has no control-flow, so we just need one basic block: + +.. code-block:: c + + gcc_jit_block *block = gcc_jit_function_new_block (func, NULL); + +Our basic block is relatively simple: it immediately terminates by +returning the value of an expression. + +We can build the expression using :c:func:`gcc_jit_context_new_binary_op`: + +.. code-block:: c + + 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)); + +A :c:type:`gcc_jit_rvalue *` is another example of a +:c:type:`gcc_jit_object *` subclass. We can upcast it using +:c:func:`gcc_jit_rvalue_as_object` and as before print it with +:c:func:`gcc_jit_object_get_debug_string`. + +.. code-block:: c + + printf ("expr: %s\n", + gcc_jit_object_get_debug_string ( + gcc_jit_rvalue_as_object (expr))); + +giving this output: + +.. code-block:: bash + + 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: + +.. code-block:: c + + gcc_jit_block_end_with_return (block, NULL, expr); + +OK, we've populated the context. We can now compile it using +:c:func:`gcc_jit_context_compile`: + +.. code-block:: c + + gcc_jit_result *result; + result = gcc_jit_context_compile (ctxt); + +and get a :c:type:`gcc_jit_result *`. + +We can now use :c:func:`gcc_jit_result_get_code` to look up a specific +machine code routine within the result, in this case, the function we +created above. + +.. code-block:: c + + void *fn_ptr = gcc_jit_result_get_code (result, "square"); + if (!fn_ptr) + { + fprintf (stderr, "NULL fn_ptr"); + goto error; + } + +We can now cast the pointer to an appropriate function pointer type, and +then call it: + +.. code-block:: c + + typedef int (*fn_type) (int); + fn_type square = (fn_type)fn_ptr; + printf ("result: %d", square (5)); + +.. code-block:: bash + + result: 25 + + +Options +******* + +To get more information on what's going on, you can set debugging flags +on the context using :c:func:`gcc_jit_context_set_bool_option`. + +.. (I'm deliberately not mentioning + :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE` here since I think + it's probably more of use to implementors than to users) + +Setting :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE` will dump a +C-like representation to stderr when you compile (GCC's "GIMPLE" +representation): + +.. code-block:: c + + gcc_jit_context_set_bool_option ( + ctxt, + GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, + 1); + result = gcc_jit_context_compile (ctxt); + +.. code-block:: c + + square (signed int i) + { + signed int D.260; + + entry: + D.260 = i * i; + return D.260; + } + +We can see the generated machine code in assembler form (on stderr) by +setting :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE` on the context +before compiling: + +.. code-block:: c + + gcc_jit_context_set_bool_option ( + ctxt, + GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, + 1); + result = gcc_jit_context_compile (ctxt); + +.. code-block:: gas + + .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-0.5.1920c315ff984892399893b380305ab36e07b455.fc20)" + .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 +:c:func:`gcc_jit_context_set_int_option` with +:c:macro:`GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL`: + +.. code-block:: c + + gcc_jit_context_set_int_option ( + ctxt, + GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, + 3); + +.. code-block:: gas + + .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-0.5.1920c315ff984892399893b380305ab36e07b455.fc20)" + .section .note.GNU-stack,"",@progbits + +Naturally this has only a small effect on such a trivial function. + + +Full example +************ + +Here's what the above looks like as a complete program: + + .. literalinclude:: ../examples/tut02-square.c + :lines: 1- + :language: c + +Building and running it: + +.. code-block:: console + + $ gcc \ + tut02-square.c \ + -o tut02-square \ + -lgccjit + + # Run the built program: + $ ./tut02-square + result: 25 |