]>
Commit | Line | Data |
---|---|---|
a5544970 | 1 | .. Copyright (C) 2014-2019 Free Software Foundation, Inc. |
35485da9 DM |
2 | Originally contributed by David Malcolm <dmalcolm@redhat.com> |
3 | ||
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. | |
8 | ||
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. | |
13 | ||
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/>. | |
17 | ||
18 | .. default-domain:: c | |
19 | ||
20 | Tutorial part 2: Creating a trivial machine code function | |
21 | --------------------------------------------------------- | |
22 | ||
23 | Consider this C function: | |
24 | ||
25 | .. code-block:: c | |
26 | ||
27 | int square (int i) | |
28 | { | |
29 | return i * i; | |
30 | } | |
31 | ||
32 | How can we construct this at run-time using libgccjit? | |
33 | ||
34 | First we need to include the relevant header: | |
35 | ||
36 | .. code-block:: c | |
37 | ||
38 | #include <libgccjit.h> | |
39 | ||
40 | All state associated with compilation is associated with a | |
41 | :c:type:`gcc_jit_context *`. | |
42 | ||
43 | Create one using :c:func:`gcc_jit_context_acquire`: | |
44 | ||
45 | .. code-block:: c | |
46 | ||
47 | gcc_jit_context *ctxt; | |
48 | ctxt = gcc_jit_context_acquire (); | |
49 | ||
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`: | |
55 | ||
56 | .. code-block:: c | |
57 | ||
58 | gcc_jit_type *int_type = | |
59 | gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); | |
60 | ||
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 *`. | |
63 | ||
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`: | |
67 | ||
68 | .. code-block:: c | |
69 | ||
70 | gcc_jit_context_release (ctxt); | |
71 | ||
72 | so you don't need to manually track and cleanup all objects, just the | |
73 | contexts. | |
74 | ||
75 | Although the API is C-based, there is a form of class hierarchy, which | |
76 | looks like this:: | |
77 | ||
78 | +- gcc_jit_object | |
79 | +- gcc_jit_location | |
80 | +- gcc_jit_type | |
81 | +- gcc_jit_struct | |
82 | +- gcc_jit_field | |
83 | +- gcc_jit_function | |
84 | +- gcc_jit_block | |
85 | +- gcc_jit_rvalue | |
86 | +- gcc_jit_lvalue | |
87 | +- gcc_jit_param | |
88 | ||
89 | There are casting methods for upcasting from subclasses to parent classes. | |
90 | For example, :c:func:`gcc_jit_type_as_object`: | |
91 | ||
92 | .. code-block:: c | |
93 | ||
94 | gcc_jit_object *obj = gcc_jit_type_as_object (int_type); | |
95 | ||
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`: | |
99 | ||
100 | .. code-block:: c | |
101 | ||
102 | printf ("obj: %s\n", gcc_jit_object_get_debug_string (obj)); | |
103 | ||
104 | giving this text on stdout: | |
105 | ||
106 | .. code-block:: bash | |
107 | ||
108 | obj: int | |
109 | ||
110 | This is invaluable when debugging. | |
111 | ||
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`: | |
115 | ||
116 | .. code-block:: c | |
117 | ||
118 | gcc_jit_param *param_i = | |
119 | gcc_jit_context_new_param (ctxt, NULL, int_type, "i"); | |
120 | ||
121 | Now we can create the function, using | |
122 | :c:func:`gcc_jit_context_new_function`: | |
123 | ||
124 | .. code-block:: c | |
125 | ||
126 | gcc_jit_function *func = | |
127 | gcc_jit_context_new_function (ctxt, NULL, | |
128 | GCC_JIT_FUNCTION_EXPORTED, | |
129 | int_type, | |
130 | "square", | |
131 | 1, ¶m_i, | |
132 | 0); | |
133 | ||
134 | To define the code within the function, we must create basic blocks | |
135 | containing statements. | |
136 | ||
137 | Every basic block contains a list of statements, eventually terminated | |
138 | by a statement that either returns, or jumps to another basic block. | |
139 | ||
140 | Our function has no control-flow, so we just need one basic block: | |
141 | ||
142 | .. code-block:: c | |
143 | ||
144 | gcc_jit_block *block = gcc_jit_function_new_block (func, NULL); | |
145 | ||
146 | Our basic block is relatively simple: it immediately terminates by | |
147 | returning the value of an expression. | |
148 | ||
149 | We can build the expression using :c:func:`gcc_jit_context_new_binary_op`: | |
150 | ||
151 | .. code-block:: c | |
152 | ||
153 | gcc_jit_rvalue *expr = | |
154 | gcc_jit_context_new_binary_op ( | |
155 | ctxt, NULL, | |
156 | GCC_JIT_BINARY_OP_MULT, int_type, | |
157 | gcc_jit_param_as_rvalue (param_i), | |
158 | gcc_jit_param_as_rvalue (param_i)); | |
159 | ||
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`. | |
164 | ||
165 | .. code-block:: c | |
166 | ||
167 | printf ("expr: %s\n", | |
168 | gcc_jit_object_get_debug_string ( | |
169 | gcc_jit_rvalue_as_object (expr))); | |
170 | ||
171 | giving this output: | |
172 | ||
173 | .. code-block:: bash | |
174 | ||
175 | expr: i * i | |
176 | ||
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: | |
180 | ||
181 | .. code-block:: c | |
182 | ||
183 | gcc_jit_block_end_with_return (block, NULL, expr); | |
184 | ||
185 | OK, we've populated the context. We can now compile it using | |
186 | :c:func:`gcc_jit_context_compile`: | |
187 | ||
188 | .. code-block:: c | |
189 | ||
190 | gcc_jit_result *result; | |
191 | result = gcc_jit_context_compile (ctxt); | |
192 | ||
193 | and get a :c:type:`gcc_jit_result *`. | |
194 | ||
81ba15f1 DM |
195 | At this point we're done with the context; we can release it: |
196 | ||
197 | .. code-block:: c | |
198 | ||
199 | gcc_jit_context_release (ctxt); | |
200 | ||
35485da9 DM |
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 | |
203 | created above. | |
204 | ||
205 | .. code-block:: c | |
206 | ||
207 | void *fn_ptr = gcc_jit_result_get_code (result, "square"); | |
208 | if (!fn_ptr) | |
209 | { | |
210 | fprintf (stderr, "NULL fn_ptr"); | |
211 | goto error; | |
212 | } | |
213 | ||
214 | We can now cast the pointer to an appropriate function pointer type, and | |
215 | then call it: | |
216 | ||
217 | .. code-block:: c | |
218 | ||
219 | typedef int (*fn_type) (int); | |
220 | fn_type square = (fn_type)fn_ptr; | |
221 | printf ("result: %d", square (5)); | |
222 | ||
223 | .. code-block:: bash | |
224 | ||
225 | result: 25 | |
226 | ||
e250f0dc DM |
227 | Once we're done with the code, we can release the result: |
228 | ||
229 | .. code-block:: c | |
230 | ||
231 | gcc_jit_result_release (result); | |
232 | ||
233 | We can't call ``square`` anymore once we've released ``result``. | |
234 | ||
235 | ||
236 | Error-handling | |
237 | ************** | |
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. | |
241 | ||
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 | |
244 | problem: | |
245 | ||
246 | .. code-block:: console | |
247 | ||
248 | ./buggy-program: error: gcc_jit_block_add_assignment: mismatching types: assignment to i (type: int) from "hello world" (type: const char *) | |
249 | ||
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: | |
252 | ||
253 | .. code-block:: c | |
254 | ||
255 | void *fn_ptr = gcc_jit_result_get_code (result, "square"); | |
256 | if (!fn_ptr) | |
257 | { | |
258 | fprintf (stderr, "NULL fn_ptr"); | |
259 | goto error; | |
260 | } | |
261 | ||
262 | For more information, see the :ref:`error-handling guide <error-handling>` | |
263 | within the Topic eference. | |
264 | ||
35485da9 DM |
265 | |
266 | Options | |
267 | ******* | |
268 | ||
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`. | |
271 | ||
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) | |
275 | ||
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" | |
278 | representation): | |
279 | ||
280 | .. code-block:: c | |
281 | ||
282 | gcc_jit_context_set_bool_option ( | |
283 | ctxt, | |
284 | GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, | |
285 | 1); | |
286 | result = gcc_jit_context_compile (ctxt); | |
287 | ||
288 | .. code-block:: c | |
289 | ||
290 | square (signed int i) | |
291 | { | |
292 | signed int D.260; | |
293 | ||
294 | entry: | |
295 | D.260 = i * i; | |
296 | return D.260; | |
297 | } | |
298 | ||
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 | |
301 | before compiling: | |
302 | ||
303 | .. code-block:: c | |
304 | ||
305 | gcc_jit_context_set_bool_option ( | |
306 | ctxt, | |
307 | GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, | |
308 | 1); | |
309 | result = gcc_jit_context_compile (ctxt); | |
310 | ||
311 | .. code-block:: gas | |
312 | ||
313 | .file "fake.c" | |
314 | .text | |
315 | .globl square | |
316 | .type square, @function | |
317 | square: | |
318 | .LFB6: | |
319 | .cfi_startproc | |
320 | pushq %rbp | |
321 | .cfi_def_cfa_offset 16 | |
322 | .cfi_offset 6, -16 | |
323 | movq %rsp, %rbp | |
324 | .cfi_def_cfa_register 6 | |
325 | movl %edi, -4(%rbp) | |
326 | .L14: | |
327 | movl -4(%rbp), %eax | |
328 | imull -4(%rbp), %eax | |
329 | popq %rbp | |
330 | .cfi_def_cfa 7, 8 | |
331 | ret | |
332 | .cfi_endproc | |
333 | .LFE6: | |
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 | |
337 | ||
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`: | |
342 | ||
343 | .. code-block:: c | |
344 | ||
345 | gcc_jit_context_set_int_option ( | |
346 | ctxt, | |
347 | GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, | |
348 | 3); | |
349 | ||
350 | .. code-block:: gas | |
351 | ||
352 | .file "fake.c" | |
353 | .text | |
354 | .p2align 4,,15 | |
355 | .globl square | |
356 | .type square, @function | |
357 | square: | |
358 | .LFB7: | |
359 | .cfi_startproc | |
360 | .L16: | |
361 | movl %edi, %eax | |
362 | imull %edi, %eax | |
363 | ret | |
364 | .cfi_endproc | |
365 | .LFE7: | |
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 | |
369 | ||
370 | Naturally this has only a small effect on such a trivial function. | |
371 | ||
372 | ||
373 | Full example | |
374 | ************ | |
375 | ||
376 | Here's what the above looks like as a complete program: | |
377 | ||
378 | .. literalinclude:: ../examples/tut02-square.c | |
379 | :lines: 1- | |
380 | :language: c | |
381 | ||
382 | Building and running it: | |
383 | ||
384 | .. code-block:: console | |
385 | ||
386 | $ gcc \ | |
387 | tut02-square.c \ | |
388 | -o tut02-square \ | |
389 | -lgccjit | |
390 | ||
391 | # Run the built program: | |
392 | $ ./tut02-square | |
393 | result: 25 |