145
|
1 .. Copyright (C) 2014-2020 Free Software Foundation, Inc.
|
111
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|