tutorial02.rst revision 1.1.1.1
1.. Copyright (C) 2014-2015 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:: cpp 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's C++ API? 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:type:`gccjit::context`, which is a thin C++ wrapper around the C API's 42:c:type:`gcc_jit_context *`. 43 44Create one using :func:`gccjit::context::acquire`: 45 46.. code-block:: c++ 47 48 gccjit::context ctxt; 49 ctxt = gccjit::context::acquire (); 50 51The JIT library has a system of types. It is statically-typed: every 52expression is of a specific type, fixed at compile-time. In our example, 53all of the expressions are of the C `int` type, so let's obtain this from 54the context, as a :type:`gccjit::type`, using 55:func:`gccjit::context::get_type`: 56 57.. code-block:: c++ 58 59 gccjit::type int_type = ctxt.get_type (GCC_JIT_TYPE_INT); 60 61:type:`gccjit::type` is an example of a "contextual" object: every 62entity in the API is associated with a :type:`gccjit::context`. 63 64Memory management is easy: all such "contextual" objects are automatically 65cleaned up for you when the context is released, using 66:func:`gccjit::context::release`: 67 68.. code-block:: c++ 69 70 ctxt.release (); 71 72so you don't need to manually track and cleanup all objects, just the 73contexts. 74 75All of the C++ classes in the API are thin wrappers around pointers to 76types in the C API. 77 78The C++ class hierarchy within the ``gccjit`` namespace looks like this:: 79 80 +- object 81 +- location 82 +- type 83 +- struct 84 +- field 85 +- function 86 +- block 87 +- rvalue 88 +- lvalue 89 +- param 90 91One thing you can do with a :type:`gccjit::object` is 92to ask it for a human-readable description as a :type:`std::string`, using 93:func:`gccjit::object::get_debug_string`: 94 95.. code-block:: c++ 96 97 printf ("obj: %s\n", obj.get_debug_string ().c_str ()); 98 99giving this text on stdout: 100 101.. code-block:: bash 102 103 obj: int 104 105This is invaluable when debugging. 106 107Let's create the function. To do so, we first need to construct 108its single parameter, specifying its type and giving it a name, 109using :func:`gccjit::context::new_param`: 110 111.. code-block:: c++ 112 113 gccjit::param param_i = ctxt.new_param (int_type, "i"); 114 115and we can then make a vector of all of the params of the function, 116in this case just one: 117 118.. code-block:: c++ 119 120 std::vector<gccjit::param> params; 121 params.push_back (param_i); 122 123Now we can create the function, using 124:c:func:`gccjit::context::new_function`: 125 126.. code-block:: c++ 127 128 gccjit::function func = 129 ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED, 130 int_type, 131 "square", 132 params, 133 0); 134 135To define the code within the function, we must create basic blocks 136containing statements. 137 138Every basic block contains a list of statements, eventually terminated 139by a statement that either returns, or jumps to another basic block. 140 141Our function has no control-flow, so we just need one basic block: 142 143.. code-block:: c++ 144 145 gccjit::block block = func.new_block (); 146 147Our basic block is relatively simple: it immediately terminates by 148returning the value of an expression. 149 150We can build the expression using :func:`gccjit::context::new_binary_op`: 151 152.. code-block:: c++ 153 154 gccjit::rvalue expr = 155 ctxt.new_binary_op ( 156 GCC_JIT_BINARY_OP_MULT, int_type, 157 param_i, param_i); 158 159A :type:`gccjit::rvalue` is another example of a 160:type:`gccjit::object` subclass. As before, we can print it with 161:func:`gccjit::object::get_debug_string`. 162 163.. code-block:: c++ 164 165 printf ("expr: %s\n", expr.get_debug_string ().c_str ()); 166 167giving this output: 168 169.. code-block:: bash 170 171 expr: i * i 172 173Note that :type:`gccjit::rvalue` provides numerous overloaded operators 174which can be used to dramatically reduce the amount of typing needed. 175We can build the above binary operation more directly with this one-liner: 176 177.. code-block:: c++ 178 179 gccjit::rvalue expr = param_i * param_i; 180 181Creating the expression in itself doesn't do anything; we have to add 182this expression to a statement within the block. In this case, we use it 183to build a return statement, which terminates the basic block: 184 185.. code-block:: c++ 186 187 block.end_with_return (expr); 188 189OK, we've populated the context. We can now compile it using 190:func:`gccjit::context::compile`: 191 192.. code-block:: c++ 193 194 gcc_jit_result *result; 195 result = ctxt.compile (); 196 197and get a :c:type:`gcc_jit_result *`. 198 199We can now use :c:func:`gcc_jit_result_get_code` to look up a specific 200machine code routine within the result, in this case, the function we 201created above. 202 203.. code-block:: c++ 204 205 void *fn_ptr = gcc_jit_result_get_code (result, "square"); 206 if (!fn_ptr) 207 { 208 fprintf (stderr, "NULL fn_ptr"); 209 goto error; 210 } 211 212We can now cast the pointer to an appropriate function pointer type, and 213then call it: 214 215.. code-block:: c++ 216 217 typedef int (*fn_type) (int); 218 fn_type square = (fn_type)fn_ptr; 219 printf ("result: %d", square (5)); 220 221.. code-block:: bash 222 223 result: 25 224 225 226Options 227******* 228 229To get more information on what's going on, you can set debugging flags 230on the context using :func:`gccjit::context::set_bool_option`. 231 232.. (I'm deliberately not mentioning 233 :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE` here since I think 234 it's probably more of use to implementors than to users) 235 236Setting :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE` will dump a 237C-like representation to stderr when you compile (GCC's "GIMPLE" 238representation): 239 240.. code-block:: c++ 241 242 ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, 1); 243 result = ctxt.compile (); 244 245.. code-block:: c 246 247 square (signed int i) 248 { 249 signed int D.260; 250 251 entry: 252 D.260 = i * i; 253 return D.260; 254 } 255 256We can see the generated machine code in assembler form (on stderr) by 257setting :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE` on the context 258before compiling: 259 260.. code-block:: c++ 261 262 ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, 1); 263 result = ctxt.compile (); 264 265.. code-block:: gas 266 267 .file "fake.c" 268 .text 269 .globl square 270 .type square, @function 271 square: 272 .LFB6: 273 .cfi_startproc 274 pushq %rbp 275 .cfi_def_cfa_offset 16 276 .cfi_offset 6, -16 277 movq %rsp, %rbp 278 .cfi_def_cfa_register 6 279 movl %edi, -4(%rbp) 280 .L14: 281 movl -4(%rbp), %eax 282 imull -4(%rbp), %eax 283 popq %rbp 284 .cfi_def_cfa 7, 8 285 ret 286 .cfi_endproc 287 .LFE6: 288 .size square, .-square 289 .ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2-0.5.1920c315ff984892399893b380305ab36e07b455.fc20)" 290 .section .note.GNU-stack,"",@progbits 291 292By default, no optimizations are performed, the equivalent of GCC's 293`-O0` option. We can turn things up to e.g. `-O3` by calling 294:func:`gccjit::context::set_int_option` with 295:c:macro:`GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL`: 296 297.. code-block:: c++ 298 299 ctxt.set_int_option (GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3); 300 301.. code-block:: gas 302 303 .file "fake.c" 304 .text 305 .p2align 4,,15 306 .globl square 307 .type square, @function 308 square: 309 .LFB7: 310 .cfi_startproc 311 .L16: 312 movl %edi, %eax 313 imull %edi, %eax 314 ret 315 .cfi_endproc 316 .LFE7: 317 .size square, .-square 318 .ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2-0.5.1920c315ff984892399893b380305ab36e07b455.fc20)" 319 .section .note.GNU-stack,"",@progbits 320 321Naturally this has only a small effect on such a trivial function. 322 323 324Full example 325************ 326 327Here's what the above looks like as a complete program: 328 329 .. literalinclude:: ../../examples/tut02-square.cc 330 :lines: 1- 331 :language: c++ 332 333Building and running it: 334 335.. code-block:: console 336 337 $ gcc \ 338 tut02-square.cc \ 339 -o tut02-square \ 340 -lgccjit 341 342 # Run the built program: 343 $ ./tut02-square 344 result: 25 345