1 .. Copyright (C) 2015-2025 Free Software Foundation, Inc.
2 Originally contributed by David Malcolm <dmalcolm@redhat.com>
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.
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.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see
16 <https://www.gnu.org/licenses/>.
18 Tutorial part 5: Implementing an Ahead-of-Time compiler
19 -------------------------------------------------------
21 If you have a pre-existing language frontend that's compatible with
22 libgccjit's license, it's possible to hook it up to libgccjit as a
23 backend. In the previous example we showed
24 how to do that for in-memory JIT-compilation, but libgccjit can also
25 compile code directly to a file, allowing you to implement a more
26 traditional ahead-of-time compiler ("JIT" is something of a misnomer
29 The essential difference is to compile the context using
30 :c:func:`gcc_jit_context_compile_to_file` rather than
31 :c:func:`gcc_jit_context_compile`.
36 In this example we use libgccjit to construct an ahead-of-time compiler
37 for an esoteric programming language that we shall refer to as "brainf".
39 brainf scripts operate on an array of bytes, with a notional data pointer
42 brainf is hard for humans to read, but it's trivial to write a parser for
43 it, as there is no lexing; just a stream of bytes. The operations are:
60 - ``output (data[idx])``
62 - ``data[idx] = input ()``
64 - loop until ``data[idx] == 0``
70 Unlike the previous example, we'll implement an ahead-of-time compiler,
71 which reads ``.bf`` scripts and outputs executables (though it would
72 be trivial to have it run them JIT-compiled in-process).
74 Here's what a simple ``.bf`` script looks like:
76 .. literalinclude:: ../examples/emit-alphabet.bf
81 This example makes use of whitespace and comments for legibility, but
82 could have been written as::
84 ++++++++++++++++++++++++++
85 >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++<
88 It's not a particularly useful language, except for providing
89 compiler-writers with a test case that's easy to parse. The point
90 is that you can use :c:func:`gcc_jit_context_compile_to_file`
91 to use libgccjit as a backend for a pre-existing language frontend
92 (provided that the pre-existing frontend is compatible with libgccjit's
95 Converting a brainf script to libgccjit IR
96 ******************************************
98 As before we write simple code to populate a :c:expr:`gcc_jit_context *`.
100 .. literalinclude:: ../examples/tut05-bf.c
101 :start-after: #define MAX_OPEN_PARENS 16
102 :end-before: /* Entrypoint to the compiler. */
105 Compiling a context to a file
106 *****************************
108 Unlike the previous tutorial, this time we'll compile the context
109 directly to an executable, using :c:func:`gcc_jit_context_compile_to_file`:
113 gcc_jit_context_compile_to_file (ctxt,
114 GCC_JIT_OUTPUT_KIND_EXECUTABLE,
117 Here's the top-level of the compiler, which is what actually calls into
118 :c:func:`gcc_jit_context_compile_to_file`:
120 .. literalinclude:: ../examples/tut05-bf.c
121 :start-after: /* Entrypoint to the compiler. */
122 :end-before: /* Use the built compiler to compile the example to an executable:
125 Note how once the context is populated you could trivially instead compile
126 it to memory using :c:func:`gcc_jit_context_compile` and run it in-process
127 as in the previous tutorial.
129 To create an executable, we need to export a ``main`` function. Here's
130 how to create one from the JIT API:
132 .. literalinclude:: ../examples/tut05-bf.c
133 :start-after: #include "libgccjit.h"
134 :end-before: #define MAX_OPEN_PARENS 16
139 The above implementation ignores ``argc`` and ``argv``, but you could
140 make use of them by exposing ``param_argc`` and ``param_argv`` to the
143 Upon compiling this C code, we obtain a bf-to-machine-code compiler;
144 let's call it ``bfc``:
146 .. code-block:: console
153 We can now use ``bfc`` to compile .bf files into machine code executables:
155 .. code-block:: console
161 which we can run directly:
163 .. code-block:: console
166 ABCDEFGHIJKLMNOPQRSTUVWXYZ
170 We can also inspect the generated executable using standard tools:
172 .. code-block:: console
174 $ objdump -d a.out |less
176 which shows that libgccjit has managed to optimize the function
177 somewhat (for example, the runs of 26 and 65 increment operations
178 have become integer constants 0x1a and 0x41):
180 .. code-block:: console
182 0000000000400620 <main>:
183 400620: 80 3d 39 0a 20 00 00 cmpb $0x0,0x200a39(%rip) # 601060 <data
184 400627: 74 07 je 400630 <main
185 400629: eb fe jmp 400629 <main+0x9>
186 40062b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
187 400630: 48 83 ec 08 sub $0x8,%rsp
188 400634: 0f b6 05 26 0a 20 00 movzbl 0x200a26(%rip),%eax # 601061 <data_cells+0x1>
189 40063b: c6 05 1e 0a 20 00 1a movb $0x1a,0x200a1e(%rip) # 601060 <data_cells>
190 400642: 8d 78 41 lea 0x41(%rax),%edi
191 400645: 40 88 3d 15 0a 20 00 mov %dil,0x200a15(%rip) # 601061 <data_cells+0x1>
192 40064c: 0f 1f 40 00 nopl 0x0(%rax)
193 400650: 40 0f b6 ff movzbl %dil,%edi
194 400654: e8 87 fe ff ff callq 4004e0 <putchar@plt>
195 400659: 0f b6 05 01 0a 20 00 movzbl 0x200a01(%rip),%eax # 601061 <data_cells+0x1>
196 400660: 80 2d f9 09 20 00 01 subb $0x1,0x2009f9(%rip) # 601060 <data_cells>
197 400667: 8d 78 01 lea 0x1(%rax),%edi
198 40066a: 40 88 3d f0 09 20 00 mov %dil,0x2009f0(%rip) # 601061 <data_cells+0x1>
199 400671: 75 dd jne 400650 <main+0x30>
200 400673: 31 c0 xor %eax,%eax
201 400675: 48 83 c4 08 add $0x8,%rsp
203 40067a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
205 We also set up debugging information (via
206 :c:func:`gcc_jit_context_new_location` and
207 :c:macro:`GCC_JIT_BOOL_OPTION_DEBUGINFO`), so it's possible to use ``gdb``
208 to singlestep through the generated binary and inspect the internal
209 state ``idx`` and ``data_cells``:
211 .. code-block:: console
214 Breakpoint 1 at 0x400790
216 Starting program: a.out
218 Breakpoint 1, 0x0000000000400790 in main (argc=1, argv=0x7fffffffe448)
220 0x0000000000400797 in main (argc=1, argv=0x7fffffffe448)
222 0x00000000004007a0 in main (argc=1, argv=0x7fffffffe448)
224 9 >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++<
228 6 ++++++++++++++++++++++++++
231 9 >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++<
237 6 ++++++++++++++++++++++++++
239 9 >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++<
243 $2 = "\032", '\000' <repeats 29998 times>
244 (gdb) p data_cells[0]
246 (gdb) p data_cells[1]
251 6 ++++++++++++++++++++++++++
254 9 >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++<
261 Other forms of ahead-of-time-compilation
262 ****************************************
264 The above demonstrates compiling a :c:expr:`gcc_jit_context *` directly
265 to an executable. It's also possible to compile it to an object file,
266 and to a dynamic library. See the documentation of
267 :c:func:`gcc_jit_context_compile_to_file` for more information.