1 .. Copyright (C) 2014-2015 Free Software Foundation, Inc.
2 Originally contributed by David Malcolm <dmalcolm@redhat.com>
4 This is free software: you can redistribute it and/or modify it
5 under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful, but
10 WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see
16 <http://www.gnu.org/licenses/>.
20 Tutorial part 2: Creating a trivial machine code function
21 ---------------------------------------------------------
23 Consider this C function:
32 How can we construct this at run-time using libgccjit?
34 First we need to include the relevant header:
38 #include <libgccjit.h>
40 All state associated with compilation is associated with a
41 :c:type:`gcc_jit_context *`.
43 Create one using :c:func:`gcc_jit_context_acquire`:
47 gcc_jit_context *ctxt;
48 ctxt = gcc_jit_context_acquire ();
50 The JIT library has a system of types. It is statically-typed: every
51 expression is of a specific type, fixed at compile-time. In our example,
52 all of the expressions are of the C `int` type, so let's obtain this from
53 the context, as a :c:type:`gcc_jit_type *`, using
54 :c:func:`gcc_jit_context_get_type`:
58 gcc_jit_type *int_type =
59 gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
61 :c:type:`gcc_jit_type *` is an example of a "contextual" object: every
62 entity in the API is associated with a :c:type:`gcc_jit_context *`.
64 Memory management is easy: all such "contextual" objects are automatically
65 cleaned up for you when the context is released, using
66 :c:func:`gcc_jit_context_release`:
70 gcc_jit_context_release (ctxt);
72 so you don't need to manually track and cleanup all objects, just the
75 Although the API is C-based, there is a form of class hierarchy, which
89 There are casting methods for upcasting from subclasses to parent classes.
90 For example, :c:func:`gcc_jit_type_as_object`:
94 gcc_jit_object *obj = gcc_jit_type_as_object (int_type);
96 One thing you can do with a :c:type:`gcc_jit_object *` is
97 to ask it for a human-readable description, using
98 :c:func:`gcc_jit_object_get_debug_string`:
102 printf ("obj: %s\n", gcc_jit_object_get_debug_string (obj));
104 giving this text on stdout:
110 This is invaluable when debugging.
112 Let's create the function. To do so, we first need to construct
113 its single parameter, specifying its type and giving it a name,
114 using :c:func:`gcc_jit_context_new_param`:
118 gcc_jit_param *param_i =
119 gcc_jit_context_new_param (ctxt, NULL, int_type, "i");
121 Now we can create the function, using
122 :c:func:`gcc_jit_context_new_function`:
126 gcc_jit_function *func =
127 gcc_jit_context_new_function (ctxt, NULL,
128 GCC_JIT_FUNCTION_EXPORTED,
134 To define the code within the function, we must create basic blocks
135 containing statements.
137 Every basic block contains a list of statements, eventually terminated
138 by a statement that either returns, or jumps to another basic block.
140 Our function has no control-flow, so we just need one basic block:
144 gcc_jit_block *block = gcc_jit_function_new_block (func, NULL);
146 Our basic block is relatively simple: it immediately terminates by
147 returning the value of an expression.
149 We can build the expression using :c:func:`gcc_jit_context_new_binary_op`:
153 gcc_jit_rvalue *expr =
154 gcc_jit_context_new_binary_op (
156 GCC_JIT_BINARY_OP_MULT, int_type,
157 gcc_jit_param_as_rvalue (param_i),
158 gcc_jit_param_as_rvalue (param_i));
160 A :c:type:`gcc_jit_rvalue *` is another example of a
161 :c:type:`gcc_jit_object *` subclass. We can upcast it using
162 :c:func:`gcc_jit_rvalue_as_object` and as before print it with
163 :c:func:`gcc_jit_object_get_debug_string`.
167 printf ("expr: %s\n",
168 gcc_jit_object_get_debug_string (
169 gcc_jit_rvalue_as_object (expr)));
177 Creating the expression in itself doesn't do anything; we have to add
178 this expression to a statement within the block. In this case, we use it
179 to build a return statement, which terminates the basic block:
183 gcc_jit_block_end_with_return (block, NULL, expr);
185 OK, we've populated the context. We can now compile it using
186 :c:func:`gcc_jit_context_compile`:
190 gcc_jit_result *result;
191 result = gcc_jit_context_compile (ctxt);
193 and get a :c:type:`gcc_jit_result *`.
195 At this point we're done with the context; we can release it:
199 gcc_jit_context_release (ctxt);
201 We can now use :c:func:`gcc_jit_result_get_code` to look up a specific
202 machine code routine within the result, in this case, the function we
207 void *fn_ptr = gcc_jit_result_get_code (result, "square");
210 fprintf (stderr, "NULL fn_ptr");
214 We can now cast the pointer to an appropriate function pointer type, and
219 typedef int (*fn_type) (int);
220 fn_type square = (fn_type)fn_ptr;
221 printf ("result: %d", square (5));
227 Once we're done with the code, we can release the result:
231 gcc_jit_result_release (result);
233 We can't call ``square`` anymore once we've released ``result``.
238 Various kinds of errors are possible when using the API, such as
239 mismatched types in an assignment. You can only compile and get code
240 from a context if no errors occur.
242 Errors are printed on stderr; they typically contain the name of the API
243 entrypoint where the error occurred, and pertinent information on the
246 .. code-block:: console
248 ./buggy-program: error: gcc_jit_block_add_assignment: mismatching types: assignment to i (type: int) from "hello world" (type: const char *)
250 The API is designed to cope with errors without crashing, so you can get
251 away with having a single error-handling check in your code:
255 void *fn_ptr = gcc_jit_result_get_code (result, "square");
258 fprintf (stderr, "NULL fn_ptr");
262 For more information, see the :ref:`error-handling guide <error-handling>`
263 within the Topic eference.
269 To get more information on what's going on, you can set debugging flags
270 on the context using :c:func:`gcc_jit_context_set_bool_option`.
272 .. (I'm deliberately not mentioning
273 :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE` here since I think
274 it's probably more of use to implementors than to users)
276 Setting :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE` will dump a
277 C-like representation to stderr when you compile (GCC's "GIMPLE"
282 gcc_jit_context_set_bool_option (
284 GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE,
286 result = gcc_jit_context_compile (ctxt);
290 square (signed int i)
299 We can see the generated machine code in assembler form (on stderr) by
300 setting :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE` on the context
305 gcc_jit_context_set_bool_option (
307 GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE,
309 result = gcc_jit_context_compile (ctxt);
316 .type square, @function
321 .cfi_def_cfa_offset 16
324 .cfi_def_cfa_register 6
334 .size square, .-square
335 .ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2-0.5.1920c315ff984892399893b380305ab36e07b455.fc20)"
336 .section .note.GNU-stack,"",@progbits
338 By default, no optimizations are performed, the equivalent of GCC's
339 `-O0` option. We can turn things up to e.g. `-O3` by calling
340 :c:func:`gcc_jit_context_set_int_option` with
341 :c:macro:`GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL`:
345 gcc_jit_context_set_int_option (
347 GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL,
356 .type square, @function
366 .size square, .-square
367 .ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2-0.5.1920c315ff984892399893b380305ab36e07b455.fc20)"
368 .section .note.GNU-stack,"",@progbits
370 Naturally this has only a small effect on such a trivial function.
376 Here's what the above looks like as a complete program:
378 .. literalinclude:: ../examples/tut02-square.c
382 Building and running it:
384 .. code-block:: console
391 # Run the built program: