xref: /netbsd-src/external/gpl3/gcc.old/dist/gcc/jit/docs/intro/tutorial02.rst (revision 8feb0f0b7eaff0608f8350bbfa3098827b4bb91b)
1*8feb0f0bSmrg.. Copyright (C) 2014-2020 Free Software Foundation, Inc.
236ac495dSmrg   Originally contributed by David Malcolm <dmalcolm@redhat.com>
336ac495dSmrg
436ac495dSmrg   This is free software: you can redistribute it and/or modify it
536ac495dSmrg   under the terms of the GNU General Public License as published by
636ac495dSmrg   the Free Software Foundation, either version 3 of the License, or
736ac495dSmrg   (at your option) any later version.
836ac495dSmrg
936ac495dSmrg   This program is distributed in the hope that it will be useful, but
1036ac495dSmrg   WITHOUT ANY WARRANTY; without even the implied warranty of
1136ac495dSmrg   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1236ac495dSmrg   General Public License for more details.
1336ac495dSmrg
1436ac495dSmrg   You should have received a copy of the GNU General Public License
1536ac495dSmrg   along with this program.  If not, see
1636ac495dSmrg   <http://www.gnu.org/licenses/>.
1736ac495dSmrg
1836ac495dSmrg.. default-domain:: c
1936ac495dSmrg
2036ac495dSmrgTutorial part 2: Creating a trivial machine code function
2136ac495dSmrg---------------------------------------------------------
2236ac495dSmrg
2336ac495dSmrgConsider this C function:
2436ac495dSmrg
2536ac495dSmrg.. code-block:: c
2636ac495dSmrg
2736ac495dSmrg   int square (int i)
2836ac495dSmrg   {
2936ac495dSmrg     return i * i;
3036ac495dSmrg   }
3136ac495dSmrg
3236ac495dSmrgHow can we construct this at run-time using libgccjit?
3336ac495dSmrg
3436ac495dSmrgFirst we need to include the relevant header:
3536ac495dSmrg
3636ac495dSmrg.. code-block:: c
3736ac495dSmrg
3836ac495dSmrg  #include <libgccjit.h>
3936ac495dSmrg
4036ac495dSmrgAll state associated with compilation is associated with a
4136ac495dSmrg:c:type:`gcc_jit_context *`.
4236ac495dSmrg
4336ac495dSmrgCreate one using :c:func:`gcc_jit_context_acquire`:
4436ac495dSmrg
4536ac495dSmrg.. code-block:: c
4636ac495dSmrg
4736ac495dSmrg  gcc_jit_context *ctxt;
4836ac495dSmrg  ctxt = gcc_jit_context_acquire ();
4936ac495dSmrg
5036ac495dSmrgThe JIT library has a system of types.  It is statically-typed: every
5136ac495dSmrgexpression is of a specific type, fixed at compile-time.  In our example,
5236ac495dSmrgall of the expressions are of the C `int` type, so let's obtain this from
5336ac495dSmrgthe context, as a :c:type:`gcc_jit_type *`, using
5436ac495dSmrg:c:func:`gcc_jit_context_get_type`:
5536ac495dSmrg
5636ac495dSmrg.. code-block:: c
5736ac495dSmrg
5836ac495dSmrg  gcc_jit_type *int_type =
5936ac495dSmrg    gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT);
6036ac495dSmrg
6136ac495dSmrg:c:type:`gcc_jit_type *` is an example of a "contextual" object: every
6236ac495dSmrgentity in the API is associated with a :c:type:`gcc_jit_context *`.
6336ac495dSmrg
6436ac495dSmrgMemory management is easy: all such "contextual" objects are automatically
6536ac495dSmrgcleaned up for you when the context is released, using
6636ac495dSmrg:c:func:`gcc_jit_context_release`:
6736ac495dSmrg
6836ac495dSmrg.. code-block:: c
6936ac495dSmrg
7036ac495dSmrg  gcc_jit_context_release (ctxt);
7136ac495dSmrg
7236ac495dSmrgso you don't need to manually track and cleanup all objects, just the
7336ac495dSmrgcontexts.
7436ac495dSmrg
7536ac495dSmrgAlthough the API is C-based, there is a form of class hierarchy, which
7636ac495dSmrglooks like this::
7736ac495dSmrg
7836ac495dSmrg  +- gcc_jit_object
7936ac495dSmrg      +- gcc_jit_location
8036ac495dSmrg      +- gcc_jit_type
8136ac495dSmrg         +- gcc_jit_struct
8236ac495dSmrg      +- gcc_jit_field
8336ac495dSmrg      +- gcc_jit_function
8436ac495dSmrg      +- gcc_jit_block
8536ac495dSmrg      +- gcc_jit_rvalue
8636ac495dSmrg          +- gcc_jit_lvalue
8736ac495dSmrg             +- gcc_jit_param
8836ac495dSmrg
8936ac495dSmrgThere are casting methods for upcasting from subclasses to parent classes.
9036ac495dSmrgFor example, :c:func:`gcc_jit_type_as_object`:
9136ac495dSmrg
9236ac495dSmrg.. code-block:: c
9336ac495dSmrg
9436ac495dSmrg   gcc_jit_object *obj = gcc_jit_type_as_object (int_type);
9536ac495dSmrg
9636ac495dSmrgOne thing you can do with a :c:type:`gcc_jit_object *` is
9736ac495dSmrgto ask it for a human-readable description, using
9836ac495dSmrg:c:func:`gcc_jit_object_get_debug_string`:
9936ac495dSmrg
10036ac495dSmrg.. code-block:: c
10136ac495dSmrg
10236ac495dSmrg   printf ("obj: %s\n", gcc_jit_object_get_debug_string (obj));
10336ac495dSmrg
10436ac495dSmrggiving this text on stdout:
10536ac495dSmrg
10636ac495dSmrg.. code-block:: bash
10736ac495dSmrg
10836ac495dSmrg   obj: int
10936ac495dSmrg
11036ac495dSmrgThis is invaluable when debugging.
11136ac495dSmrg
11236ac495dSmrgLet's create the function.  To do so, we first need to construct
11336ac495dSmrgits single parameter, specifying its type and giving it a name,
11436ac495dSmrgusing :c:func:`gcc_jit_context_new_param`:
11536ac495dSmrg
11636ac495dSmrg.. code-block:: c
11736ac495dSmrg
11836ac495dSmrg  gcc_jit_param *param_i =
11936ac495dSmrg    gcc_jit_context_new_param (ctxt, NULL, int_type, "i");
12036ac495dSmrg
12136ac495dSmrgNow we can create the function, using
12236ac495dSmrg:c:func:`gcc_jit_context_new_function`:
12336ac495dSmrg
12436ac495dSmrg.. code-block:: c
12536ac495dSmrg
12636ac495dSmrg  gcc_jit_function *func =
12736ac495dSmrg    gcc_jit_context_new_function (ctxt, NULL,
12836ac495dSmrg                                  GCC_JIT_FUNCTION_EXPORTED,
12936ac495dSmrg                                  int_type,
13036ac495dSmrg                                  "square",
13136ac495dSmrg                                  1, &param_i,
13236ac495dSmrg                                  0);
13336ac495dSmrg
13436ac495dSmrgTo define the code within the function, we must create basic blocks
13536ac495dSmrgcontaining statements.
13636ac495dSmrg
13736ac495dSmrgEvery basic block contains a list of statements, eventually terminated
13836ac495dSmrgby a statement that either returns, or jumps to another basic block.
13936ac495dSmrg
14036ac495dSmrgOur function has no control-flow, so we just need one basic block:
14136ac495dSmrg
14236ac495dSmrg.. code-block:: c
14336ac495dSmrg
14436ac495dSmrg  gcc_jit_block *block = gcc_jit_function_new_block (func, NULL);
14536ac495dSmrg
14636ac495dSmrgOur basic block is relatively simple: it immediately terminates by
14736ac495dSmrgreturning the value of an expression.
14836ac495dSmrg
14936ac495dSmrgWe can build the expression using :c:func:`gcc_jit_context_new_binary_op`:
15036ac495dSmrg
15136ac495dSmrg.. code-block:: c
15236ac495dSmrg
15336ac495dSmrg   gcc_jit_rvalue *expr =
15436ac495dSmrg     gcc_jit_context_new_binary_op (
15536ac495dSmrg       ctxt, NULL,
15636ac495dSmrg       GCC_JIT_BINARY_OP_MULT, int_type,
15736ac495dSmrg       gcc_jit_param_as_rvalue (param_i),
15836ac495dSmrg       gcc_jit_param_as_rvalue (param_i));
15936ac495dSmrg
16036ac495dSmrgA :c:type:`gcc_jit_rvalue *` is another example of a
16136ac495dSmrg:c:type:`gcc_jit_object *` subclass.  We can upcast it using
16236ac495dSmrg:c:func:`gcc_jit_rvalue_as_object` and as before print it with
16336ac495dSmrg:c:func:`gcc_jit_object_get_debug_string`.
16436ac495dSmrg
16536ac495dSmrg.. code-block:: c
16636ac495dSmrg
16736ac495dSmrg   printf ("expr: %s\n",
16836ac495dSmrg           gcc_jit_object_get_debug_string (
16936ac495dSmrg             gcc_jit_rvalue_as_object (expr)));
17036ac495dSmrg
17136ac495dSmrggiving this output:
17236ac495dSmrg
17336ac495dSmrg.. code-block:: bash
17436ac495dSmrg
17536ac495dSmrg   expr: i * i
17636ac495dSmrg
17736ac495dSmrgCreating the expression in itself doesn't do anything; we have to add
17836ac495dSmrgthis expression to a statement within the block.  In this case, we use it
17936ac495dSmrgto build a return statement, which terminates the basic block:
18036ac495dSmrg
18136ac495dSmrg.. code-block:: c
18236ac495dSmrg
18336ac495dSmrg  gcc_jit_block_end_with_return (block, NULL, expr);
18436ac495dSmrg
18536ac495dSmrgOK, we've populated the context.  We can now compile it using
18636ac495dSmrg:c:func:`gcc_jit_context_compile`:
18736ac495dSmrg
18836ac495dSmrg.. code-block:: c
18936ac495dSmrg
19036ac495dSmrg   gcc_jit_result *result;
19136ac495dSmrg   result = gcc_jit_context_compile (ctxt);
19236ac495dSmrg
19336ac495dSmrgand get a :c:type:`gcc_jit_result *`.
19436ac495dSmrg
19536ac495dSmrgAt this point we're done with the context; we can release it:
19636ac495dSmrg
19736ac495dSmrg.. code-block:: c
19836ac495dSmrg
19936ac495dSmrg   gcc_jit_context_release (ctxt);
20036ac495dSmrg
20136ac495dSmrgWe can now use :c:func:`gcc_jit_result_get_code` to look up a specific
20236ac495dSmrgmachine code routine within the result, in this case, the function we
20336ac495dSmrgcreated above.
20436ac495dSmrg
20536ac495dSmrg.. code-block:: c
20636ac495dSmrg
20736ac495dSmrg   void *fn_ptr = gcc_jit_result_get_code (result, "square");
20836ac495dSmrg   if (!fn_ptr)
20936ac495dSmrg     {
21036ac495dSmrg       fprintf (stderr, "NULL fn_ptr");
21136ac495dSmrg       goto error;
21236ac495dSmrg     }
21336ac495dSmrg
21436ac495dSmrgWe can now cast the pointer to an appropriate function pointer type, and
21536ac495dSmrgthen call it:
21636ac495dSmrg
21736ac495dSmrg.. code-block:: c
21836ac495dSmrg
21936ac495dSmrg  typedef int (*fn_type) (int);
22036ac495dSmrg  fn_type square = (fn_type)fn_ptr;
22136ac495dSmrg  printf ("result: %d", square (5));
22236ac495dSmrg
22336ac495dSmrg.. code-block:: bash
22436ac495dSmrg
22536ac495dSmrg  result: 25
22636ac495dSmrg
22736ac495dSmrgOnce we're done with the code, we can release the result:
22836ac495dSmrg
22936ac495dSmrg.. code-block:: c
23036ac495dSmrg
23136ac495dSmrg   gcc_jit_result_release (result);
23236ac495dSmrg
23336ac495dSmrgWe can't call ``square`` anymore once we've released ``result``.
23436ac495dSmrg
23536ac495dSmrg
23636ac495dSmrgError-handling
23736ac495dSmrg**************
23836ac495dSmrgVarious kinds of errors are possible when using the API, such as
23936ac495dSmrgmismatched types in an assignment.  You can only compile and get code
24036ac495dSmrgfrom a context if no errors occur.
24136ac495dSmrg
24236ac495dSmrgErrors are printed on stderr; they typically contain the name of the API
24336ac495dSmrgentrypoint where the error occurred, and pertinent information on the
24436ac495dSmrgproblem:
24536ac495dSmrg
24636ac495dSmrg.. code-block:: console
24736ac495dSmrg
24836ac495dSmrg  ./buggy-program: error: gcc_jit_block_add_assignment: mismatching types: assignment to i (type: int) from "hello world" (type: const char *)
24936ac495dSmrg
25036ac495dSmrgThe API is designed to cope with errors without crashing, so you can get
25136ac495dSmrgaway with having a single error-handling check in your code:
25236ac495dSmrg
25336ac495dSmrg.. code-block:: c
25436ac495dSmrg
25536ac495dSmrg   void *fn_ptr = gcc_jit_result_get_code (result, "square");
25636ac495dSmrg   if (!fn_ptr)
25736ac495dSmrg     {
25836ac495dSmrg       fprintf (stderr, "NULL fn_ptr");
25936ac495dSmrg       goto error;
26036ac495dSmrg     }
26136ac495dSmrg
26236ac495dSmrgFor more information, see the :ref:`error-handling guide <error-handling>`
26336ac495dSmrgwithin the Topic eference.
26436ac495dSmrg
26536ac495dSmrg
26636ac495dSmrgOptions
26736ac495dSmrg*******
26836ac495dSmrg
26936ac495dSmrgTo get more information on what's going on, you can set debugging flags
27036ac495dSmrgon the context using :c:func:`gcc_jit_context_set_bool_option`.
27136ac495dSmrg
27236ac495dSmrg.. (I'm deliberately not mentioning
27336ac495dSmrg    :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE` here since I think
27436ac495dSmrg    it's probably more of use to implementors than to users)
27536ac495dSmrg
27636ac495dSmrgSetting :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE` will dump a
27736ac495dSmrgC-like representation to stderr when you compile (GCC's "GIMPLE"
27836ac495dSmrgrepresentation):
27936ac495dSmrg
28036ac495dSmrg.. code-block:: c
28136ac495dSmrg
28236ac495dSmrg   gcc_jit_context_set_bool_option (
28336ac495dSmrg     ctxt,
28436ac495dSmrg     GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE,
28536ac495dSmrg     1);
28636ac495dSmrg   result = gcc_jit_context_compile (ctxt);
28736ac495dSmrg
28836ac495dSmrg.. code-block:: c
28936ac495dSmrg
29036ac495dSmrg  square (signed int i)
29136ac495dSmrg  {
29236ac495dSmrg    signed int D.260;
29336ac495dSmrg
29436ac495dSmrg    entry:
29536ac495dSmrg    D.260 = i * i;
29636ac495dSmrg    return D.260;
29736ac495dSmrg  }
29836ac495dSmrg
29936ac495dSmrgWe can see the generated machine code in assembler form (on stderr) by
30036ac495dSmrgsetting :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE` on the context
30136ac495dSmrgbefore compiling:
30236ac495dSmrg
30336ac495dSmrg.. code-block:: c
30436ac495dSmrg
30536ac495dSmrg  gcc_jit_context_set_bool_option (
30636ac495dSmrg    ctxt,
30736ac495dSmrg    GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE,
30836ac495dSmrg    1);
30936ac495dSmrg  result = gcc_jit_context_compile (ctxt);
31036ac495dSmrg
31136ac495dSmrg.. code-block:: gas
31236ac495dSmrg
31336ac495dSmrg        .file   "fake.c"
31436ac495dSmrg        .text
31536ac495dSmrg        .globl  square
31636ac495dSmrg        .type   square, @function
31736ac495dSmrg  square:
31836ac495dSmrg  .LFB6:
31936ac495dSmrg        .cfi_startproc
32036ac495dSmrg        pushq   %rbp
32136ac495dSmrg        .cfi_def_cfa_offset 16
32236ac495dSmrg        .cfi_offset 6, -16
32336ac495dSmrg        movq    %rsp, %rbp
32436ac495dSmrg        .cfi_def_cfa_register 6
32536ac495dSmrg        movl    %edi, -4(%rbp)
32636ac495dSmrg  .L14:
32736ac495dSmrg        movl    -4(%rbp), %eax
32836ac495dSmrg        imull   -4(%rbp), %eax
32936ac495dSmrg        popq    %rbp
33036ac495dSmrg        .cfi_def_cfa 7, 8
33136ac495dSmrg        ret
33236ac495dSmrg        .cfi_endproc
33336ac495dSmrg  .LFE6:
33436ac495dSmrg        .size   square, .-square
33536ac495dSmrg        .ident  "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2-0.5.1920c315ff984892399893b380305ab36e07b455.fc20)"
33636ac495dSmrg        .section       .note.GNU-stack,"",@progbits
33736ac495dSmrg
33836ac495dSmrgBy default, no optimizations are performed, the equivalent of GCC's
33936ac495dSmrg`-O0` option.  We can turn things up to e.g. `-O3` by calling
34036ac495dSmrg:c:func:`gcc_jit_context_set_int_option` with
34136ac495dSmrg:c:macro:`GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL`:
34236ac495dSmrg
34336ac495dSmrg.. code-block:: c
34436ac495dSmrg
34536ac495dSmrg  gcc_jit_context_set_int_option (
34636ac495dSmrg    ctxt,
34736ac495dSmrg    GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL,
34836ac495dSmrg    3);
34936ac495dSmrg
35036ac495dSmrg.. code-block:: gas
35136ac495dSmrg
35236ac495dSmrg        .file   "fake.c"
35336ac495dSmrg        .text
35436ac495dSmrg        .p2align 4,,15
35536ac495dSmrg        .globl  square
35636ac495dSmrg        .type   square, @function
35736ac495dSmrg  square:
35836ac495dSmrg  .LFB7:
35936ac495dSmrg        .cfi_startproc
36036ac495dSmrg  .L16:
36136ac495dSmrg        movl    %edi, %eax
36236ac495dSmrg        imull   %edi, %eax
36336ac495dSmrg        ret
36436ac495dSmrg        .cfi_endproc
36536ac495dSmrg  .LFE7:
36636ac495dSmrg        .size   square, .-square
36736ac495dSmrg        .ident  "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2-0.5.1920c315ff984892399893b380305ab36e07b455.fc20)"
36836ac495dSmrg        .section        .note.GNU-stack,"",@progbits
36936ac495dSmrg
37036ac495dSmrgNaturally this has only a small effect on such a trivial function.
37136ac495dSmrg
37236ac495dSmrg
37336ac495dSmrgFull example
37436ac495dSmrg************
37536ac495dSmrg
37636ac495dSmrgHere's what the above looks like as a complete program:
37736ac495dSmrg
37836ac495dSmrg   .. literalinclude:: ../examples/tut02-square.c
37936ac495dSmrg    :lines: 1-
38036ac495dSmrg    :language: c
38136ac495dSmrg
38236ac495dSmrgBuilding and running it:
38336ac495dSmrg
38436ac495dSmrg.. code-block:: console
38536ac495dSmrg
38636ac495dSmrg  $ gcc \
38736ac495dSmrg      tut02-square.c \
38836ac495dSmrg      -o tut02-square \
38936ac495dSmrg      -lgccjit
39036ac495dSmrg
39136ac495dSmrg  # Run the built program:
39236ac495dSmrg  $ ./tut02-square
39336ac495dSmrg  result: 25
394