tutorial02.rst revision 1.6
1.. Copyright (C) 2014-2020 Free Software Foundation, Inc. 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 20Tutorial part 2: Creating a trivial machine code function 21--------------------------------------------------------- 22 23Consider this C function: 24 25.. code-block:: c 26 27 int square (int i) 28 { 29 return i * i; 30 } 31 32How can we construct this at run-time using libgccjit? 33 34First we need to include the relevant header: 35 36.. code-block:: c 37 38 #include <libgccjit.h> 39 40All state associated with compilation is associated with a 41:c:type:`gcc_jit_context *`. 42 43Create 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 50The JIT library has a system of types. It is statically-typed: every 51expression is of a specific type, fixed at compile-time. In our example, 52all of the expressions are of the C `int` type, so let's obtain this from 53the 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 62entity in the API is associated with a :c:type:`gcc_jit_context *`. 63 64Memory management is easy: all such "contextual" objects are automatically 65cleaned 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 72so you don't need to manually track and cleanup all objects, just the 73contexts. 74 75Although the API is C-based, there is a form of class hierarchy, which 76looks 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 89There are casting methods for upcasting from subclasses to parent classes. 90For 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 96One thing you can do with a :c:type:`gcc_jit_object *` is 97to 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 104giving this text on stdout: 105 106.. code-block:: bash 107 108 obj: int 109 110This is invaluable when debugging. 111 112Let's create the function. To do so, we first need to construct 113its single parameter, specifying its type and giving it a name, 114using :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 121Now 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 134To define the code within the function, we must create basic blocks 135containing statements. 136 137Every basic block contains a list of statements, eventually terminated 138by a statement that either returns, or jumps to another basic block. 139 140Our 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 146Our basic block is relatively simple: it immediately terminates by 147returning the value of an expression. 148 149We 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 160A :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 171giving this output: 172 173.. code-block:: bash 174 175 expr: i * i 176 177Creating the expression in itself doesn't do anything; we have to add 178this expression to a statement within the block. In this case, we use it 179to 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 185OK, 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 193and get a :c:type:`gcc_jit_result *`. 194 195At 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 201We can now use :c:func:`gcc_jit_result_get_code` to look up a specific 202machine code routine within the result, in this case, the function we 203created 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 214We can now cast the pointer to an appropriate function pointer type, and 215then 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 227Once we're done with the code, we can release the result: 228 229.. code-block:: c 230 231 gcc_jit_result_release (result); 232 233We can't call ``square`` anymore once we've released ``result``. 234 235 236Error-handling 237************** 238Various kinds of errors are possible when using the API, such as 239mismatched types in an assignment. You can only compile and get code 240from a context if no errors occur. 241 242Errors are printed on stderr; they typically contain the name of the API 243entrypoint where the error occurred, and pertinent information on the 244problem: 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 250The API is designed to cope with errors without crashing, so you can get 251away 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 262For more information, see the :ref:`error-handling guide <error-handling>` 263within the Topic eference. 264 265 266Options 267******* 268 269To get more information on what's going on, you can set debugging flags 270on 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 276Setting :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE` will dump a 277C-like representation to stderr when you compile (GCC's "GIMPLE" 278representation): 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 299We can see the generated machine code in assembler form (on stderr) by 300setting :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE` on the context 301before 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 338By 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 370Naturally this has only a small effect on such a trivial function. 371 372 373Full example 374************ 375 376Here's what the above looks like as a complete program: 377 378 .. literalinclude:: ../examples/tut02-square.c 379 :lines: 1- 380 :language: c 381 382Building 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 394