\input texinfo @c -*-texinfo-*- @c %**start of header @setfilename libgccjit.info @documentencoding UTF-8 @ifinfo @*Generated by Sphinx 1.1.3.@* @end ifinfo @settitle libgccjit Documentation @defindex ge @paragraphindent 2 @exampleindent 4 @afourlatex @dircategory Miscellaneous @direntry * libgccjit: (libgccjit.info). One line description of project. @end direntry @c %**end of header @copying @quotation libgccjit 5.0.0 (experimental 20150313), March 13, 2015 David Malcolm Copyright @copyright{} 2014-2015 Free Software Foundation, Inc. @end quotation @end copying @titlepage @title libgccjit Documentation @insertcopying @end titlepage @contents @c %** start of user preamble @c %** end of user preamble @ifnottex @node Top @top libgccjit Documentation @insertcopying @end ifnottex @c %**start of body @anchor{index doc}@anchor{0} @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . This document describes libgccjit@footnote{http://gcc.gnu.org/wiki/JIT}, an API for embedding GCC inside programs and libraries. Note that libgccjit is currently of "Alpha" quality; the APIs are not yet set in stone, and they shouldn't be used in production yet. There are actually two APIs for the library: @itemize * @item a pure C API: @code{libgccjit.h} @item a C++ wrapper API: @code{libgccjit++.h}. This is a collection of "thin" wrapper classes around the C API, to save typing. @end itemize Contents: @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @menu * Tutorial:: * Topic Reference:: * C++ bindings for libgccjit:: * Internals:: * Indices and tables:: * Index:: @detailmenu --- The Detailed Node Listing --- Tutorial * Tutorial part 1; "Hello world": Tutorial part 1 "Hello world". * Tutorial part 2; Creating a trivial machine code function: Tutorial part 2 Creating a trivial machine code function. * Tutorial part 3; Loops and variables: Tutorial part 3 Loops and variables. * Tutorial part 4; Adding JIT-compilation to a toy interpreter: Tutorial part 4 Adding JIT-compilation to a toy interpreter. * Tutorial part 5; Implementing an Ahead-of-Time compiler: Tutorial part 5 Implementing an Ahead-of-Time compiler. Tutorial part 2: Creating a trivial machine code function * Error-handling:: * Options:: * Full example:: Tutorial part 3: Loops and variables * Expressions; lvalues and rvalues: Expressions lvalues and rvalues. * Control flow:: * Visualizing the control flow graph:: * Full example: Full example<2>. Tutorial part 4: Adding JIT-compilation to a toy interpreter * Our toy interpreter:: * Compiling to machine code:: * Setting things up:: * Populating the function:: * Verifying the control flow graph:: * Compiling the context:: * Single-stepping through the generated code:: * Examining the generated code:: * Putting it all together:: * Behind the curtain; How does our code get optimized?: Behind the curtain How does our code get optimized?. Behind the curtain: How does our code get optimized? * Optimizing away stack manipulation:: * Elimination of tail recursion:: Tutorial part 5: Implementing an Ahead-of-Time compiler * The "brainf" language:: * Converting a brainf script to libgccjit IR:: * Compiling a context to a file:: * Other forms of ahead-of-time-compilation:: Topic Reference * Compilation contexts:: * Objects:: * Types:: * Expressions:: * Creating and using functions:: * Source Locations:: * Compiling a context:: Compilation contexts * Lifetime-management:: * Thread-safety:: * Error-handling: Error-handling<2>. * Debugging:: * Options: Options<2>. Options * String Options:: * Boolean options:: * Integer options:: Types * Standard types:: * Pointers@comma{} const@comma{} and volatile: Pointers const and volatile. * Structures and unions:: Expressions * Rvalues:: * Lvalues:: * Working with pointers@comma{} structs and unions: Working with pointers structs and unions. Rvalues * Simple expressions:: * Unary Operations:: * Binary Operations:: * Comparisons:: * Function calls:: * Type-coercion:: Lvalues * Global variables:: Creating and using functions * Params:: * Functions:: * Blocks:: * Statements:: Source Locations * Faking it:: Compiling a context * In-memory compilation:: * Ahead-of-time compilation:: C++ bindings for libgccjit * Tutorial: Tutorial<2>. * Topic Reference: Topic Reference<2>. Tutorial * Tutorial part 1; "Hello world": Tutorial part 1 "Hello world"<2>. * Tutorial part 2; Creating a trivial machine code function: Tutorial part 2 Creating a trivial machine code function<2>. * Tutorial part 3; Loops and variables: Tutorial part 3 Loops and variables<2>. * Tutorial part 4; Adding JIT-compilation to a toy interpreter: Tutorial part 4 Adding JIT-compilation to a toy interpreter<2>. Tutorial part 2: Creating a trivial machine code function * Options: Options<3>. * Full example: Full example<3>. Tutorial part 3: Loops and variables * Expressions; lvalues and rvalues: Expressions lvalues and rvalues<2>. * Control flow: Control flow<2>. * Visualizing the control flow graph: Visualizing the control flow graph<2>. * Full example: Full example<4>. Tutorial part 4: Adding JIT-compilation to a toy interpreter * Our toy interpreter: Our toy interpreter<2>. * Compiling to machine code: Compiling to machine code<2>. * Setting things up: Setting things up<2>. * Populating the function: Populating the function<2>. * Verifying the control flow graph: Verifying the control flow graph<2>. * Compiling the context: Compiling the context<2>. * Single-stepping through the generated code: Single-stepping through the generated code<2>. * Examining the generated code: Examining the generated code<2>. * Putting it all together: Putting it all together<2>. * Behind the curtain; How does our code get optimized?: Behind the curtain How does our code get optimized?<2>. Behind the curtain: How does our code get optimized? * Optimizing away stack manipulation: Optimizing away stack manipulation<2>. * Elimination of tail recursion: Elimination of tail recursion<2>. Topic Reference * Compilation contexts: Compilation contexts<2>. * Objects: Objects<2>. * Types: Types<2>. * Expressions: Expressions<2>. * Creating and using functions: Creating and using functions<2>. * Source Locations: Source Locations<2>. * Compiling a context: Compiling a context<2>. Compilation contexts * Lifetime-management: Lifetime-management<2>. * Thread-safety: Thread-safety<2>. * Error-handling: Error-handling<3>. * Debugging: Debugging<2>. * Options: Options<4>. Options * String Options: String Options<2>. * Boolean options: Boolean options<2>. * Integer options: Integer options<2>. Types * Standard types: Standard types<2>. * Pointers@comma{} const@comma{} and volatile: Pointers const and volatile<2>. * Structures and unions: Structures and unions<2>. Expressions * Rvalues: Rvalues<2>. * Lvalues: Lvalues<2>. * Working with pointers@comma{} structs and unions: Working with pointers structs and unions<2>. Rvalues * Simple expressions: Simple expressions<2>. * Unary Operations: Unary Operations<2>. * Binary Operations: Binary Operations<2>. * Comparisons: Comparisons<2>. * Function calls: Function calls<2>. * Type-coercion: Type-coercion<2>. Lvalues * Global variables: Global variables<2>. Creating and using functions * Params: Params<2>. * Functions: Functions<2>. * Blocks: Blocks<2>. * Statements: Statements<2>. Source Locations * Faking it: Faking it<2>. Compiling a context * In-memory compilation: In-memory compilation<2>. * Ahead-of-time compilation: Ahead-of-time compilation<2>. Internals * Working on the JIT library:: * Running the test suite:: * Environment variables:: * Packaging notes:: * Overview of code structure:: * Design notes:: Running the test suite * Running under valgrind:: @end detailmenu @end menu @node Tutorial,Topic Reference,Top,Top @anchor{intro/index libgccjit}@anchor{1}@anchor{intro/index doc}@anchor{2}@anchor{intro/index tutorial}@anchor{3} @chapter Tutorial @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @menu * Tutorial part 1; "Hello world": Tutorial part 1 "Hello world". * Tutorial part 2; Creating a trivial machine code function: Tutorial part 2 Creating a trivial machine code function. * Tutorial part 3; Loops and variables: Tutorial part 3 Loops and variables. * Tutorial part 4; Adding JIT-compilation to a toy interpreter: Tutorial part 4 Adding JIT-compilation to a toy interpreter. * Tutorial part 5; Implementing an Ahead-of-Time compiler: Tutorial part 5 Implementing an Ahead-of-Time compiler. @end menu @node Tutorial part 1 "Hello world",Tutorial part 2 Creating a trivial machine code function,,Tutorial @anchor{intro/tutorial01 doc}@anchor{4}@anchor{intro/tutorial01 tutorial-part-1-hello-world}@anchor{5} @section Tutorial part 1: "Hello world" Before we look at the details of the API, let's look at building and running programs that use the library. Here's a toy "hello world" program that uses the library to synthesize a call to @cite{printf} and uses it to write a message to stdout. Don't worry about the content of the program for now; we'll cover the details in later parts of this tutorial. @quotation @example /* Smoketest example for libgccjit.so Copyright (C) 2014-2015 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include #include #include static void create_code (gcc_jit_context *ctxt) @{ /* Let's try to inject the equivalent of: void greet (const char *name) @{ printf ("hello %s\n", name); @} */ gcc_jit_type *void_type = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_VOID); gcc_jit_type *const_char_ptr_type = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_CONST_CHAR_PTR); gcc_jit_param *param_name = gcc_jit_context_new_param (ctxt, NULL, const_char_ptr_type, "name"); gcc_jit_function *func = gcc_jit_context_new_function (ctxt, NULL, GCC_JIT_FUNCTION_EXPORTED, void_type, "greet", 1, ¶m_name, 0); gcc_jit_param *param_format = gcc_jit_context_new_param (ctxt, NULL, const_char_ptr_type, "format"); gcc_jit_function *printf_func = gcc_jit_context_new_function (ctxt, NULL, GCC_JIT_FUNCTION_IMPORTED, gcc_jit_context_get_type ( ctxt, GCC_JIT_TYPE_INT), "printf", 1, ¶m_format, 1); gcc_jit_rvalue *args[2]; args[0] = gcc_jit_context_new_string_literal (ctxt, "hello %s\n"); args[1] = gcc_jit_param_as_rvalue (param_name); gcc_jit_block *block = gcc_jit_function_new_block (func, NULL); gcc_jit_block_add_eval ( block, NULL, gcc_jit_context_new_call (ctxt, NULL, printf_func, 2, args)); gcc_jit_block_end_with_void_return (block, NULL); @} int main (int argc, char **argv) @{ gcc_jit_context *ctxt; gcc_jit_result *result; /* Get a "context" object for working with the library. */ ctxt = gcc_jit_context_acquire (); if (!ctxt) @{ fprintf (stderr, "NULL ctxt"); exit (1); @} /* Set some options on the context. Let's see the code being generated, in assembler form. */ gcc_jit_context_set_bool_option ( ctxt, GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, 0); /* Populate the context. */ create_code (ctxt); /* Compile the code. */ result = gcc_jit_context_compile (ctxt); if (!result) @{ fprintf (stderr, "NULL result"); exit (1); @} /* Extract the generated code from "result". */ typedef void (*fn_type) (const char *); fn_type greet = (fn_type)gcc_jit_result_get_code (result, "greet"); if (!greet) @{ fprintf (stderr, "NULL greet"); exit (1); @} /* Now call the generated function: */ greet ("world"); fflush (stdout); gcc_jit_context_release (ctxt); gcc_jit_result_release (result); return 0; @} @end example @noindent @end quotation Copy the above to @cite{tut01-hello-world.c}. Assuming you have the jit library installed, build the test program using: @example $ gcc \ tut01-hello-world.c \ -o tut01-hello-world \ -lgccjit @end example @noindent You should then be able to run the built program: @example $ ./tut01-hello-world hello world @end example @noindent @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Tutorial part 2 Creating a trivial machine code function,Tutorial part 3 Loops and variables,Tutorial part 1 "Hello world",Tutorial @anchor{intro/tutorial02 doc}@anchor{6}@anchor{intro/tutorial02 tutorial-part-2-creating-a-trivial-machine-code-function}@anchor{7} @section Tutorial part 2: Creating a trivial machine code function Consider this C function: @example int square (int i) @{ return i * i; @} @end example @noindent How can we construct this at run-time using libgccjit? First we need to include the relevant header: @example #include @end example @noindent All state associated with compilation is associated with a @pxref{8,,gcc_jit_context *}. Create one using @pxref{9,,gcc_jit_context_acquire()}: @example gcc_jit_context *ctxt; ctxt = gcc_jit_context_acquire (); @end example @noindent The JIT library has a system of types. It is statically-typed: every expression is of a specific type, fixed at compile-time. In our example, all of the expressions are of the C @cite{int} type, so let's obtain this from the context, as a @pxref{a,,gcc_jit_type *}, using @pxref{b,,gcc_jit_context_get_type()}: @example gcc_jit_type *int_type = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); @end example @noindent @pxref{a,,gcc_jit_type *} is an example of a "contextual" object: every entity in the API is associated with a @pxref{8,,gcc_jit_context *}. Memory management is easy: all such "contextual" objects are automatically cleaned up for you when the context is released, using @pxref{c,,gcc_jit_context_release()}: @example gcc_jit_context_release (ctxt); @end example @noindent so you don't need to manually track and cleanup all objects, just the contexts. Although the API is C-based, there is a form of class hierarchy, which looks like this: @example +- gcc_jit_object +- gcc_jit_location +- gcc_jit_type +- gcc_jit_struct +- gcc_jit_field +- gcc_jit_function +- gcc_jit_block +- gcc_jit_rvalue +- gcc_jit_lvalue +- gcc_jit_param @end example @noindent There are casting methods for upcasting from subclasses to parent classes. For example, @pxref{d,,gcc_jit_type_as_object()}: @example gcc_jit_object *obj = gcc_jit_type_as_object (int_type); @end example @noindent One thing you can do with a @pxref{e,,gcc_jit_object *} is to ask it for a human-readable description, using @pxref{f,,gcc_jit_object_get_debug_string()}: @example printf ("obj: %s\n", gcc_jit_object_get_debug_string (obj)); @end example @noindent giving this text on stdout: @example obj: int @end example @noindent This is invaluable when debugging. Let's create the function. To do so, we first need to construct its single parameter, specifying its type and giving it a name, using @pxref{10,,gcc_jit_context_new_param()}: @example gcc_jit_param *param_i = gcc_jit_context_new_param (ctxt, NULL, int_type, "i"); @end example @noindent Now we can create the function, using @pxref{11,,gcc_jit_context_new_function()}: @example gcc_jit_function *func = gcc_jit_context_new_function (ctxt, NULL, GCC_JIT_FUNCTION_EXPORTED, int_type, "square", 1, ¶m_i, 0); @end example @noindent To define the code within the function, we must create basic blocks containing statements. Every basic block contains a list of statements, eventually terminated by a statement that either returns, or jumps to another basic block. Our function has no control-flow, so we just need one basic block: @example gcc_jit_block *block = gcc_jit_function_new_block (func, NULL); @end example @noindent Our basic block is relatively simple: it immediately terminates by returning the value of an expression. We can build the expression using @pxref{12,,gcc_jit_context_new_binary_op()}: @example gcc_jit_rvalue *expr = gcc_jit_context_new_binary_op ( ctxt, NULL, GCC_JIT_BINARY_OP_MULT, int_type, gcc_jit_param_as_rvalue (param_i), gcc_jit_param_as_rvalue (param_i)); @end example @noindent A @pxref{13,,gcc_jit_rvalue *} is another example of a @pxref{e,,gcc_jit_object *} subclass. We can upcast it using @pxref{14,,gcc_jit_rvalue_as_object()} and as before print it with @pxref{f,,gcc_jit_object_get_debug_string()}. @example printf ("expr: %s\n", gcc_jit_object_get_debug_string ( gcc_jit_rvalue_as_object (expr))); @end example @noindent giving this output: @example expr: i * i @end example @noindent Creating the expression in itself doesn't do anything; we have to add this expression to a statement within the block. In this case, we use it to build a return statement, which terminates the basic block: @example gcc_jit_block_end_with_return (block, NULL, expr); @end example @noindent OK, we've populated the context. We can now compile it using @pxref{15,,gcc_jit_context_compile()}: @example gcc_jit_result *result; result = gcc_jit_context_compile (ctxt); @end example @noindent and get a @pxref{16,,gcc_jit_result *}. At this point we're done with the context; we can release it: @example gcc_jit_context_release (ctxt); @end example @noindent We can now use @pxref{17,,gcc_jit_result_get_code()} to look up a specific machine code routine within the result, in this case, the function we created above. @example void *fn_ptr = gcc_jit_result_get_code (result, "square"); if (!fn_ptr) @{ fprintf (stderr, "NULL fn_ptr"); goto error; @} @end example @noindent We can now cast the pointer to an appropriate function pointer type, and then call it: @example typedef int (*fn_type) (int); fn_type square = (fn_type)fn_ptr; printf ("result: %d", square (5)); @end example @noindent @example result: 25 @end example @noindent Once we're done with the code, we can release the result: @example gcc_jit_result_release (result); @end example @noindent We can't call @code{square} anymore once we've released @code{result}. @menu * Error-handling:: * Options:: * Full example:: @end menu @node Error-handling,Options,,Tutorial part 2 Creating a trivial machine code function @anchor{intro/tutorial02 error-handling}@anchor{18} @subsection Error-handling Various kinds of errors are possible when using the API, such as mismatched types in an assignment. You can only compile and get code from a context if no errors occur. Errors are printed on stderr; they typically contain the name of the API entrypoint where the error occurred, and pertinent information on the problem: @example ./buggy-program: error: gcc_jit_block_add_assignment: mismatching types: assignment to i (type: int) from "hello world" (type: const char *) @end example @noindent The API is designed to cope with errors without crashing, so you can get away with having a single error-handling check in your code: @example void *fn_ptr = gcc_jit_result_get_code (result, "square"); if (!fn_ptr) @{ fprintf (stderr, "NULL fn_ptr"); goto error; @} @end example @noindent For more information, see the @pxref{19,,error-handling guide} within the Topic eference. @node Options,Full example,Error-handling,Tutorial part 2 Creating a trivial machine code function @anchor{intro/tutorial02 options}@anchor{1a} @subsection Options To get more information on what's going on, you can set debugging flags on the context using @pxref{1b,,gcc_jit_context_set_bool_option()}. @c (I'm deliberately not mentioning @c :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE` here since I think @c it's probably more of use to implementors than to users) Setting @pxref{1c,,GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE} will dump a C-like representation to stderr when you compile (GCC's "GIMPLE" representation): @example gcc_jit_context_set_bool_option ( ctxt, GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, 1); result = gcc_jit_context_compile (ctxt); @end example @noindent @example square (signed int i) @{ signed int D.260; entry: D.260 = i * i; return D.260; @} @end example @noindent We can see the generated machine code in assembler form (on stderr) by setting @pxref{1d,,GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE} on the context before compiling: @example gcc_jit_context_set_bool_option ( ctxt, GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, 1); result = gcc_jit_context_compile (ctxt); @end example @noindent @example .file "fake.c" .text .globl square .type square, @@function square: .LFB6: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl %edi, -4(%rbp) .L14: movl -4(%rbp), %eax imull -4(%rbp), %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE6: .size square, .-square .ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2-0.5.1920c315ff984892399893b380305ab36e07b455.fc20)" .section .note.GNU-stack,"",@@progbits @end example @noindent By default, no optimizations are performed, the equivalent of GCC's @cite{-O0} option. We can turn things up to e.g. @cite{-O3} by calling @pxref{1e,,gcc_jit_context_set_int_option()} with @pxref{1f,,GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL}: @example gcc_jit_context_set_int_option ( ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3); @end example @noindent @example .file "fake.c" .text .p2align 4,,15 .globl square .type square, @@function square: .LFB7: .cfi_startproc .L16: movl %edi, %eax imull %edi, %eax ret .cfi_endproc .LFE7: .size square, .-square .ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2-0.5.1920c315ff984892399893b380305ab36e07b455.fc20)" .section .note.GNU-stack,"",@@progbits @end example @noindent Naturally this has only a small effect on such a trivial function. @node Full example,,Options,Tutorial part 2 Creating a trivial machine code function @anchor{intro/tutorial02 full-example}@anchor{20} @subsection Full example Here's what the above looks like as a complete program: @quotation @example /* Usage example for libgccjit.so Copyright (C) 2014-2015 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include #include #include void create_code (gcc_jit_context *ctxt) @{ /* Let's try to inject the equivalent of: int square (int i) @{ return i * i; @} */ gcc_jit_type *int_type = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); gcc_jit_param *param_i = gcc_jit_context_new_param (ctxt, NULL, int_type, "i"); gcc_jit_function *func = gcc_jit_context_new_function (ctxt, NULL, GCC_JIT_FUNCTION_EXPORTED, int_type, "square", 1, ¶m_i, 0); gcc_jit_block *block = gcc_jit_function_new_block (func, NULL); gcc_jit_rvalue *expr = gcc_jit_context_new_binary_op ( ctxt, NULL, GCC_JIT_BINARY_OP_MULT, int_type, gcc_jit_param_as_rvalue (param_i), gcc_jit_param_as_rvalue (param_i)); gcc_jit_block_end_with_return (block, NULL, expr); @} int main (int argc, char **argv) @{ gcc_jit_context *ctxt = NULL; gcc_jit_result *result = NULL; /* Get a "context" object for working with the library. */ ctxt = gcc_jit_context_acquire (); if (!ctxt) @{ fprintf (stderr, "NULL ctxt"); goto error; @} /* Set some options on the context. Let's see the code being generated, in assembler form. */ gcc_jit_context_set_bool_option ( ctxt, GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, 0); /* Populate the context. */ create_code (ctxt); /* Compile the code. */ result = gcc_jit_context_compile (ctxt); if (!result) @{ fprintf (stderr, "NULL result"); goto error; @} /* We're done with the context; we can release it: */ gcc_jit_context_release (ctxt); ctxt = NULL; /* Extract the generated code from "result". */ void *fn_ptr = gcc_jit_result_get_code (result, "square"); if (!fn_ptr) @{ fprintf (stderr, "NULL fn_ptr"); goto error; @} typedef int (*fn_type) (int); fn_type square = (fn_type)fn_ptr; printf ("result: %d\n", square (5)); error: if (ctxt) gcc_jit_context_release (ctxt); if (result) gcc_jit_result_release (result); return 0; @} @end example @noindent @end quotation Building and running it: @example $ gcc \ tut02-square.c \ -o tut02-square \ -lgccjit # Run the built program: $ ./tut02-square result: 25 @end example @noindent @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Tutorial part 3 Loops and variables,Tutorial part 4 Adding JIT-compilation to a toy interpreter,Tutorial part 2 Creating a trivial machine code function,Tutorial @anchor{intro/tutorial03 tutorial-part-3-loops-and-variables}@anchor{21}@anchor{intro/tutorial03 doc}@anchor{22} @section Tutorial part 3: Loops and variables Consider this C function: @quotation @example int loop_test (int n) @{ int sum = 0; for (int i = 0; i < n; i++) sum += i * i; return sum; @} @end example @noindent @end quotation This example demonstrates some more features of libgccjit, with local variables and a loop. To break this down into libgccjit terms, it's usually easier to reword the @cite{for} loop as a @cite{while} loop, giving: @quotation @example int loop_test (int n) @{ int sum = 0; int i = 0; while (i < n) @{ sum += i * i; i++; @} return sum; @} @end example @noindent @end quotation Here's what the final control flow graph will look like: @quotation @float Figure @image{sum-of-squares1,,,image of a control flow graph,png} @end float @end quotation As before, we include the libgccjit header and make a @pxref{8,,gcc_jit_context *}. @example #include void test (void) @{ gcc_jit_context *ctxt; ctxt = gcc_jit_context_acquire (); @end example @noindent The function works with the C @cite{int} type: @example gcc_jit_type *the_type = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); gcc_jit_type *return_type = the_type; @end example @noindent though we could equally well make it work on, say, @cite{double}: @example gcc_jit_type *the_type = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_DOUBLE); @end example @noindent Let's build the function: @example gcc_jit_param *n = gcc_jit_context_new_param (ctxt, NULL, the_type, "n"); gcc_jit_param *params[1] = @{n@}; gcc_jit_function *func = gcc_jit_context_new_function (ctxt, NULL, GCC_JIT_FUNCTION_EXPORTED, return_type, "loop_test", 1, params, 0); @end example @noindent @menu * Expressions; lvalues and rvalues: Expressions lvalues and rvalues. * Control flow:: * Visualizing the control flow graph:: * Full example: Full example<2>. @end menu @node Expressions lvalues and rvalues,Control flow,,Tutorial part 3 Loops and variables @anchor{intro/tutorial03 expressions-lvalues-and-rvalues}@anchor{23} @subsection Expressions: lvalues and rvalues The base class of expression is the @pxref{13,,gcc_jit_rvalue *}, representing an expression that can be on the @emph{right}-hand side of an assignment: a value that can be computed somehow, and assigned @emph{to} a storage area (such as a variable). It has a specific @pxref{a,,gcc_jit_type *}. Anothe important class is @pxref{24,,gcc_jit_lvalue *}. A @pxref{24,,gcc_jit_lvalue *}. is something that can of the @emph{left}-hand side of an assignment: a storage area (such as a variable). In other words, every assignment can be thought of as: @example LVALUE = RVALUE; @end example @noindent Note that @pxref{24,,gcc_jit_lvalue *} is a subclass of @pxref{13,,gcc_jit_rvalue *}, where in an assignment of the form: @example LVALUE_A = LVALUE_B; @end example @noindent the @cite{LVALUE_B} implies reading the current value of that storage area, assigning it into the @cite{LVALUE_A}. So far the only expressions we've seen are @cite{i * i}: @example gcc_jit_rvalue *expr = gcc_jit_context_new_binary_op ( ctxt, NULL, GCC_JIT_BINARY_OP_MULT, int_type, gcc_jit_param_as_rvalue (param_i), gcc_jit_param_as_rvalue (param_i)); @end example @noindent which is a @pxref{13,,gcc_jit_rvalue *}, and the various function parameters: @cite{param_i} and @cite{param_n}, instances of @pxref{25,,gcc_jit_param *}, which is a subclass of @pxref{24,,gcc_jit_lvalue *} (and, in turn, of @pxref{13,,gcc_jit_rvalue *}): we can both read from and write to function parameters within the body of a function. Our new example has a couple of local variables. We create them by calling @pxref{26,,gcc_jit_function_new_local()}, supplying a type and a name: @example /* Build locals: */ gcc_jit_lvalue *i = gcc_jit_function_new_local (func, NULL, the_type, "i"); gcc_jit_lvalue *sum = gcc_jit_function_new_local (func, NULL, the_type, "sum"); @end example @noindent These are instances of @pxref{24,,gcc_jit_lvalue *} - they can be read from and written to. Note that there is no precanned way to create @emph{and} initialize a variable like in C: @example int i = 0; @end example @noindent Instead, having added the local to the function, we have to separately add an assignment of @cite{0} to @cite{local_i} at the beginning of the function. @node Control flow,Visualizing the control flow graph,Expressions lvalues and rvalues,Tutorial part 3 Loops and variables @anchor{intro/tutorial03 control-flow}@anchor{27} @subsection Control flow This function has a loop, so we need to build some basic blocks to handle the control flow. In this case, we need 4 blocks: @enumerate @item before the loop (initializing the locals) @item the conditional at the top of the loop (comparing @cite{i < n}) @item the body of the loop @item after the loop terminates (@cite{return sum}) @end enumerate so we create these as @pxref{28,,gcc_jit_block *} instances within the @pxref{29,,gcc_jit_function *}: @example gcc_jit_block *b_initial = gcc_jit_function_new_block (func, "initial"); gcc_jit_block *b_loop_cond = gcc_jit_function_new_block (func, "loop_cond"); gcc_jit_block *b_loop_body = gcc_jit_function_new_block (func, "loop_body"); gcc_jit_block *b_after_loop = gcc_jit_function_new_block (func, "after_loop"); @end example @noindent We now populate each block with statements. The entry block @cite{b_initial} consists of initializations followed by a jump to the conditional. We assign @cite{0} to @cite{i} and to @cite{sum}, using @pxref{2a,,gcc_jit_block_add_assignment()} to add an assignment statement, and using @pxref{2b,,gcc_jit_context_zero()} to get the constant value @cite{0} for the relevant type for the right-hand side of the assignment: @example /* sum = 0; */ gcc_jit_block_add_assignment ( b_initial, NULL, sum, gcc_jit_context_zero (ctxt, the_type)); /* i = 0; */ gcc_jit_block_add_assignment ( b_initial, NULL, i, gcc_jit_context_zero (ctxt, the_type)); @end example @noindent We can then terminate the entry block by jumping to the conditional: @example gcc_jit_block_end_with_jump (b_initial, NULL, b_loop_cond); @end example @noindent The conditional block is equivalent to the line @cite{while (i < n)} from our C example. It contains a single statement: a conditional, which jumps to one of two destination blocks depending on a boolean @pxref{13,,gcc_jit_rvalue *}, in this case the comparison of @cite{i} and @cite{n}. We build the comparison using @pxref{2c,,gcc_jit_context_new_comparison()}: @example /* (i >= n) */ gcc_jit_rvalue *guard = gcc_jit_context_new_comparison ( ctxt, NULL, GCC_JIT_COMPARISON_GE, gcc_jit_lvalue_as_rvalue (i), gcc_jit_param_as_rvalue (n)); @end example @noindent and can then use this to add @cite{b_loop_cond}'s sole statement, via @pxref{2d,,gcc_jit_block_end_with_conditional()}: @example /* Equivalent to: if (guard) goto after_loop; else goto loop_body; */ gcc_jit_block_end_with_conditional ( b_loop_cond, NULL, guard, b_after_loop, /* on_true */ b_loop_body); /* on_false */ @end example @noindent Next, we populate the body of the loop. The C statement @cite{sum += i * i;} is an assignment operation, where an lvalue is modified "in-place". We use @pxref{2e,,gcc_jit_block_add_assignment_op()} to handle these operations: @example /* sum += i * i */ gcc_jit_block_add_assignment_op ( b_loop_body, NULL, sum, GCC_JIT_BINARY_OP_PLUS, gcc_jit_context_new_binary_op ( ctxt, NULL, GCC_JIT_BINARY_OP_MULT, the_type, gcc_jit_lvalue_as_rvalue (i), gcc_jit_lvalue_as_rvalue (i))); @end example @noindent The @cite{i++} can be thought of as @cite{i += 1}, and can thus be handled in a similar way. We use @pxref{2f,,gcc_jit_context_one()} to get the constant value @cite{1} (for the relevant type) for the right-hand side of the assignment. @example /* i++ */ gcc_jit_block_add_assignment_op ( b_loop_body, NULL, i, GCC_JIT_BINARY_OP_PLUS, gcc_jit_context_one (ctxt, the_type)); @end example @noindent @cartouche @quotation Note For numeric constants other than 0 or 1, we could use @pxref{30,,gcc_jit_context_new_rvalue_from_int()} and @pxref{31,,gcc_jit_context_new_rvalue_from_double()}. @end quotation @end cartouche The loop body completes by jumping back to the conditional: @example gcc_jit_block_end_with_jump (b_loop_body, NULL, b_loop_cond); @end example @noindent Finally, we populate the @cite{b_after_loop} block, reached when the loop conditional is false. We want to generate the equivalent of: @example return sum; @end example @noindent so the block is just one statement: @example /* return sum */ gcc_jit_block_end_with_return ( b_after_loop, NULL, gcc_jit_lvalue_as_rvalue (sum)); @end example @noindent @cartouche @quotation Note You can intermingle block creation with statement creation, but given that the terminator statements generally include references to other blocks, I find it's clearer to create all the blocks, @emph{then} all the statements. @end quotation @end cartouche We've finished populating the function. As before, we can now compile it to machine code: @example gcc_jit_result *result; result = gcc_jit_context_compile (ctxt); typedef int (*loop_test_fn_type) (int); loop_test_fn_type loop_test = (loop_test_fn_type)gcc_jit_result_get_code (result, "loop_test"); if (!loop_test) goto error; printf ("result: %d", loop_test (10)); @end example @noindent @example result: 285 @end example @noindent @node Visualizing the control flow graph,Full example<2>,Control flow,Tutorial part 3 Loops and variables @anchor{intro/tutorial03 visualizing-the-control-flow-graph}@anchor{32} @subsection Visualizing the control flow graph You can see the control flow graph of a function using @pxref{33,,gcc_jit_function_dump_to_dot()}: @example gcc_jit_function_dump_to_dot (func, "/tmp/sum-of-squares.dot"); @end example @noindent giving a .dot file in GraphViz format. You can convert this to an image using @cite{dot}: @example $ dot -Tpng /tmp/sum-of-squares.dot -o /tmp/sum-of-squares.png @end example @noindent or use a viewer (my preferred one is xdot.py; see @indicateurl{https://github.com/jrfonseca/xdot.py}; on Fedora you can install it with @cite{yum install python-xdot}): @quotation @float Figure @image{sum-of-squares1,,,image of a control flow graph,png} @end float @end quotation @node Full example<2>,,Visualizing the control flow graph,Tutorial part 3 Loops and variables @anchor{intro/tutorial03 full-example}@anchor{34} @subsection Full example @quotation @example /* Usage example for libgccjit.so Copyright (C) 2014-2015 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include #include #include void create_code (gcc_jit_context *ctxt) @{ /* Simple sum-of-squares, to test conditionals and looping int loop_test (int n) @{ int i; int sum = 0; for (i = 0; i < n ; i ++) @{ sum += i * i; @} return sum; */ gcc_jit_type *the_type = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); gcc_jit_type *return_type = the_type; gcc_jit_param *n = gcc_jit_context_new_param (ctxt, NULL, the_type, "n"); gcc_jit_param *params[1] = @{n@}; gcc_jit_function *func = gcc_jit_context_new_function (ctxt, NULL, GCC_JIT_FUNCTION_EXPORTED, return_type, "loop_test", 1, params, 0); /* Build locals: */ gcc_jit_lvalue *i = gcc_jit_function_new_local (func, NULL, the_type, "i"); gcc_jit_lvalue *sum = gcc_jit_function_new_local (func, NULL, the_type, "sum"); gcc_jit_block *b_initial = gcc_jit_function_new_block (func, "initial"); gcc_jit_block *b_loop_cond = gcc_jit_function_new_block (func, "loop_cond"); gcc_jit_block *b_loop_body = gcc_jit_function_new_block (func, "loop_body"); gcc_jit_block *b_after_loop = gcc_jit_function_new_block (func, "after_loop"); /* sum = 0; */ gcc_jit_block_add_assignment ( b_initial, NULL, sum, gcc_jit_context_zero (ctxt, the_type)); /* i = 0; */ gcc_jit_block_add_assignment ( b_initial, NULL, i, gcc_jit_context_zero (ctxt, the_type)); gcc_jit_block_end_with_jump (b_initial, NULL, b_loop_cond); /* if (i >= n) */ gcc_jit_block_end_with_conditional ( b_loop_cond, NULL, gcc_jit_context_new_comparison ( ctxt, NULL, GCC_JIT_COMPARISON_GE, gcc_jit_lvalue_as_rvalue (i), gcc_jit_param_as_rvalue (n)), b_after_loop, b_loop_body); /* sum += i * i */ gcc_jit_block_add_assignment_op ( b_loop_body, NULL, sum, GCC_JIT_BINARY_OP_PLUS, gcc_jit_context_new_binary_op ( ctxt, NULL, GCC_JIT_BINARY_OP_MULT, the_type, gcc_jit_lvalue_as_rvalue (i), gcc_jit_lvalue_as_rvalue (i))); /* i++ */ gcc_jit_block_add_assignment_op ( b_loop_body, NULL, i, GCC_JIT_BINARY_OP_PLUS, gcc_jit_context_one (ctxt, the_type)); gcc_jit_block_end_with_jump (b_loop_body, NULL, b_loop_cond); /* return sum */ gcc_jit_block_end_with_return ( b_after_loop, NULL, gcc_jit_lvalue_as_rvalue (sum)); @} int main (int argc, char **argv) @{ gcc_jit_context *ctxt = NULL; gcc_jit_result *result = NULL; /* Get a "context" object for working with the library. */ ctxt = gcc_jit_context_acquire (); if (!ctxt) @{ fprintf (stderr, "NULL ctxt"); goto error; @} /* Set some options on the context. Let's see the code being generated, in assembler form. */ gcc_jit_context_set_bool_option ( ctxt, GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, 0); /* Populate the context. */ create_code (ctxt); /* Compile the code. */ result = gcc_jit_context_compile (ctxt); if (!result) @{ fprintf (stderr, "NULL result"); goto error; @} /* Extract the generated code from "result". */ typedef int (*loop_test_fn_type) (int); loop_test_fn_type loop_test = (loop_test_fn_type)gcc_jit_result_get_code (result, "loop_test"); if (!loop_test) @{ fprintf (stderr, "NULL loop_test"); goto error; @} /* Run the generated code. */ int val = loop_test (10); printf("loop_test returned: %d\n", val); error: gcc_jit_context_release (ctxt); gcc_jit_result_release (result); return 0; @} @end example @noindent @end quotation Building and running it: @example $ gcc \ tut03-sum-of-squares.c \ -o tut03-sum-of-squares \ -lgccjit # Run the built program: $ ./tut03-sum-of-squares loop_test returned: 285 @end example @noindent @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Tutorial part 4 Adding JIT-compilation to a toy interpreter,Tutorial part 5 Implementing an Ahead-of-Time compiler,Tutorial part 3 Loops and variables,Tutorial @anchor{intro/tutorial04 tutorial-part-4-adding-jit-compilation-to-a-toy-interpreter}@anchor{35}@anchor{intro/tutorial04 doc}@anchor{36} @section Tutorial part 4: Adding JIT-compilation to a toy interpreter In this example we construct a "toy" interpreter, and add JIT-compilation to it. @menu * Our toy interpreter:: * Compiling to machine code:: * Setting things up:: * Populating the function:: * Verifying the control flow graph:: * Compiling the context:: * Single-stepping through the generated code:: * Examining the generated code:: * Putting it all together:: * Behind the curtain; How does our code get optimized?: Behind the curtain How does our code get optimized?. @end menu @node Our toy interpreter,Compiling to machine code,,Tutorial part 4 Adding JIT-compilation to a toy interpreter @anchor{intro/tutorial04 our-toy-interpreter}@anchor{37} @subsection Our toy interpreter It's a stack-based interpreter, and is intended as a (very simple) example of the kind of bytecode interpreter seen in dynamic languages such as Python, Ruby etc. For the sake of simplicity, our toy virtual machine is very limited: @quotation @itemize * @item The only data type is @cite{int} @item It can only work on one function at a time (so that the only function call that can be made is to recurse). @item Functions can only take one parameter. @item Functions have a stack of @cite{int} values. @item We'll implement function call within the interpreter by calling a function in our implementation, rather than implementing our own frame stack. @item The parser is only good enough to get the examples to work. @end itemize @end quotation Naturally, a real interpreter would be much more complicated that this. The following operations are supported: @multitable {xxxxxxxxxxxxxxxxxxxxxxxx} {xxxxxxxxxxxxxxxxxxxxxxxxxx} {xxxxxxxxxxxxxxxxx} {xxxxxxxxxxxxxxxxxx} @headitem Operation @tab Meaning @tab Old Stack @tab New Stack @item DUP @tab Duplicate top of stack. @tab @code{[..., x]} @tab @code{[..., x, x]} @item ROT @tab Swap top two elements of stack. @tab @code{[..., x, y]} @tab @code{[..., y, x]} @item BINARY_ADD @tab Add the top two elements on the stack. @tab @code{[..., x, y]} @tab @code{[..., (x+y)]} @item BINARY_SUBTRACT @tab Likewise, but subtract. @tab @code{[..., x, y]} @tab @code{[..., (x-y)]} @item BINARY_MULT @tab Likewise, but multiply. @tab @code{[..., x, y]} @tab @code{[..., (x*y)]} @item BINARY_COMPARE_LT @tab Compare the top two elements on the stack and push a nonzero/zero if (xfn_num_ops); op = &fn->fn_ops[frame.frm_pc++]; if (trace) @{ toyvm_frame_dump_stack (&frame, trace); toyvm_function_disassemble_op (fn, op, frame.frm_pc, trace); @} switch (op->op_opcode) @{ /* Ops taking no operand. */ case DUP: x = POP (); PUSH (x); PUSH (x); break; case ROT: y = POP (); x = POP (); PUSH (y); PUSH (x); break; case BINARY_ADD: y = POP (); x = POP (); PUSH (x + y); break; case BINARY_SUBTRACT: y = POP (); x = POP (); PUSH (x - y); break; case BINARY_MULT: y = POP (); x = POP (); PUSH (x * y); break; case BINARY_COMPARE_LT: y = POP (); x = POP (); PUSH (x < y); break; case RECURSE: x = POP (); x = toyvm_function_interpret (fn, x, trace); PUSH (x); break; case RETURN: return POP (); /* Ops taking an operand. */ case PUSH_CONST: PUSH (op->op_operand); break; case JUMP_ABS_IF_TRUE: x = POP (); if (x) frame.frm_pc = op->op_operand; break; default: assert (0); /* unknown opcode */ @} /* end of switch on opcode */ @} /* end of while loop */ #undef PUSH #undef POP @} @end example @noindent @end quotation @node Compiling to machine code,Setting things up,Our toy interpreter,Tutorial part 4 Adding JIT-compilation to a toy interpreter @anchor{intro/tutorial04 compiling-to-machine-code}@anchor{38} @subsection Compiling to machine code We want to generate machine code that can be cast to this type and then directly executed in-process: @quotation @example typedef int (*toyvm_compiled_code) (int); @end example @noindent @end quotation The lifetime of the code is tied to that of a @pxref{16,,gcc_jit_result *}. We'll handle this by bundling them up in a structure, so that we can clean them up together by calling @pxref{39,,gcc_jit_result_release()}: @quotation @example struct toyvm_compiled_function @{ gcc_jit_result *cf_jit_result; toyvm_compiled_code cf_code; @}; @end example @noindent @end quotation Our compiler isn't very sophisticated; it takes the implementation of each opcode above, and maps it directly to the operations supported by the libgccjit API. How should we handle the stack? In theory we could calculate what the stack depth will be at each opcode, and optimize away the stack manipulation "by hand". We'll see below that libgccjit is able to do this for us, so we'll implement stack manipulation in a direct way, by creating a @code{stack} array and @code{stack_depth} variables, local within the generated function, equivalent to this C code: @example int stack_depth; int stack[MAX_STACK_DEPTH]; @end example @noindent We'll also have local variables @code{x} and @code{y} for use when implementing the opcodes, equivalent to this: @example int x; int y; @end example @noindent This means our compiler has the following state: @quotation @example struct compilation_state @{ gcc_jit_context *ctxt; gcc_jit_type *int_type; gcc_jit_type *bool_type; gcc_jit_type *stack_type; /* int[MAX_STACK_DEPTH] */ gcc_jit_rvalue *const_one; gcc_jit_function *fn; gcc_jit_param *param_arg; gcc_jit_lvalue *stack; gcc_jit_lvalue *stack_depth; gcc_jit_lvalue *x; gcc_jit_lvalue *y; gcc_jit_location *op_locs[MAX_OPS]; gcc_jit_block *initial_block; gcc_jit_block *op_blocks[MAX_OPS]; @}; @end example @noindent @end quotation @node Setting things up,Populating the function,Compiling to machine code,Tutorial part 4 Adding JIT-compilation to a toy interpreter @anchor{intro/tutorial04 setting-things-up}@anchor{3a} @subsection Setting things up First we create our types: @quotation @example state.int_type = gcc_jit_context_get_type (state.ctxt, GCC_JIT_TYPE_INT); state.bool_type = gcc_jit_context_get_type (state.ctxt, GCC_JIT_TYPE_BOOL); state.stack_type = gcc_jit_context_new_array_type (state.ctxt, NULL, state.int_type, MAX_STACK_DEPTH); @end example @noindent @end quotation along with extracting a useful @cite{int} constant: @quotation @example state.const_one = gcc_jit_context_one (state.ctxt, state.int_type); @end example @noindent @end quotation We'll implement push and pop in terms of the @code{stack} array and @code{stack_depth}. Here are helper functions for adding statements to a block, implementing pushing and popping values: @quotation @example static void add_push (compilation_state *state, gcc_jit_block *block, gcc_jit_rvalue *rvalue, gcc_jit_location *loc) @{ /* stack[stack_depth] = RVALUE */ gcc_jit_block_add_assignment ( block, loc, /* stack[stack_depth] */ gcc_jit_context_new_array_access ( state->ctxt, loc, gcc_jit_lvalue_as_rvalue (state->stack), gcc_jit_lvalue_as_rvalue (state->stack_depth)), rvalue); /* "stack_depth++;". */ gcc_jit_block_add_assignment_op ( block, loc, state->stack_depth, GCC_JIT_BINARY_OP_PLUS, state->const_one); @} static void add_pop (compilation_state *state, gcc_jit_block *block, gcc_jit_lvalue *lvalue, gcc_jit_location *loc) @{ /* "--stack_depth;". */ gcc_jit_block_add_assignment_op ( block, loc, state->stack_depth, GCC_JIT_BINARY_OP_MINUS, state->const_one); /* "LVALUE = stack[stack_depth];". */ gcc_jit_block_add_assignment ( block, loc, lvalue, /* stack[stack_depth] */ gcc_jit_lvalue_as_rvalue ( gcc_jit_context_new_array_access ( state->ctxt, loc, gcc_jit_lvalue_as_rvalue (state->stack), gcc_jit_lvalue_as_rvalue (state->stack_depth)))); @} @end example @noindent @end quotation We will support single-stepping through the generated code in the debugger, so we need to create @pxref{3b,,gcc_jit_location} instances, one per operation in the source code. These will reference the lines of e.g. @code{factorial.toy}. @quotation @example for (pc = 0; pc < fn->fn_num_ops; pc++) @{ toyvm_op *op = &fn->fn_ops[pc]; state.op_locs[pc] = gcc_jit_context_new_location (state.ctxt, fn->fn_filename, op->op_linenum, 0); /* column */ @} @end example @noindent @end quotation Let's create the function itself. As usual, we create its parameter first, then use the parameter to create the function: @quotation @example state.param_arg = gcc_jit_context_new_param (state.ctxt, state.op_locs[0], state.int_type, "arg"); state.fn = gcc_jit_context_new_function (state.ctxt, state.op_locs[0], GCC_JIT_FUNCTION_EXPORTED, state.int_type, funcname, 1, &state.param_arg, 0); @end example @noindent @end quotation We create the locals within the function. @quotation @example state.stack = gcc_jit_function_new_local (state.fn, NULL, state.stack_type, "stack"); state.stack_depth = gcc_jit_function_new_local (state.fn, NULL, state.int_type, "stack_depth"); state.x = gcc_jit_function_new_local (state.fn, NULL, state.int_type, "x"); state.y = gcc_jit_function_new_local (state.fn, NULL, state.int_type, "y"); @end example @noindent @end quotation @node Populating the function,Verifying the control flow graph,Setting things up,Tutorial part 4 Adding JIT-compilation to a toy interpreter @anchor{intro/tutorial04 populating-the-function}@anchor{3c} @subsection Populating the function There's some one-time initialization, and the API treats the first block you create as the entrypoint of the function, so we need to create that block first: @quotation @example state.initial_block = gcc_jit_function_new_block (state.fn, "initial"); @end example @noindent @end quotation We can now create blocks for each of the operations. Most of these will be consolidated into larger blocks when the optimizer runs. @quotation @example for (pc = 0; pc < fn->fn_num_ops; pc++) @{ char buf[16]; sprintf (buf, "instr%i", pc); state.op_blocks[pc] = gcc_jit_function_new_block (state.fn, buf); @} @end example @noindent @end quotation Now that we have a block it can jump to when it's done, we can populate the initial block: @quotation @example /* "stack_depth = 0;". */ gcc_jit_block_add_assignment ( state.initial_block, state.op_locs[0], state.stack_depth, gcc_jit_context_zero (state.ctxt, state.int_type)); /* "PUSH (arg);". */ add_push (&state, state.initial_block, gcc_jit_param_as_rvalue (state.param_arg), state.op_locs[0]); /* ...and jump to insn 0. */ gcc_jit_block_end_with_jump (state.initial_block, state.op_locs[0], state.op_blocks[0]); @end example @noindent @end quotation We can now populate the blocks for the individual operations. We loop through them, adding instructions to their blocks: @quotation @example for (pc = 0; pc < fn->fn_num_ops; pc++) @{ gcc_jit_location *loc = state.op_locs[pc]; gcc_jit_block *block = state.op_blocks[pc]; gcc_jit_block *next_block = (pc < fn->fn_num_ops ? state.op_blocks[pc + 1] : NULL); toyvm_op *op; op = &fn->fn_ops[pc]; @end example @noindent @end quotation We're going to have another big @code{switch} statement for implementing the opcodes, this time for compiling them, rather than interpreting them. It's helpful to have macros for implementing push and pop, so that we can make the @code{switch} statement that's coming up look as much as possible like the one above within the interpreter: @example #define X_EQUALS_POP()\ add_pop (&state, block, state.x, loc) #define Y_EQUALS_POP()\ add_pop (&state, block, state.y, loc) #define PUSH_RVALUE(RVALUE)\ add_push (&state, block, (RVALUE), loc) #define PUSH_X()\ PUSH_RVALUE (gcc_jit_lvalue_as_rvalue (state.x)) #define PUSH_Y() \ PUSH_RVALUE (gcc_jit_lvalue_as_rvalue (state.y)) @end example @noindent @cartouche @quotation Note A particularly clever implementation would have an @emph{identical} @code{switch} statement shared by the interpreter and the compiler, with some preprocessor "magic". We're not doing that here, for the sake of simplicity. @end quotation @end cartouche When I first implemented this compiler, I accidentally missed an edit when copying and pasting the @code{Y_EQUALS_POP} macro, so that popping the stack into @code{y} instead erroneously assigned it to @code{x}, leaving @code{y} uninitialized. To track this kind of thing down, we can use @pxref{3d,,gcc_jit_block_add_comment()} to add descriptive comments to the internal representation. This is invaluable when looking through the generated IR for, say @code{factorial}: @quotation @example gcc_jit_block_add_comment (block, loc, opcode_names[op->op_opcode]); @end example @noindent @end quotation We can now write the big @code{switch} statement that implements the individual opcodes, populating the relevant block with statements: @quotation @example switch (op->op_opcode) @{ case DUP: X_EQUALS_POP (); PUSH_X (); PUSH_X (); break; case ROT: Y_EQUALS_POP (); X_EQUALS_POP (); PUSH_Y (); PUSH_X (); break; case BINARY_ADD: Y_EQUALS_POP (); X_EQUALS_POP (); PUSH_RVALUE ( gcc_jit_context_new_binary_op ( state.ctxt, loc, GCC_JIT_BINARY_OP_PLUS, state.int_type, gcc_jit_lvalue_as_rvalue (state.x), gcc_jit_lvalue_as_rvalue (state.y))); break; case BINARY_SUBTRACT: Y_EQUALS_POP (); X_EQUALS_POP (); PUSH_RVALUE ( gcc_jit_context_new_binary_op ( state.ctxt, loc, GCC_JIT_BINARY_OP_MINUS, state.int_type, gcc_jit_lvalue_as_rvalue (state.x), gcc_jit_lvalue_as_rvalue (state.y))); break; case BINARY_MULT: Y_EQUALS_POP (); X_EQUALS_POP (); PUSH_RVALUE ( gcc_jit_context_new_binary_op ( state.ctxt, loc, GCC_JIT_BINARY_OP_MULT, state.int_type, gcc_jit_lvalue_as_rvalue (state.x), gcc_jit_lvalue_as_rvalue (state.y))); break; case BINARY_COMPARE_LT: Y_EQUALS_POP (); X_EQUALS_POP (); PUSH_RVALUE ( /* cast of bool to int */ gcc_jit_context_new_cast ( state.ctxt, loc, /* (x < y) as a bool */ gcc_jit_context_new_comparison ( state.ctxt, loc, GCC_JIT_COMPARISON_LT, gcc_jit_lvalue_as_rvalue (state.x), gcc_jit_lvalue_as_rvalue (state.y)), state.int_type)); break; case RECURSE: @{ X_EQUALS_POP (); gcc_jit_rvalue *arg = gcc_jit_lvalue_as_rvalue (state.x); PUSH_RVALUE ( gcc_jit_context_new_call ( state.ctxt, loc, state.fn, 1, &arg)); break; @} case RETURN: X_EQUALS_POP (); gcc_jit_block_end_with_return ( block, loc, gcc_jit_lvalue_as_rvalue (state.x)); break; /* Ops taking an operand. */ case PUSH_CONST: PUSH_RVALUE ( gcc_jit_context_new_rvalue_from_int ( state.ctxt, state.int_type, op->op_operand)); break; case JUMP_ABS_IF_TRUE: X_EQUALS_POP (); gcc_jit_block_end_with_conditional ( block, loc, /* "(bool)x". */ gcc_jit_context_new_cast ( state.ctxt, loc, gcc_jit_lvalue_as_rvalue (state.x), state.bool_type), state.op_blocks[op->op_operand], /* on_true */ next_block); /* on_false */ break; default: assert(0); @} /* end of switch on opcode */ @end example @noindent @end quotation Every block must be terminated, via a call to one of the @code{gcc_jit_block_end_with_} entrypoints. This has been done for two of the opcodes, but we need to do it for the other ones, by jumping to the next block. @quotation @example if (op->op_opcode != JUMP_ABS_IF_TRUE && op->op_opcode != RETURN) gcc_jit_block_end_with_jump ( block, loc, next_block); @end example @noindent @end quotation This is analogous to simply incrementing the program counter. @node Verifying the control flow graph,Compiling the context,Populating the function,Tutorial part 4 Adding JIT-compilation to a toy interpreter @anchor{intro/tutorial04 verifying-the-control-flow-graph}@anchor{3e} @subsection Verifying the control flow graph Having finished looping over the blocks, the context is complete. As before, we can verify that the control flow and statements are sane by using @pxref{33,,gcc_jit_function_dump_to_dot()}: @example gcc_jit_function_dump_to_dot (state.fn, "/tmp/factorial.dot"); @end example @noindent and viewing the result. Note how the label names, comments, and variable names show up in the dump, to make it easier to spot errors in our compiler. @quotation @float Figure @image{factorial1,,,image of a control flow graph,png} @end float @end quotation @node Compiling the context,Single-stepping through the generated code,Verifying the control flow graph,Tutorial part 4 Adding JIT-compilation to a toy interpreter @anchor{intro/tutorial04 compiling-the-context}@anchor{3f} @subsection Compiling the context Having finished looping over the blocks and populating them with statements, the context is complete. We can now compile it, and extract machine code from the result: @quotation @example gcc_jit_result *jit_result = gcc_jit_context_compile (state.ctxt); gcc_jit_context_release (state.ctxt); toyvm_compiled_function *toyvm_result = (toyvm_compiled_function *)calloc (1, sizeof (toyvm_compiled_function)); if (!toyvm_result) @{ fprintf (stderr, "out of memory allocating toyvm_compiled_function\n"); gcc_jit_result_release (jit_result); return NULL; @} toyvm_result->cf_jit_result = jit_result; toyvm_result->cf_code = (toyvm_compiled_code)gcc_jit_result_get_code (jit_result, funcname); free (funcname); return toyvm_result; @} char test[1024]; #define CHECK_NON_NULL(PTR) \ do @{ \ if ((PTR) != NULL) \ @{ \ pass ("%s: %s is non-null", test, #PTR); \ @} \ else \ @{ \ fail ("%s: %s is NULL", test, #PTR); \ abort (); \ @} \ @} while (0) #define CHECK_VALUE(ACTUAL, EXPECTED) \ do @{ \ if ((ACTUAL) == (EXPECTED)) \ @{ \ pass ("%s: actual: %s == expected: %s", test, #ACTUAL, #EXPECTED); \ @} \ else \ @{ \ fail ("%s: actual: %s != expected: %s", test, #ACTUAL, #EXPECTED); \ fprintf (stderr, "incorrect value\n"); \ abort (); \ @} \ @} while (0) static void test_script (const char *scripts_dir, const char *script_name, int input, int expected_result) @{ char *script_path; toyvm_function *fn; int interpreted_result; toyvm_compiled_function *compiled_fn; toyvm_compiled_code code; int compiled_result; snprintf (test, sizeof (test), "toyvm.c: %s", script_name); script_path = (char *)malloc (strlen (scripts_dir) + strlen (script_name) + 1); CHECK_NON_NULL (script_path); sprintf (script_path, "%s%s", scripts_dir, script_name); fn = toyvm_function_parse (script_path, script_name); CHECK_NON_NULL (fn); interpreted_result = toyvm_function_interpret (fn, input, NULL); CHECK_VALUE (interpreted_result, expected_result); compiled_fn = toyvm_function_compile (fn); CHECK_NON_NULL (compiled_fn); code = (toyvm_compiled_code)compiled_fn->cf_code; CHECK_NON_NULL (code); compiled_result = code (input); CHECK_VALUE (compiled_result, expected_result); gcc_jit_result_release (compiled_fn->cf_jit_result); free (compiled_fn); free (fn); free (script_path); @} #define PATH_TO_SCRIPTS ("/jit/docs/examples/tut04-toyvm/") static void test_suite (void) @{ const char *srcdir; char *scripts_dir; snprintf (test, sizeof (test), "toyvm.c"); /* We need to locate the test scripts. Rely on "srcdir" being set in the environment. */ srcdir = getenv ("srcdir"); CHECK_NON_NULL (srcdir); scripts_dir = (char *)malloc (strlen (srcdir) + strlen(PATH_TO_SCRIPTS) + 1); CHECK_NON_NULL (scripts_dir); sprintf (scripts_dir, "%s%s", srcdir, PATH_TO_SCRIPTS); test_script (scripts_dir, "factorial.toy", 10, 3628800); test_script (scripts_dir, "fibonacci.toy", 10, 55); free (scripts_dir); @} int main (int argc, char **argv) @{ const char *filename = NULL; toyvm_function *fn = NULL; /* If called with no args, assume we're being run by the test suite. */ if (argc < 3) @{ test_suite (); return 0; @} if (argc != 3) @{ fprintf (stdout, "%s FILENAME INPUT: Parse and run a .toy file\n", argv[0]); exit (1); @} filename = argv[1]; fn = toyvm_function_parse (filename, filename); if (!fn) exit (1); if (0) toyvm_function_disassemble (fn, stdout); printf ("interpreter result: %d\n", toyvm_function_interpret (fn, atoi (argv[2]), NULL)); /* JIT-compilation. */ toyvm_compiled_function *compiled_fn = toyvm_function_compile (fn); toyvm_compiled_code code = compiled_fn->cf_code; printf ("compiler result: %d\n", code (atoi (argv[2]))); gcc_jit_result_release (compiled_fn->cf_jit_result); free (compiled_fn); return 0; @} @end example @noindent @end quotation We can now run the result: @quotation @example toyvm_compiled_function *compiled_fn = toyvm_function_compile (fn); toyvm_compiled_code code = compiled_fn->cf_code; printf ("compiler result: %d\n", code (atoi (argv[2]))); gcc_jit_result_release (compiled_fn->cf_jit_result); free (compiled_fn); @end example @noindent @end quotation @node Single-stepping through the generated code,Examining the generated code,Compiling the context,Tutorial part 4 Adding JIT-compilation to a toy interpreter @anchor{intro/tutorial04 single-stepping-through-the-generated-code}@anchor{40} @subsection Single-stepping through the generated code It's possible to debug the generated code. To do this we need to both: @quotation @itemize * @item Set up source code locations for our statements, so that we can meaningfully step through the code. We did this above by calling @pxref{41,,gcc_jit_context_new_location()} and using the results. @item Enable the generation of debugging information, by setting @pxref{42,,GCC_JIT_BOOL_OPTION_DEBUGINFO} on the @pxref{8,,gcc_jit_context} via @pxref{1b,,gcc_jit_context_set_bool_option()}: @example gcc_jit_context_set_bool_option ( ctxt, GCC_JIT_BOOL_OPTION_DEBUGINFO, 1); @end example @noindent @end itemize @end quotation Having done this, we can put a breakpoint on the generated function: @example $ gdb --args ./toyvm factorial.toy 10 (gdb) break factorial Function "factorial" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 1 (factorial) pending. (gdb) run Breakpoint 1, factorial (arg=10) at factorial.toy:14 14 DUP @end example @noindent We've set up location information, which references @code{factorial.toy}. This allows us to use e.g. @code{list} to see where we are in the script: @example (gdb) list 9 10 # Initial state: 11 # stack: [arg] 12 13 # 0: 14 DUP 15 # stack: [arg, arg] 16 17 # 1: 18 PUSH_CONST 2 @end example @noindent and to step through the function, examining the data: @example (gdb) n 18 PUSH_CONST 2 (gdb) n 22 BINARY_COMPARE_LT (gdb) print stack $5 = @{10, 10, 2, 0, -7152, 32767, 0, 0@} (gdb) print stack_depth $6 = 3 @end example @noindent You'll see that the parts of the @code{stack} array that haven't been touched yet are uninitialized. @cartouche @quotation Note Turning on optimizations may lead to unpredictable results when stepping through the generated code: the execution may appear to "jump around" the source code. This is analogous to turning up the optimization level in a regular compiler. @end quotation @end cartouche @node Examining the generated code,Putting it all together,Single-stepping through the generated code,Tutorial part 4 Adding JIT-compilation to a toy interpreter @anchor{intro/tutorial04 examining-the-generated-code}@anchor{43} @subsection Examining the generated code How good is the optimized code? We can turn up optimizations, by calling @pxref{1e,,gcc_jit_context_set_int_option()} with @pxref{1f,,GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL}: @example gcc_jit_context_set_int_option ( ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3); @end example @noindent One of GCC's internal representations is called "gimple". A dump of the initial gimple representation of the code can be seen by setting: @example gcc_jit_context_set_bool_option (ctxt, GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, 1); @end example @noindent With optimization on and source locations displayed, this gives: @c We'll use "c" for gimple dumps @example factorial (signed int arg) @{ D.80; signed int D.81; signed int D.82; signed int D.83; signed int D.84; signed int D.85; signed int y; signed int x; signed int stack_depth; signed int stack[8]; try @{ initial: stack_depth = 0; stack[stack_depth] = arg; stack_depth = stack_depth + 1; goto instr0; instr0: /* DUP */: stack_depth = stack_depth + -1; x = stack[stack_depth]; stack[stack_depth] = x; stack_depth = stack_depth + 1; stack[stack_depth] = x; stack_depth = stack_depth + 1; goto instr1; instr1: /* PUSH_CONST */: stack[stack_depth] = 2; stack_depth = stack_depth + 1; goto instr2; /* etc */ @end example @noindent You can see the generated machine code in assembly form via: @example gcc_jit_context_set_bool_option ( ctxt, GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, 1); result = gcc_jit_context_compile (ctxt); @end example @noindent which shows that (on this x86_64 box) the compiler has unrolled the loop and is using MMX instructions to perform several multiplications simultaneously: @example .file "fake.c" .text .Ltext0: .p2align 4,,15 .globl factorial .type factorial, @@function factorial: .LFB0: .file 1 "factorial.toy" .loc 1 14 0 .cfi_startproc .LVL0: .L2: .loc 1 26 0 cmpl $1, %edi jle .L13 leal -1(%rdi), %edx movl %edx, %ecx shrl $2, %ecx leal 0(,%rcx,4), %esi testl %esi, %esi je .L14 cmpl $9, %edx jbe .L14 leal -2(%rdi), %eax movl %eax, -16(%rsp) leal -3(%rdi), %eax movd -16(%rsp), %xmm0 movl %edi, -16(%rsp) movl %eax, -12(%rsp) movd -16(%rsp), %xmm1 xorl %eax, %eax movl %edx, -16(%rsp) movd -12(%rsp), %xmm4 movd -16(%rsp), %xmm6 punpckldq %xmm4, %xmm0 movdqa .LC1(%rip), %xmm4 punpckldq %xmm6, %xmm1 punpcklqdq %xmm0, %xmm1 movdqa .LC0(%rip), %xmm0 jmp .L5 # etc - edited for brevity @end example @noindent This is clearly overkill for a function that will likely overflow the @code{int} type before the vectorization is worthwhile - but then again, this is a toy example. Turning down the optimization level to 2: @example gcc_jit_context_set_int_option ( ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3); @end example @noindent yields this code, which is simple enough to quote in its entirety: @example .file "fake.c" .text .p2align 4,,15 .globl factorial .type factorial, @@function factorial: .LFB0: .cfi_startproc .L2: cmpl $1, %edi jle .L8 movl $1, %edx jmp .L4 .p2align 4,,10 .p2align 3 .L6: movl %eax, %edi .L4: .L5: leal -1(%rdi), %eax imull %edi, %edx cmpl $1, %eax jne .L6 .L3: .L7: imull %edx, %eax ret .L8: movl %edi, %eax movl $1, %edx jmp .L7 .cfi_endproc .LFE0: .size factorial, .-factorial .ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2-%@{gcc_release@})" .section .note.GNU-stack,"",@@progbits @end example @noindent Note that the stack pushing and popping have been eliminated, as has the recursive call (in favor of an iteration). @node Putting it all together,Behind the curtain How does our code get optimized?,Examining the generated code,Tutorial part 4 Adding JIT-compilation to a toy interpreter @anchor{intro/tutorial04 putting-it-all-together}@anchor{44} @subsection Putting it all together The complete example can be seen in the source tree at @code{gcc/jit/docs/examples/tut04-toyvm/toyvm.c} along with a Makefile and a couple of sample .toy scripts: @example $ ls -al drwxrwxr-x. 2 david david 4096 Sep 19 17:46 . drwxrwxr-x. 3 david david 4096 Sep 19 15:26 .. -rw-rw-r--. 1 david david 615 Sep 19 12:43 factorial.toy -rw-rw-r--. 1 david david 834 Sep 19 13:08 fibonacci.toy -rw-rw-r--. 1 david david 238 Sep 19 14:22 Makefile -rw-rw-r--. 1 david david 16457 Sep 19 17:07 toyvm.c $ make toyvm g++ -Wall -g -o toyvm toyvm.c -lgccjit $ ./toyvm factorial.toy 10 interpreter result: 3628800 compiler result: 3628800 $ ./toyvm fibonacci.toy 10 interpreter result: 55 compiler result: 55 @end example @noindent @node Behind the curtain How does our code get optimized?,,Putting it all together,Tutorial part 4 Adding JIT-compilation to a toy interpreter @anchor{intro/tutorial04 behind-the-curtain-how-does-our-code-get-optimized}@anchor{45} @subsection Behind the curtain: How does our code get optimized? Our example is done, but you may be wondering about exactly how the compiler turned what we gave it into the machine code seen above. We can examine what the compiler is doing in detail by setting: @example gcc_jit_context_set_bool_option (state.ctxt, GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING, 1); gcc_jit_context_set_bool_option (state.ctxt, GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES, 1); @end example @noindent This will dump detailed information about the compiler's state to a directory under @code{/tmp}, and keep it from being cleaned up. The precise names and their formats of these files is subject to change. Higher optimization levels lead to more files. Here's what I saw (edited for brevity; there were almost 200 files): @example intermediate files written to /tmp/libgccjit-KPQbGw $ ls /tmp/libgccjit-KPQbGw/ fake.c.000i.cgraph fake.c.000i.type-inheritance fake.c.004t.gimple fake.c.007t.omplower fake.c.008t.lower fake.c.011t.eh fake.c.012t.cfg fake.c.014i.visibility fake.c.015i.early_local_cleanups fake.c.016t.ssa # etc @end example @noindent The gimple code is converted into Static Single Assignment form, with annotations for use when generating the debuginfo: @example $ less /tmp/libgccjit-KPQbGw/fake.c.016t.ssa @end example @noindent @example ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0) factorial (signed int arg) @{ signed int stack[8]; signed int stack_depth; signed int x; signed int y; _20; signed int _21; signed int _38; signed int _44; signed int _51; signed int _56; initial: stack_depth_3 = 0; # DEBUG stack_depth => stack_depth_3 stack[stack_depth_3] = arg_5(D); stack_depth_7 = stack_depth_3 + 1; # DEBUG stack_depth => stack_depth_7 # DEBUG instr0 => NULL # DEBUG /* DUP */ => NULL stack_depth_8 = stack_depth_7 + -1; # DEBUG stack_depth => stack_depth_8 x_9 = stack[stack_depth_8]; # DEBUG x => x_9 stack[stack_depth_8] = x_9; stack_depth_11 = stack_depth_8 + 1; # DEBUG stack_depth => stack_depth_11 stack[stack_depth_11] = x_9; stack_depth_13 = stack_depth_11 + 1; # DEBUG stack_depth => stack_depth_13 # DEBUG instr1 => NULL # DEBUG /* PUSH_CONST */ => NULL stack[stack_depth_13] = 2; /* etc; edited for brevity */ @end example @noindent We can perhaps better see the code by turning off @pxref{42,,GCC_JIT_BOOL_OPTION_DEBUGINFO} to suppress all those @code{DEBUG} statements, giving: @example $ less /tmp/libgccjit-1Hywc0/fake.c.016t.ssa @end example @noindent @example ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0) factorial (signed int arg) @{ signed int stack[8]; signed int stack_depth; signed int x; signed int y; _20; signed int _21; signed int _38; signed int _44; signed int _51; signed int _56; initial: stack_depth_3 = 0; stack[stack_depth_3] = arg_5(D); stack_depth_7 = stack_depth_3 + 1; stack_depth_8 = stack_depth_7 + -1; x_9 = stack[stack_depth_8]; stack[stack_depth_8] = x_9; stack_depth_11 = stack_depth_8 + 1; stack[stack_depth_11] = x_9; stack_depth_13 = stack_depth_11 + 1; stack[stack_depth_13] = 2; stack_depth_15 = stack_depth_13 + 1; stack_depth_16 = stack_depth_15 + -1; y_17 = stack[stack_depth_16]; stack_depth_18 = stack_depth_16 + -1; x_19 = stack[stack_depth_18]; _20 = x_19 < y_17; _21 = (signed int) _20; stack[stack_depth_18] = _21; stack_depth_23 = stack_depth_18 + 1; stack_depth_24 = stack_depth_23 + -1; x_25 = stack[stack_depth_24]; if (x_25 != 0) goto (instr9); else goto (instr4); instr4: /* DUP */: stack_depth_26 = stack_depth_24 + -1; x_27 = stack[stack_depth_26]; stack[stack_depth_26] = x_27; stack_depth_29 = stack_depth_26 + 1; stack[stack_depth_29] = x_27; stack_depth_31 = stack_depth_29 + 1; stack[stack_depth_31] = 1; stack_depth_33 = stack_depth_31 + 1; stack_depth_34 = stack_depth_33 + -1; y_35 = stack[stack_depth_34]; stack_depth_36 = stack_depth_34 + -1; x_37 = stack[stack_depth_36]; _38 = x_37 - y_35; stack[stack_depth_36] = _38; stack_depth_40 = stack_depth_36 + 1; stack_depth_41 = stack_depth_40 + -1; x_42 = stack[stack_depth_41]; _44 = factorial (x_42); stack[stack_depth_41] = _44; stack_depth_46 = stack_depth_41 + 1; stack_depth_47 = stack_depth_46 + -1; y_48 = stack[stack_depth_47]; stack_depth_49 = stack_depth_47 + -1; x_50 = stack[stack_depth_49]; _51 = x_50 * y_48; stack[stack_depth_49] = _51; stack_depth_53 = stack_depth_49 + 1; # stack_depth_1 = PHI instr9: /* RETURN */: stack_depth_54 = stack_depth_1 + -1; x_55 = stack[stack_depth_54]; _56 = x_55; stack =@{v@} @{CLOBBER@}; return _56; @} @end example @noindent Note in the above how all the @pxref{28,,gcc_jit_block} instances we created have been consolidated into just 3 blocks in GCC's internal representation: @code{initial}, @code{instr4} and @code{instr9}. @menu * Optimizing away stack manipulation:: * Elimination of tail recursion:: @end menu @node Optimizing away stack manipulation,Elimination of tail recursion,,Behind the curtain How does our code get optimized? @anchor{intro/tutorial04 optimizing-away-stack-manipulation}@anchor{46} @subsubsection Optimizing away stack manipulation Recall our simple implementation of stack operations. Let's examine how the stack operations are optimized away. After a pass of constant-propagation, the depth of the stack at each opcode can be determined at compile-time: @example $ less /tmp/libgccjit-1Hywc0/fake.c.021t.ccp1 @end example @noindent @example ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0) factorial (signed int arg) @{ signed int stack[8]; signed int stack_depth; signed int x; signed int y; _20; signed int _21; signed int _38; signed int _44; signed int _51; initial: stack[0] = arg_5(D); x_9 = stack[0]; stack[0] = x_9; stack[1] = x_9; stack[2] = 2; y_17 = stack[2]; x_19 = stack[1]; _20 = x_19 < y_17; _21 = (signed int) _20; stack[1] = _21; x_25 = stack[1]; if (x_25 != 0) goto (instr9); else goto (instr4); instr4: /* DUP */: x_27 = stack[0]; stack[0] = x_27; stack[1] = x_27; stack[2] = 1; y_35 = stack[2]; x_37 = stack[1]; _38 = x_37 - y_35; stack[1] = _38; x_42 = stack[1]; _44 = factorial (x_42); stack[1] = _44; y_48 = stack[1]; x_50 = stack[0]; _51 = x_50 * y_48; stack[0] = _51; instr9: /* RETURN */: x_55 = stack[0]; x_56 = x_55; stack =@{v@} @{CLOBBER@}; return x_56; @} @end example @noindent Note how, in the above, all those @code{stack_depth} values are now just constants: we're accessing specific stack locations at each opcode. The "esra" pass ("Early Scalar Replacement of Aggregates") breaks out our "stack" array into individual elements: @example $ less /tmp/libgccjit-1Hywc0/fake.c.024t.esra @end example @noindent @example ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0) Created a replacement for stack offset: 0, size: 32: stack$0 Created a replacement for stack offset: 32, size: 32: stack$1 Created a replacement for stack offset: 64, size: 32: stack$2 Symbols to be put in SSA form @{ D.89 D.90 D.91 @} Incremental SSA update started at block: 0 Number of blocks in CFG: 5 Number of blocks to update: 4 ( 80%) factorial (signed int arg) @{ signed int stack$2; signed int stack$1; signed int stack$0; signed int stack[8]; signed int stack_depth; signed int x; signed int y; _20; signed int _21; signed int _38; signed int _44; signed int _51; initial: stack$0_45 = arg_5(D); x_9 = stack$0_45; stack$0_39 = x_9; stack$1_32 = x_9; stack$2_30 = 2; y_17 = stack$2_30; x_19 = stack$1_32; _20 = x_19 < y_17; _21 = (signed int) _20; stack$1_28 = _21; x_25 = stack$1_28; if (x_25 != 0) goto (instr9); else goto (instr4); instr4: /* DUP */: x_27 = stack$0_39; stack$0_22 = x_27; stack$1_14 = x_27; stack$2_12 = 1; y_35 = stack$2_12; x_37 = stack$1_14; _38 = x_37 - y_35; stack$1_10 = _38; x_42 = stack$1_10; _44 = factorial (x_42); stack$1_6 = _44; y_48 = stack$1_6; x_50 = stack$0_22; _51 = x_50 * y_48; stack$0_1 = _51; # stack$0_52 = PHI instr9: /* RETURN */: x_55 = stack$0_52; x_56 = x_55; stack =@{v@} @{CLOBBER@}; return x_56; @} @end example @noindent Hence at this point, all those pushes and pops of the stack are now simply assignments to specific temporary variables. After some copy propagation, the stack manipulation has been completely optimized away: @example $ less /tmp/libgccjit-1Hywc0/fake.c.026t.copyprop1 @end example @noindent @example ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0) factorial (signed int arg) @{ signed int stack$2; signed int stack$1; signed int stack$0; signed int stack[8]; signed int stack_depth; signed int x; signed int y; _20; signed int _21; signed int _38; signed int _44; signed int _51; initial: stack$0_39 = arg_5(D); _20 = arg_5(D) <= 1; _21 = (signed int) _20; if (_21 != 0) goto (instr9); else goto (instr4); instr4: /* DUP */: _38 = arg_5(D) + -1; _44 = factorial (_38); _51 = arg_5(D) * _44; stack$0_1 = _51; # stack$0_52 = PHI instr9: /* RETURN */: stack =@{v@} @{CLOBBER@}; return stack$0_52; @} @end example @noindent Later on, another pass finally eliminated @code{stack_depth} local and the unused parts of the @cite{stack`} array altogether: @example $ less /tmp/libgccjit-1Hywc0/fake.c.036t.release_ssa @end example @noindent @example ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0) Released 44 names, 314.29%, removed 44 holes factorial (signed int arg) @{ signed int stack$0; signed int mult_acc_1; _5; signed int _6; signed int _7; signed int mul_tmp_10; signed int mult_acc_11; signed int mult_acc_13; # arg_9 = PHI # mult_acc_13 = PHI <1(0)> initial: : # arg_4 = PHI # mult_acc_1 = PHI _5 = arg_4 <= 1; _6 = (signed int) _5; if (_6 != 0) goto (instr9); else goto (instr4); instr4: /* DUP */: _7 = arg_4 + -1; mult_acc_11 = mult_acc_1 * arg_4; goto ; # stack$0_12 = PHI instr9: /* RETURN */: mul_tmp_10 = mult_acc_1 * stack$0_12; return mul_tmp_10; @} @end example @noindent @node Elimination of tail recursion,,Optimizing away stack manipulation,Behind the curtain How does our code get optimized? @anchor{intro/tutorial04 elimination-of-tail-recursion}@anchor{47} @subsubsection Elimination of tail recursion Another significant optimization is the detection that the call to @code{factorial} is tail recursion, which can be eliminated in favor of an iteration: @example $ less /tmp/libgccjit-1Hywc0/fake.c.030t.tailr1 @end example @noindent @example ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0) Symbols to be put in SSA form @{ D.88 @} Incremental SSA update started at block: 0 Number of blocks in CFG: 5 Number of blocks to update: 4 ( 80%) factorial (signed int arg) @{ signed int stack$2; signed int stack$1; signed int stack$0; signed int stack[8]; signed int stack_depth; signed int x; signed int y; signed int mult_acc_1; _20; signed int _21; signed int _38; signed int mul_tmp_44; signed int mult_acc_51; # arg_5 = PHI # mult_acc_1 = PHI <1(0), mult_acc_51(3)> initial: _20 = arg_5 <= 1; _21 = (signed int) _20; if (_21 != 0) goto (instr9); else goto (instr4); instr4: /* DUP */: _38 = arg_5 + -1; mult_acc_51 = mult_acc_1 * arg_5; goto (initial); # stack$0_52 = PHI instr9: /* RETURN */: stack =@{v@} @{CLOBBER@}; mul_tmp_44 = mult_acc_1 * stack$0_52; return mul_tmp_44; @} @end example @noindent @c Copyright (C) 2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Tutorial part 5 Implementing an Ahead-of-Time compiler,,Tutorial part 4 Adding JIT-compilation to a toy interpreter,Tutorial @anchor{intro/tutorial05 doc}@anchor{48}@anchor{intro/tutorial05 tutorial-part-5-implementing-an-ahead-of-time-compiler}@anchor{49} @section Tutorial part 5: Implementing an Ahead-of-Time compiler If you have a pre-existing language frontend that's compatible with libgccjit's license, it's possible to hook it up to libgccjit as a backend. In the previous example we showed how to do that for in-memory JIT-compilation, but libgccjit can also compile code directly to a file, allowing you to implement a more traditional ahead-of-time compiler ("JIT" is something of a misnomer for this use-case). The essential difference is to compile the context using @pxref{4a,,gcc_jit_context_compile_to_file()} rather than @pxref{15,,gcc_jit_context_compile()}. @menu * The "brainf" language:: * Converting a brainf script to libgccjit IR:: * Compiling a context to a file:: * Other forms of ahead-of-time-compilation:: @end menu @node The "brainf" language,Converting a brainf script to libgccjit IR,,Tutorial part 5 Implementing an Ahead-of-Time compiler @anchor{intro/tutorial05 the-brainf-language}@anchor{4b} @subsection The "brainf" language In this example we use libgccjit to construct an ahead-of-time compiler for an esoteric programming language that we shall refer to as "brainf". brainf scripts operate on an array of bytes, with a notional data pointer within the array. brainf is hard for humans to read, but it's trivial to write a parser for it, as there is no lexing; just a stream of bytes. The operations are: @multitable {xxxxxxxxxxxxxxxxxxxxxxxx} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} @headitem Character @tab Meaning @item @code{>} @tab @code{idx += 1} @item @code{<} @tab @code{idx -= 1} @item @code{+} @tab @code{data[idx] += 1} @item @code{-} @tab @code{data[idx] -= 1} @item @code{.} @tab @code{output (data[idx])} @item @code{,} @tab @code{data[idx] = input ()} @item @code{[} @tab loop until @code{data[idx] == 0} @item @code{]} @tab end of loop @item Anything else @tab ignored @end multitable Unlike the previous example, we'll implement an ahead-of-time compiler, which reads @code{.bf} scripts and outputs executables (though it would be trivial to have it run them JIT-compiled in-process). Here's what a simple @code{.bf} script looks like: @quotation @example [ Emit the uppercase alphabet ] cell 0 = 26 ++++++++++++++++++++++++++ cell 1 = 65 >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++< while cell#0 != 0 [ > . emit cell#1 + increment cell@@1 <- decrement cell@@0 ] @end example @noindent @end quotation @cartouche @quotation Note This example makes use of whitespace and comments for legibility, but could have been written as: @example ++++++++++++++++++++++++++ >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++< [>.+<-] @end example @noindent It's not a particularly useful language, except for providing compiler-writers with a test case that's easy to parse. The point is that you can use @pxref{4a,,gcc_jit_context_compile_to_file()} to use libgccjit as a backend for a pre-existing language frontend (provided that the pre-existing frontend is compatible with libgccjit's license). @end quotation @end cartouche @node Converting a brainf script to libgccjit IR,Compiling a context to a file,The "brainf" language,Tutorial part 5 Implementing an Ahead-of-Time compiler @anchor{intro/tutorial05 converting-a-brainf-script-to-libgccjit-ir}@anchor{4c} @subsection Converting a brainf script to libgccjit IR As before we write simple code to populate a @pxref{8,,gcc_jit_context *}. @quotation @example typedef struct bf_compiler @{ const char *filename; int line; int column; gcc_jit_context *ctxt; gcc_jit_type *void_type; gcc_jit_type *int_type; gcc_jit_type *byte_type; gcc_jit_type *array_type; gcc_jit_function *func_getchar; gcc_jit_function *func_putchar; gcc_jit_function *func; gcc_jit_block *curblock; gcc_jit_rvalue *int_zero; gcc_jit_rvalue *int_one; gcc_jit_rvalue *byte_zero; gcc_jit_rvalue *byte_one; gcc_jit_lvalue *data_cells; gcc_jit_lvalue *idx; int num_open_parens; gcc_jit_block *paren_test[MAX_OPEN_PARENS]; gcc_jit_block *paren_body[MAX_OPEN_PARENS]; gcc_jit_block *paren_after[MAX_OPEN_PARENS]; @} bf_compiler; /* Bail out, with a message on stderr. */ static void fatal_error (bf_compiler *bfc, const char *msg) @{ fprintf (stderr, "%s:%i:%i: %s", bfc->filename, bfc->line, bfc->column, msg); abort (); @} /* Get "data_cells[idx]" as an lvalue. */ static gcc_jit_lvalue * bf_get_current_data (bf_compiler *bfc, gcc_jit_location *loc) @{ return gcc_jit_context_new_array_access ( bfc->ctxt, loc, gcc_jit_lvalue_as_rvalue (bfc->data_cells), gcc_jit_lvalue_as_rvalue (bfc->idx)); @} /* Get "data_cells[idx] == 0" as a boolean rvalue. */ static gcc_jit_rvalue * bf_current_data_is_zero (bf_compiler *bfc, gcc_jit_location *loc) @{ return gcc_jit_context_new_comparison ( bfc->ctxt, loc, GCC_JIT_COMPARISON_EQ, gcc_jit_lvalue_as_rvalue (bf_get_current_data (bfc, loc)), bfc->byte_zero); @} /* Compile one bf character. */ static void bf_compile_char (bf_compiler *bfc, unsigned char ch) @{ gcc_jit_location *loc = gcc_jit_context_new_location (bfc->ctxt, bfc->filename, bfc->line, bfc->column); /* Turn this on to trace execution, by injecting putchar () of each source char. */ if (0) @{ gcc_jit_rvalue *arg = gcc_jit_context_new_rvalue_from_int ( bfc->ctxt, bfc->int_type, ch); gcc_jit_rvalue *call = gcc_jit_context_new_call (bfc->ctxt, loc, bfc->func_putchar, 1, &arg); gcc_jit_block_add_eval (bfc->curblock, loc, call); @} switch (ch) @{ case '>': gcc_jit_block_add_comment (bfc->curblock, loc, "'>': idx += 1;"); gcc_jit_block_add_assignment_op (bfc->curblock, loc, bfc->idx, GCC_JIT_BINARY_OP_PLUS, bfc->int_one); break; case '<': gcc_jit_block_add_comment (bfc->curblock, loc, "'<': idx -= 1;"); gcc_jit_block_add_assignment_op (bfc->curblock, loc, bfc->idx, GCC_JIT_BINARY_OP_MINUS, bfc->int_one); break; case '+': gcc_jit_block_add_comment (bfc->curblock, loc, "'+': data[idx] += 1;"); gcc_jit_block_add_assignment_op (bfc->curblock, loc, bf_get_current_data (bfc, loc), GCC_JIT_BINARY_OP_PLUS, bfc->byte_one); break; case '-': gcc_jit_block_add_comment (bfc->curblock, loc, "'-': data[idx] -= 1;"); gcc_jit_block_add_assignment_op (bfc->curblock, loc, bf_get_current_data (bfc, loc), GCC_JIT_BINARY_OP_MINUS, bfc->byte_one); break; case '.': @{ gcc_jit_rvalue *arg = gcc_jit_context_new_cast ( bfc->ctxt, loc, gcc_jit_lvalue_as_rvalue (bf_get_current_data (bfc, loc)), bfc->int_type); gcc_jit_rvalue *call = gcc_jit_context_new_call (bfc->ctxt, loc, bfc->func_putchar, 1, &arg); gcc_jit_block_add_comment (bfc->curblock, loc, "'.': putchar ((int)data[idx]);"); gcc_jit_block_add_eval (bfc->curblock, loc, call); @} break; case ',': @{ gcc_jit_rvalue *call = gcc_jit_context_new_call (bfc->ctxt, loc, bfc->func_getchar, 0, NULL); gcc_jit_block_add_comment ( bfc->curblock, loc, "',': data[idx] = (unsigned char)getchar ();"); gcc_jit_block_add_assignment (bfc->curblock, loc, bf_get_current_data (bfc, loc), gcc_jit_context_new_cast ( bfc->ctxt, loc, call, bfc->byte_type)); @} break; case '[': @{ gcc_jit_block *loop_test = gcc_jit_function_new_block (bfc->func, NULL); gcc_jit_block *on_zero = gcc_jit_function_new_block (bfc->func, NULL); gcc_jit_block *on_non_zero = gcc_jit_function_new_block (bfc->func, NULL); if (bfc->num_open_parens == MAX_OPEN_PARENS) fatal_error (bfc, "too many open parens"); gcc_jit_block_end_with_jump ( bfc->curblock, loc, loop_test); gcc_jit_block_add_comment ( loop_test, loc, "'['"); gcc_jit_block_end_with_conditional ( loop_test, loc, bf_current_data_is_zero (bfc, loc), on_zero, on_non_zero); bfc->paren_test[bfc->num_open_parens] = loop_test; bfc->paren_body[bfc->num_open_parens] = on_non_zero; bfc->paren_after[bfc->num_open_parens] = on_zero; bfc->num_open_parens += 1; bfc->curblock = on_non_zero; @} break; case ']': @{ gcc_jit_block_add_comment ( bfc->curblock, loc, "']'"); if (bfc->num_open_parens == 0) fatal_error (bfc, "mismatching parens"); bfc->num_open_parens -= 1; gcc_jit_block_end_with_jump ( bfc->curblock, loc, bfc->paren_test[bfc->num_open_parens]); bfc->curblock = bfc->paren_after[bfc->num_open_parens]; @} break; case '\n': bfc->line +=1; bfc->column = 0; break; @} if (ch != '\n') bfc->column += 1; @} /* Compile the given .bf file into a gcc_jit_context, containing a single "main" function suitable for compiling into an executable. */ gcc_jit_context * bf_compile (const char *filename) @{ bf_compiler bfc; FILE *f_in; int ch; memset (&bfc, 0, sizeof (bfc)); bfc.filename = filename; f_in = fopen (filename, "r"); if (!f_in) fatal_error (&bfc, "unable to open file"); bfc.line = 1; bfc.ctxt = gcc_jit_context_acquire (); gcc_jit_context_set_int_option ( bfc.ctxt, GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3); gcc_jit_context_set_bool_option ( bfc.ctxt, GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, 0); gcc_jit_context_set_bool_option ( bfc.ctxt, GCC_JIT_BOOL_OPTION_DEBUGINFO, 1); gcc_jit_context_set_bool_option ( bfc.ctxt, GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING, 0); gcc_jit_context_set_bool_option ( bfc.ctxt, GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES, 0); bfc.void_type = gcc_jit_context_get_type (bfc.ctxt, GCC_JIT_TYPE_VOID); bfc.int_type = gcc_jit_context_get_type (bfc.ctxt, GCC_JIT_TYPE_INT); bfc.byte_type = gcc_jit_context_get_type (bfc.ctxt, GCC_JIT_TYPE_UNSIGNED_CHAR); bfc.array_type = gcc_jit_context_new_array_type (bfc.ctxt, NULL, bfc.byte_type, 30000); bfc.func_getchar = gcc_jit_context_new_function (bfc.ctxt, NULL, GCC_JIT_FUNCTION_IMPORTED, bfc.int_type, "getchar", 0, NULL, 0); gcc_jit_param *param_c = gcc_jit_context_new_param (bfc.ctxt, NULL, bfc.int_type, "c"); bfc.func_putchar = gcc_jit_context_new_function (bfc.ctxt, NULL, GCC_JIT_FUNCTION_IMPORTED, bfc.void_type, "putchar", 1, ¶m_c, 0); bfc.func = make_main (bfc.ctxt); bfc.curblock = gcc_jit_function_new_block (bfc.func, "initial"); bfc.int_zero = gcc_jit_context_zero (bfc.ctxt, bfc.int_type); bfc.int_one = gcc_jit_context_one (bfc.ctxt, bfc.int_type); bfc.byte_zero = gcc_jit_context_zero (bfc.ctxt, bfc.byte_type); bfc.byte_one = gcc_jit_context_one (bfc.ctxt, bfc.byte_type); bfc.data_cells = gcc_jit_context_new_global (bfc.ctxt, NULL, GCC_JIT_GLOBAL_INTERNAL, bfc.array_type, "data_cells"); bfc.idx = gcc_jit_function_new_local (bfc.func, NULL, bfc.int_type, "idx"); gcc_jit_block_add_comment (bfc.curblock, NULL, "idx = 0;"); gcc_jit_block_add_assignment (bfc.curblock, NULL, bfc.idx, bfc.int_zero); bfc.num_open_parens = 0; while ( EOF != (ch = fgetc (f_in))) bf_compile_char (&bfc, (unsigned char)ch); gcc_jit_block_end_with_return (bfc.curblock, NULL, bfc.int_zero); fclose (f_in); return bfc.ctxt; @} @end example @noindent @end quotation @node Compiling a context to a file,Other forms of ahead-of-time-compilation,Converting a brainf script to libgccjit IR,Tutorial part 5 Implementing an Ahead-of-Time compiler @anchor{intro/tutorial05 compiling-a-context-to-a-file}@anchor{4d} @subsection Compiling a context to a file Unlike the previous tutorial, this time we'll compile the context directly to an executable, using @pxref{4a,,gcc_jit_context_compile_to_file()}: @example gcc_jit_context_compile_to_file (ctxt, GCC_JIT_OUTPUT_KIND_EXECUTABLE, output_file); @end example @noindent Here's the top-level of the compiler, which is what actually calls into @pxref{4a,,gcc_jit_context_compile_to_file()}: @quotation @example int main (int argc, char **argv) @{ const char *input_file; const char *output_file; gcc_jit_context *ctxt; const char *err; if (argc != 3) @{ fprintf (stderr, "%s: INPUT_FILE OUTPUT_FILE\n", argv[0]); return 1; @} input_file = argv[1]; output_file = argv[2]; ctxt = bf_compile (input_file); gcc_jit_context_compile_to_file (ctxt, GCC_JIT_OUTPUT_KIND_EXECUTABLE, output_file); err = gcc_jit_context_get_first_error (ctxt); if (err) @{ gcc_jit_context_release (ctxt); return 1; @} gcc_jit_context_release (ctxt); return 0; @} @end example @noindent @end quotation Note how once the context is populated you could trivially instead compile it to memory using @pxref{15,,gcc_jit_context_compile()} and run it in-process as in the previous tutorial. To create an executable, we need to export a @code{main} function. Here's how to create one from the JIT API: @quotation @example /* Make "main" function: int main (int argc, char **argv) @{ ... @} */ static gcc_jit_function * make_main (gcc_jit_context *ctxt) @{ gcc_jit_type *int_type = gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_INT); gcc_jit_param *param_argc = gcc_jit_context_new_param (ctxt, NULL, int_type, "argc"); gcc_jit_type *char_ptr_ptr_type = gcc_jit_type_get_pointer ( gcc_jit_type_get_pointer ( gcc_jit_context_get_type (ctxt, GCC_JIT_TYPE_CHAR))); gcc_jit_param *param_argv = gcc_jit_context_new_param (ctxt, NULL, char_ptr_ptr_type, "argv"); gcc_jit_param *params[2] = @{param_argc, param_argv@}; gcc_jit_function *func_main = gcc_jit_context_new_function (ctxt, NULL, GCC_JIT_FUNCTION_EXPORTED, int_type, "main", 2, params, 0); return func_main; @} @end example @noindent @end quotation @cartouche @quotation Note The above implementation ignores @code{argc} and @code{argv}, but you could make use of them by exposing @code{param_argc} and @code{param_argv} to the caller. @end quotation @end cartouche Upon compiling this C code, we obtain a bf-to-machine-code compiler; let's call it @code{bfc}: @example $ gcc \ tut05-bf.c \ -o bfc \ -lgccjit @end example @noindent We can now use @code{bfc} to compile .bf files into machine code executables: @example $ ./bfc \ emit-alphabet.bf \ a.out @end example @noindent which we can run directly: @example $ ./a.out ABCDEFGHIJKLMNOPQRSTUVWXYZ @end example @noindent Success! We can also inspect the generated executable using standard tools: @example $ objdump -d a.out |less @end example @noindent which shows that libgccjit has managed to optimize the function somewhat (for example, the runs of 26 and 65 increment operations have become integer constants 0x1a and 0x41): @example 0000000000400620
: 400620: 80 3d 39 0a 20 00 00 cmpb $0x0,0x200a39(%rip) # 601060 40062b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1) 400630: 48 83 ec 08 sub $0x8,%rsp 400634: 0f b6 05 26 0a 20 00 movzbl 0x200a26(%rip),%eax # 601061 40063b: c6 05 1e 0a 20 00 1a movb $0x1a,0x200a1e(%rip) # 601060 400642: 8d 78 41 lea 0x41(%rax),%edi 400645: 40 88 3d 15 0a 20 00 mov %dil,0x200a15(%rip) # 601061 40064c: 0f 1f 40 00 nopl 0x0(%rax) 400650: 40 0f b6 ff movzbl %dil,%edi 400654: e8 87 fe ff ff callq 4004e0 400659: 0f b6 05 01 0a 20 00 movzbl 0x200a01(%rip),%eax # 601061 400660: 80 2d f9 09 20 00 01 subb $0x1,0x2009f9(%rip) # 601060 400667: 8d 78 01 lea 0x1(%rax),%edi 40066a: 40 88 3d f0 09 20 00 mov %dil,0x2009f0(%rip) # 601061 400671: 75 dd jne 400650 400673: 31 c0 xor %eax,%eax 400675: 48 83 c4 08 add $0x8,%rsp 400679: c3 retq 40067a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) @end example @noindent We also set up debugging information (via @pxref{41,,gcc_jit_context_new_location()} and @pxref{42,,GCC_JIT_BOOL_OPTION_DEBUGINFO}), so it's possible to use @code{gdb} to singlestep through the generated binary and inspect the internal state @code{idx} and @code{data_cells}: @example (gdb) break main Breakpoint 1 at 0x400790 (gdb) run Starting program: a.out Breakpoint 1, 0x0000000000400790 in main (argc=1, argv=0x7fffffffe448) (gdb) stepi 0x0000000000400797 in main (argc=1, argv=0x7fffffffe448) (gdb) stepi 0x00000000004007a0 in main (argc=1, argv=0x7fffffffe448) (gdb) stepi 9 >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++< (gdb) list 4 5 cell 0 = 26 6 ++++++++++++++++++++++++++ 7 8 cell 1 = 65 9 >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++< 10 11 while cell#0 != 0 12 [ 13 > (gdb) n 6 ++++++++++++++++++++++++++ (gdb) n 9 >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++< (gdb) p idx $1 = 1 (gdb) p data_cells $2 = "\032", '\000' (gdb) p data_cells[0] $3 = 26 '\032' (gdb) p data_cells[1] $4 = 0 '\000' (gdb) list 4 5 cell 0 = 26 6 ++++++++++++++++++++++++++ 7 8 cell 1 = 65 9 >+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++< 10 11 while cell#0 != 0 12 [ 13 > @end example @noindent @node Other forms of ahead-of-time-compilation,,Compiling a context to a file,Tutorial part 5 Implementing an Ahead-of-Time compiler @anchor{intro/tutorial05 other-forms-of-ahead-of-time-compilation}@anchor{4e} @subsection Other forms of ahead-of-time-compilation The above demonstrates compiling a @pxref{8,,gcc_jit_context *} directly to an executable. It's also possible to compile it to an object file, and to a dynamic library. See the documentation of @pxref{4a,,gcc_jit_context_compile_to_file()} for more information. @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Topic Reference,C++ bindings for libgccjit,Tutorial,Top @anchor{topics/index doc}@anchor{4f}@anchor{topics/index topic-reference}@anchor{50} @chapter Topic Reference @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @menu * Compilation contexts:: * Objects:: * Types:: * Expressions:: * Creating and using functions:: * Source Locations:: * Compiling a context:: Compilation contexts * Lifetime-management:: * Thread-safety:: * Error-handling: Error-handling<2>. * Debugging:: * Options: Options<2>. Options * String Options:: * Boolean options:: * Integer options:: Types * Standard types:: * Pointers@comma{} const@comma{} and volatile: Pointers const and volatile. * Structures and unions:: Expressions * Rvalues:: * Lvalues:: * Working with pointers@comma{} structs and unions: Working with pointers structs and unions. Rvalues * Simple expressions:: * Unary Operations:: * Binary Operations:: * Comparisons:: * Function calls:: * Type-coercion:: Lvalues * Global variables:: Creating and using functions * Params:: * Functions:: * Blocks:: * Statements:: Source Locations * Faking it:: Compiling a context * In-memory compilation:: * Ahead-of-time compilation:: @end menu @node Compilation contexts,Objects,,Topic Reference @anchor{topics/contexts compilation-contexts}@anchor{51}@anchor{topics/contexts doc}@anchor{52} @section Compilation contexts @geindex gcc_jit_context (C type) @anchor{topics/contexts gcc_jit_context}@anchor{8} @deffn {C Type} gcc_jit_context @end deffn The top-level of the API is the @pxref{8,,gcc_jit_context} type. A @pxref{8,,gcc_jit_context} instance encapsulates the state of a compilation. You can set up options on it, and add types, functions and code. Invoking @pxref{15,,gcc_jit_context_compile()} on it gives you a @pxref{16,,gcc_jit_result}. @menu * Lifetime-management:: * Thread-safety:: * Error-handling: Error-handling<2>. * Debugging:: * Options: Options<2>. @end menu @node Lifetime-management,Thread-safety,,Compilation contexts @anchor{topics/contexts lifetime-management}@anchor{53} @subsection Lifetime-management Contexts are the unit of lifetime-management within the API: objects have their lifetime bounded by the context they are created within, and cleanup of such objects is done for you when the context is released. @geindex gcc_jit_context_acquire (C function) @anchor{topics/contexts gcc_jit_context_acquire}@anchor{9} @deffn {C Function} gcc_jit_context *gcc_jit_context_acquire (void) This function acquires a new @pxref{8,,gcc_jit_context *} instance, which is independent of any others that may be present within this process. @end deffn @geindex gcc_jit_context_release (C function) @anchor{topics/contexts gcc_jit_context_release}@anchor{c} @deffn {C Function} void gcc_jit_context_release (gcc_jit_context@w{ }*ctxt) This function releases all resources associated with the given context. Both the context itself and all of its @pxref{e,,gcc_jit_object *} instances are cleaned up. It should be called exactly once on a given context. It is invalid to use the context or any of its "contextual" objects after calling this. @example gcc_jit_context_release (ctxt); @end example @noindent @end deffn @geindex gcc_jit_context_new_child_context (C function) @anchor{topics/contexts gcc_jit_context_new_child_context}@anchor{54} @deffn {C Function} gcc_jit_context * gcc_jit_context_new_child_context (gcc_jit_context@w{ }*parent_ctxt) Given an existing JIT context, create a child context. The child inherits a copy of all option-settings from the parent. The child can reference objects created within the parent, but not vice-versa. The lifetime of the child context must be bounded by that of the parent: you should release a child context before releasing the parent context. If you use a function from a parent context within a child context, you have to compile the parent context before you can compile the child context, and the gcc_jit_result of the parent context must outlive the gcc_jit_result of the child context. This allows caching of shared initializations. For example, you could create types and declarations of global functions in a parent context once within a process, and then create child contexts whenever a function or loop becomes hot. Each such child context can be used for JIT-compiling just one function or loop, but can reference types and helper functions created within the parent context. Contexts can be arbitrarily nested, provided the above rules are followed, but it's probably not worth going above 2 or 3 levels, and there will likely be a performance hit for such nesting. @end deffn @node Thread-safety,Error-handling<2>,Lifetime-management,Compilation contexts @anchor{topics/contexts thread-safety}@anchor{55} @subsection Thread-safety Instances of @pxref{8,,gcc_jit_context *} created via @pxref{9,,gcc_jit_context_acquire()} are independent from each other: only one thread may use a given context at once, but multiple threads could each have their own contexts without needing locks. Contexts created via @pxref{54,,gcc_jit_context_new_child_context()} are related to their parent context. They can be partitioned by their ultimate ancestor into independent "family trees". Only one thread within a process may use a given "family tree" of such contexts at once, and if you're using multiple threads you should provide your own locking around entire such context partitions. @node Error-handling<2>,Debugging,Thread-safety,Compilation contexts @anchor{topics/contexts error-handling}@anchor{19}@anchor{topics/contexts id1}@anchor{56} @subsection Error-handling Various kinds of errors are possible when using the API, such as mismatched types in an assignment. You can only compile and get code from a context if no errors occur. Errors are printed on stderr and can be queried using @pxref{57,,gcc_jit_context_get_first_error()}. They typically contain the name of the API entrypoint where the error occurred, and pertinent information on the problem: @example ./buggy-program: error: gcc_jit_block_add_assignment: mismatching types: assignment to i (type: int) from "hello world" (type: const char *) @end example @noindent In general, if an error occurs when using an API entrypoint, the entrypoint returns NULL. You don't have to check everywhere for NULL results, since the API handles a NULL being passed in for any argument by issuing another error. This typically leads to a cascade of followup error messages, but is safe (albeit verbose). The first error message is usually the one to pay attention to, since it is likely to be responsible for all of the rest: @geindex gcc_jit_context_get_first_error (C function) @anchor{topics/contexts gcc_jit_context_get_first_error}@anchor{57} @deffn {C Function} const char * gcc_jit_context_get_first_error (gcc_jit_context@w{ }*ctxt) Returns the first error message that occurred on the context. The returned string is valid for the rest of the lifetime of the context. If no errors occurred, this will be NULL. @end deffn If you are wrapping the C API for a higher-level language that supports exception-handling, you may instead be interested in the last error that occurred on the context, so that you can embed this in an exception: @geindex gcc_jit_context_get_last_error (C function) @anchor{topics/contexts gcc_jit_context_get_last_error}@anchor{58} @deffn {C Function} const char * gcc_jit_context_get_last_error (gcc_jit_context@w{ }*ctxt) Returns the last error message that occurred on the context. If no errors occurred, this will be NULL. If non-NULL, the returned string is only guaranteed to be valid until the next call to libgccjit relating to this context. @end deffn @node Debugging,Options<2>,Error-handling<2>,Compilation contexts @anchor{topics/contexts debugging}@anchor{59} @subsection Debugging @geindex gcc_jit_context_dump_to_file (C function) @anchor{topics/contexts gcc_jit_context_dump_to_file}@anchor{5a} @deffn {C Function} void gcc_jit_context_dump_to_file (gcc_jit_context@w{ }*ctxt, const char@w{ }*path, int@w{ }update_locations) To help with debugging: dump a C-like representation to the given path, describing what's been set up on the context. If "update_locations" is true, then also set up @pxref{3b,,gcc_jit_location} information throughout the context, pointing at the dump file as if it were a source file. This may be of use in conjunction with @pxref{42,,GCC_JIT_BOOL_OPTION_DEBUGINFO} to allow stepping through the code in a debugger. @end deffn @geindex gcc_jit_context_set_logfile (C function) @anchor{topics/contexts gcc_jit_context_set_logfile}@anchor{5b} @deffn {C Function} void gcc_jit_context_set_logfile (gcc_jit_context@w{ }*ctxt, FILE@w{ }*logfile, int@w{ }flags, int@w{ }verbosity) To help with debugging; enable ongoing logging of the context's activity to the given file. For example, the following will enable logging to stderr. @example gcc_jit_context_set_logfile (ctxt, stderr, 0, 0); @end example @noindent Examples of information logged include: @itemize * @item API calls @item the various steps involved within compilation @item activity on any @pxref{16,,gcc_jit_result} instances created by the context @item activity within any child contexts @end itemize An example of a log can be seen @pxref{5c,,here}, though the precise format and kinds of information logged is subject to change. The caller remains responsible for closing @cite{logfile}, and it must not be closed until all users are released. In particular, note that child contexts and @pxref{16,,gcc_jit_result} instances created by the context will use the logfile. There may a performance cost for logging. You can turn off logging on @cite{ctxt} by passing @cite{NULL} for @cite{logfile}. Doing so only affects the context; it does not affect child contexts or @pxref{16,,gcc_jit_result} instances already created by the context. The parameters "flags" and "verbosity" are reserved for future expansion, and must be zero for now. @end deffn To contrast the above: @pxref{5a,,gcc_jit_context_dump_to_file()} dumps the current state of a context to the given path, whereas @pxref{5b,,gcc_jit_context_set_logfile()} enables on-going logging of future activies on a context to the given @cite{FILE *}. @geindex gcc_jit_context_dump_reproducer_to_file (C function) @anchor{topics/contexts gcc_jit_context_dump_reproducer_to_file}@anchor{5d} @deffn {C Function} void gcc_jit_context_dump_reproducer_to_file (gcc_jit_context@w{ }*ctxt, const char@w{ }*path) Write C source code into @cite{path} that can be compiled into a self-contained executable (i.e. with libgccjit as the only dependency). The generated code will attempt to replay the API calls that have been made into the given context. This may be useful when debugging the library or client code, for reducing a complicated recipe for reproducing a bug into a simpler form. For example, consider client code that parses some source file into some internal representation, and then walks this IR, calling into libgccjit. If this encounters a bug, a call to @cite{gcc_jit_context_dump_reproducer_to_file} will write out C code for a much simpler executable that performs the equivalent calls into libgccjit, without needing the client code and its data. Typically you need to supply @code{-Wno-unused-variable} when compiling the generated file (since the result of each API call is assigned to a unique variable within the generated C source, and not all are necessarily then used). @end deffn @geindex gcc_jit_context_enable_dump (C function) @anchor{topics/contexts gcc_jit_context_enable_dump}@anchor{5e} @deffn {C Function} void gcc_jit_context_enable_dump (gcc_jit_context@w{ }*ctxt, const char@w{ }*dumpname, char@w{ }**out_ptr) Enable the dumping of a specific set of internal state from the compilation, capturing the result in-memory as a buffer. Parameter "dumpname" corresponds to the equivalent gcc command-line option, without the "-fdump-" prefix. For example, to get the equivalent of @code{-fdump-tree-vrp1}, supply @code{"tree-vrp1"}: @example static char *dump_vrp1; void create_code (gcc_jit_context *ctxt) @{ gcc_jit_context_enable_dump (ctxt, "tree-vrp1", &dump_vrp1); /* (other API calls omitted for brevity) */ @} @end example @noindent The context directly stores the dumpname as a @code{(const char *)}, so the passed string must outlive the context. @pxref{15,,gcc_jit_context_compile()} will capture the dump as a dynamically-allocated buffer, writing it to @code{*out_ptr}. The caller becomes responsible for calling: @example free (*out_ptr) @end example @noindent each time that @pxref{15,,gcc_jit_context_compile()} is called. @code{*out_ptr} will be written to, either with the address of a buffer, or with @code{NULL} if an error occurred. @cartouche @quotation Warning This API entrypoint is likely to be less stable than the others. In particular, both the precise dumpnames, and the format and content of the dumps are subject to change. It exists primarily for writing the library's own test suite. @end quotation @end cartouche @end deffn @node Options<2>,,Debugging,Compilation contexts @anchor{topics/contexts options}@anchor{5f} @subsection Options @menu * String Options:: * Boolean options:: * Integer options:: @end menu @node String Options,Boolean options,,Options<2> @anchor{topics/contexts string-options}@anchor{60} @subsubsection String Options @geindex gcc_jit_context_set_str_option (C function) @anchor{topics/contexts gcc_jit_context_set_str_option}@anchor{61} @deffn {C Function} void gcc_jit_context_set_str_option (gcc_jit_context@w{ }*ctxt, enum gcc_jit_str_option@w{ }opt, const char@w{ }*value) Set a string option of the context. @geindex gcc_jit_str_option (C type) @anchor{topics/contexts gcc_jit_str_option}@anchor{62} @deffn {C Type} enum gcc_jit_str_option @end deffn There is currently just one string option: @geindex GCC_JIT_STR_OPTION_PROGNAME (C macro) @anchor{topics/contexts GCC_JIT_STR_OPTION_PROGNAME}@anchor{63} @deffn {C Macro} GCC_JIT_STR_OPTION_PROGNAME The name of the program, for use as a prefix when printing error messages to stderr. If @cite{NULL}, or default, "libgccjit.so" is used. @end deffn @end deffn @node Boolean options,Integer options,String Options,Options<2> @anchor{topics/contexts boolean-options}@anchor{64} @subsubsection Boolean options @geindex gcc_jit_context_set_bool_option (C function) @anchor{topics/contexts gcc_jit_context_set_bool_option}@anchor{1b} @deffn {C Function} void gcc_jit_context_set_bool_option (gcc_jit_context@w{ }*ctxt, enum gcc_jit_bool_option@w{ }opt, int@w{ }value) Set a boolean option of the context. Zero is "false" (the default), non-zero is "true". @geindex gcc_jit_bool_option (C type) @anchor{topics/contexts gcc_jit_bool_option}@anchor{65} @deffn {C Type} enum gcc_jit_bool_option @end deffn @geindex GCC_JIT_BOOL_OPTION_DEBUGINFO (C macro) @anchor{topics/contexts GCC_JIT_BOOL_OPTION_DEBUGINFO}@anchor{42} @deffn {C Macro} GCC_JIT_BOOL_OPTION_DEBUGINFO If true, @pxref{15,,gcc_jit_context_compile()} will attempt to do the right thing so that if you attach a debugger to the process, it will be able to inspect variables and step through your code. Note that you can't step through code unless you set up source location information for the code (by creating and passing in @pxref{3b,,gcc_jit_location} instances). @end deffn @geindex GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE (C macro) @anchor{topics/contexts GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE}@anchor{66} @deffn {C Macro} GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE If true, @pxref{15,,gcc_jit_context_compile()} will dump its initial "tree" representation of your code to stderr (before any optimizations). Here's some sample output (from the @cite{square} example): @example > side-effects head 0x7f4875a761e0 tail 0x7f4875a761f8 stmts 0x7f4875a62d20 0x7f4875a62d00 stmt side-effects arg 0 VOID file (null) line 0 col 0 align 1 context >> stmt unit size align 32 symtab 0 alias set -1 canonical type 0x7f4875a645e8 precision 32 min max pointer_to_this > side-effects arg 0 side-effects arg 0 arg 1 arg 0 arg 1 >>>> @end example @noindent @end deffn @geindex GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE (C macro) @anchor{topics/contexts GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE}@anchor{1c} @deffn {C Macro} GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE If true, @pxref{15,,gcc_jit_context_compile()} will dump the "gimple" representation of your code to stderr, before any optimizations are performed. The dump resembles C code: @example square (signed int i) @{ signed int D.56; entry: D.56 = i * i; return D.56; @} @end example @noindent @end deffn @geindex GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE (C macro) @anchor{topics/contexts GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE}@anchor{1d} @deffn {C Macro} GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE If true, @pxref{15,,gcc_jit_context_compile()} will dump the final generated code to stderr, in the form of assembly language: @example .file "fake.c" .text .globl square .type square, @@function square: .LFB0: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl %edi, -4(%rbp) .L2: movl -4(%rbp), %eax imull -4(%rbp), %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE0: .size square, .-square .ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.1-%@{gcc_release@})" .section .note.GNU-stack,"",@@progbits @end example @noindent @end deffn @geindex GCC_JIT_BOOL_OPTION_DUMP_SUMMARY (C macro) @anchor{topics/contexts GCC_JIT_BOOL_OPTION_DUMP_SUMMARY}@anchor{67} @deffn {C Macro} GCC_JIT_BOOL_OPTION_DUMP_SUMMARY If true, @pxref{15,,gcc_jit_context_compile()} will print information to stderr on the actions it is performing, followed by a profile showing the time taken and memory usage of each phase. @end deffn @geindex GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING (C macro) @anchor{topics/contexts GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING}@anchor{68} @deffn {C Macro} GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING If true, @pxref{15,,gcc_jit_context_compile()} will dump copious amount of information on what it's doing to various files within a temporary directory. Use @pxref{69,,GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES} (see below) to see the results. The files are intended to be human-readable, but the exact files and their formats are subject to change. @end deffn @geindex GCC_JIT_BOOL_OPTION_SELFCHECK_GC (C macro) @anchor{topics/contexts GCC_JIT_BOOL_OPTION_SELFCHECK_GC}@anchor{6a} @deffn {C Macro} GCC_JIT_BOOL_OPTION_SELFCHECK_GC If true, libgccjit will aggressively run its garbage collector, to shake out bugs (greatly slowing down the compile). This is likely to only be of interest to developers @emph{of} the library. It is used when running the selftest suite. @end deffn @geindex GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES (C macro) @anchor{topics/contexts GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES}@anchor{69} @deffn {C Macro} GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES If true, the @pxref{8,,gcc_jit_context} will not clean up intermediate files written to the filesystem, and will display their location on stderr. @end deffn @end deffn @node Integer options,,Boolean options,Options<2> @anchor{topics/contexts integer-options}@anchor{6b} @subsubsection Integer options @geindex gcc_jit_context_set_int_option (C function) @anchor{topics/contexts gcc_jit_context_set_int_option}@anchor{1e} @deffn {C Function} void gcc_jit_context_set_int_option (gcc_jit_context@w{ }*ctxt, enum gcc_jit_int_option@w{ }opt, int@w{ }value) Set an integer option of the context. @geindex gcc_jit_int_option (C type) @anchor{topics/contexts gcc_jit_int_option}@anchor{6c} @deffn {C Type} enum gcc_jit_int_option @end deffn There is currently just one integer option: @geindex GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL (C macro) @anchor{topics/contexts GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL}@anchor{1f} @deffn {C Macro} GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL How much to optimize the code. Valid values are 0-3, corresponding to GCC's command-line options -O0 through -O3. The default value is 0 (unoptimized). @end deffn @end deffn @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Objects,Types,Compilation contexts,Topic Reference @anchor{topics/objects objects}@anchor{6d}@anchor{topics/objects doc}@anchor{6e} @section Objects @geindex gcc_jit_object (C type) @anchor{topics/objects gcc_jit_object}@anchor{e} @deffn {C Type} gcc_jit_object @end deffn Almost every entity in the API (with the exception of @pxref{8,,gcc_jit_context *} and @pxref{16,,gcc_jit_result *}) is a "contextual" object, a @pxref{e,,gcc_jit_object *} A JIT object: @quotation @itemize * @item is associated with a @pxref{8,,gcc_jit_context *}. @item is automatically cleaned up for you when its context is released so you don't need to manually track and cleanup all objects, just the contexts. @end itemize @end quotation Although the API is C-based, there is a form of class hierarchy, which looks like this: @example +- gcc_jit_object +- gcc_jit_location +- gcc_jit_type +- gcc_jit_struct +- gcc_jit_field +- gcc_jit_function +- gcc_jit_block +- gcc_jit_rvalue +- gcc_jit_lvalue +- gcc_jit_param @end example @noindent There are casting methods for upcasting from subclasses to parent classes. For example, @pxref{d,,gcc_jit_type_as_object()}: @example gcc_jit_object *obj = gcc_jit_type_as_object (int_type); @end example @noindent The object "base class" has the following operations: @geindex gcc_jit_object_get_context (C function) @anchor{topics/objects gcc_jit_object_get_context}@anchor{6f} @deffn {C Function} gcc_jit_context *gcc_jit_object_get_context (gcc_jit_object@w{ }*obj) Which context is "obj" within? @end deffn @geindex gcc_jit_object_get_debug_string (C function) @anchor{topics/objects gcc_jit_object_get_debug_string}@anchor{f} @deffn {C Function} const char *gcc_jit_object_get_debug_string (gcc_jit_object@w{ }*obj) Generate a human-readable description for the given object. For example, @example printf ("obj: %s\n", gcc_jit_object_get_debug_string (obj)); @end example @noindent might give this text on stdout: @example obj: 4.0 * (float)i @end example @noindent @cartouche @quotation Note If you call this on an object, the @cite{const char *} buffer is allocated and generated on the first call for that object, and the buffer will have the same lifetime as the object i.e. it will exist until the object's context is released. @end quotation @end cartouche @end deffn @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Types,Expressions,Objects,Topic Reference @anchor{topics/types doc}@anchor{70}@anchor{topics/types types}@anchor{71} @section Types @geindex gcc_jit_type (C type) @anchor{topics/types gcc_jit_type}@anchor{a} @deffn {C Type} gcc_jit_type gcc_jit_type represents a type within the library. @end deffn @geindex gcc_jit_type_as_object (C function) @anchor{topics/types gcc_jit_type_as_object}@anchor{d} @deffn {C Function} gcc_jit_object *gcc_jit_type_as_object (gcc_jit_type@w{ }*type) Upcast a type to an object. @end deffn Types can be created in several ways: @itemize * @item fundamental types can be accessed using @pxref{b,,gcc_jit_context_get_type()}: @example gcc_jit_type *int_type = gcc_jit_context_get_type (GCC_JIT_TYPE_INT); @end example @noindent See @pxref{b,,gcc_jit_context_get_type()} for the available types. @item derived types can be accessed by using functions such as @pxref{72,,gcc_jit_type_get_pointer()} and @pxref{73,,gcc_jit_type_get_const()}: @example gcc_jit_type *const_int_star = gcc_jit_type_get_pointer (gcc_jit_type_get_const (int_type)); gcc_jit_type *int_const_star = gcc_jit_type_get_const (gcc_jit_type_get_pointer (int_type)); @end example @noindent @item by creating structures (see below). @end itemize @menu * Standard types:: * Pointers@comma{} const@comma{} and volatile: Pointers const and volatile. * Structures and unions:: @end menu @node Standard types,Pointers const and volatile,,Types @anchor{topics/types standard-types}@anchor{74} @subsection Standard types @geindex gcc_jit_context_get_type (C function) @anchor{topics/types gcc_jit_context_get_type}@anchor{b} @deffn {C Function} gcc_jit_type *gcc_jit_context_get_type (gcc_jit_context@w{ }*ctxt, enum gcc_jit_types@w{ }type_) Access a specific type. The available types are: @multitable {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} @headitem @cite{enum gcc_jit_types} value @tab Meaning @item @code{GCC_JIT_TYPE_VOID} @tab C's @code{void} type. @item @code{GCC_JIT_TYPE_VOID_PTR} @tab C's @code{void *}. @item @code{GCC_JIT_TYPE_BOOL} @tab C++'s @code{bool} type; also C99's @code{_Bool} type, aka @code{bool} if using stdbool.h. @item @code{GCC_JIT_TYPE_CHAR} @tab C's @code{char} (of some signedness) @item @code{GCC_JIT_TYPE_SIGNED_CHAR} @tab C's @code{signed char} @item @code{GCC_JIT_TYPE_UNSIGNED_CHAR} @tab C's @code{unsigned char} @item @code{GCC_JIT_TYPE_SHORT} @tab C's @code{short} (signed) @item @code{GCC_JIT_TYPE_UNSIGNED_SHORT} @tab C's @code{unsigned short} @item @code{GCC_JIT_TYPE_INT} @tab C's @code{int} (signed) @item @code{GCC_JIT_TYPE_UNSIGNED_INT} @tab C's @code{unsigned int} @item @code{GCC_JIT_TYPE_LONG} @tab C's @code{long} (signed) @item @code{GCC_JIT_TYPE_UNSIGNED_LONG} @tab C's @code{unsigned long} @item @code{GCC_JIT_TYPE_LONG_LONG} @tab C99's @code{long long} (signed) @item @code{GCC_JIT_TYPE_UNSIGNED_LONG_LONG} @tab C99's @code{unsigned long long} @item @code{GCC_JIT_TYPE_FLOAT} @tab @item @code{GCC_JIT_TYPE_DOUBLE} @tab @item @code{GCC_JIT_TYPE_LONG_DOUBLE} @tab @item @code{GCC_JIT_TYPE_CONST_CHAR_PTR} @tab C type: @code{(const char *)} @item @code{GCC_JIT_TYPE_SIZE_T} @tab C's @code{size_t} type @item @code{GCC_JIT_TYPE_FILE_PTR} @tab C type: @code{(FILE *)} @item @code{GCC_JIT_TYPE_COMPLEX_FLOAT} @tab C99's @code{_Complex float} @item @code{GCC_JIT_TYPE_COMPLEX_DOUBLE} @tab C99's @code{_Complex double} @item @code{GCC_JIT_TYPE_COMPLEX_LONG_DOUBLE} @tab C99's @code{_Complex long double} @end multitable @end deffn @geindex gcc_jit_context_get_int_type (C function) @anchor{topics/types gcc_jit_context_get_int_type}@anchor{75} @deffn {C Function} gcc_jit_type * gcc_jit_context_get_int_type (gcc_jit_context@w{ }*ctxt, int@w{ }num_bytes, int@w{ }is_signed) Access the integer type of the given size. @end deffn @node Pointers const and volatile,Structures and unions,Standard types,Types @anchor{topics/types pointers-const-and-volatile}@anchor{76} @subsection Pointers, @cite{const}, and @cite{volatile} @geindex gcc_jit_type_get_pointer (C function) @anchor{topics/types gcc_jit_type_get_pointer}@anchor{72} @deffn {C Function} gcc_jit_type *gcc_jit_type_get_pointer (gcc_jit_type@w{ }*type) Given type "T", get type "T*". @end deffn @geindex gcc_jit_type_get_const (C function) @anchor{topics/types gcc_jit_type_get_const}@anchor{73} @deffn {C Function} gcc_jit_type *gcc_jit_type_get_const (gcc_jit_type@w{ }*type) Given type "T", get type "const T". @end deffn @geindex gcc_jit_type_get_volatile (C function) @anchor{topics/types gcc_jit_type_get_volatile}@anchor{77} @deffn {C Function} gcc_jit_type *gcc_jit_type_get_volatile (gcc_jit_type@w{ }*type) Given type "T", get type "volatile T". @end deffn @geindex gcc_jit_context_new_array_type (C function) @anchor{topics/types gcc_jit_context_new_array_type}@anchor{78} @deffn {C Function} gcc_jit_type * gcc_jit_context_new_array_type (gcc_jit_context@w{ }*ctxt, gcc_jit_location@w{ }*loc, gcc_jit_type@w{ }*element_type, int@w{ }num_elements) Given type "T", get type "T[N]" (for a constant N). @end deffn @node Structures and unions,,Pointers const and volatile,Types @anchor{topics/types structures-and-unions}@anchor{79} @subsection Structures and unions @geindex gcc_jit_struct (C type) @anchor{topics/types gcc_jit_struct}@anchor{7a} @deffn {C Type} gcc_jit_struct @end deffn A compound type analagous to a C @cite{struct}. @geindex gcc_jit_field (C type) @anchor{topics/types gcc_jit_field}@anchor{7b} @deffn {C Type} gcc_jit_field @end deffn A field within a @pxref{7a,,gcc_jit_struct}. You can model C @cite{struct} types by creating @pxref{7a,,gcc_jit_struct *} and @pxref{7b,,gcc_jit_field} instances, in either order: @itemize * @item by creating the fields, then the structure. For example, to model: @example struct coord @{double x; double y; @}; @end example @noindent you could call: @example gcc_jit_field *field_x = gcc_jit_context_new_field (ctxt, NULL, double_type, "x"); gcc_jit_field *field_y = gcc_jit_context_new_field (ctxt, NULL, double_type, "y"); gcc_jit_field *fields[2] = @{field_x, field_y@}; gcc_jit_struct *coord = gcc_jit_context_new_struct_type (ctxt, NULL, "coord", 2, fields); @end example @noindent @item by creating the structure, then populating it with fields, typically to allow modelling self-referential structs such as: @example struct node @{ int m_hash; struct node *m_next; @}; @end example @noindent like this: @example gcc_jit_type *node = gcc_jit_context_new_opaque_struct (ctxt, NULL, "node"); gcc_jit_type *node_ptr = gcc_jit_type_get_pointer (node); gcc_jit_field *field_hash = gcc_jit_context_new_field (ctxt, NULL, int_type, "m_hash"); gcc_jit_field *field_next = gcc_jit_context_new_field (ctxt, NULL, node_ptr, "m_next"); gcc_jit_field *fields[2] = @{field_hash, field_next@}; gcc_jit_struct_set_fields (node, NULL, 2, fields); @end example @noindent @end itemize @geindex gcc_jit_context_new_field (C function) @anchor{topics/types gcc_jit_context_new_field}@anchor{7c} @deffn {C Function} gcc_jit_field * gcc_jit_context_new_field (gcc_jit_context@w{ }*ctxt, gcc_jit_location@w{ }*loc, gcc_jit_type@w{ }*type, const char@w{ }*name) Construct a new field, with the given type and name. @end deffn @geindex gcc_jit_field_as_object (C function) @anchor{topics/types gcc_jit_field_as_object}@anchor{7d} @deffn {C Function} gcc_jit_object * gcc_jit_field_as_object (gcc_jit_field@w{ }*field) Upcast from field to object. @end deffn @geindex gcc_jit_context_new_struct_type (C function) @anchor{topics/types gcc_jit_context_new_struct_type}@anchor{7e} @deffn {C Function} gcc_jit_struct *gcc_jit_context_new_struct_type (gcc_jit_context@w{ }*ctxt, gcc_jit_location@w{ }*loc, const char@w{ }*name, int@w{ }num_fields, gcc_jit_field@w{ }**fields) @quotation Construct a new struct type, with the given name and fields. @end quotation @end deffn @geindex gcc_jit_context_new_opaque_struct (C function) @anchor{topics/types gcc_jit_context_new_opaque_struct}@anchor{7f} @deffn {C Function} gcc_jit_struct * gcc_jit_context_new_opaque_struct (gcc_jit_context@w{ }*ctxt, gcc_jit_location@w{ }*loc, const char@w{ }*name) Construct a new struct type, with the given name, but without specifying the fields. The fields can be omitted (in which case the size of the struct is not known), or later specified using @pxref{80,,gcc_jit_struct_set_fields()}. @end deffn @geindex gcc_jit_struct_as_type (C function) @anchor{topics/types gcc_jit_struct_as_type}@anchor{81} @deffn {C Function} gcc_jit_type * gcc_jit_struct_as_type (gcc_jit_struct@w{ }*struct_type) Upcast from struct to type. @end deffn @geindex gcc_jit_struct_set_fields (C function) @anchor{topics/types gcc_jit_struct_set_fields}@anchor{80} @deffn {C Function} void gcc_jit_struct_set_fields (gcc_jit_struct@w{ }*struct_type, gcc_jit_location@w{ }*loc, int@w{ }num_fields, gcc_jit_field@w{ }**fields) Populate the fields of a formerly-opaque struct type. This can only be called once on a given struct type. @end deffn @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Expressions,Creating and using functions,Types,Topic Reference @anchor{topics/expressions expressions}@anchor{82}@anchor{topics/expressions doc}@anchor{83} @section Expressions @menu * Rvalues:: * Lvalues:: * Working with pointers@comma{} structs and unions: Working with pointers structs and unions. Rvalues * Simple expressions:: * Unary Operations:: * Binary Operations:: * Comparisons:: * Function calls:: * Type-coercion:: Lvalues * Global variables:: @end menu @node Rvalues,Lvalues,,Expressions @anchor{topics/expressions rvalues}@anchor{84} @subsection Rvalues @geindex gcc_jit_rvalue (C type) @anchor{topics/expressions gcc_jit_rvalue}@anchor{13} @deffn {C Type} gcc_jit_rvalue @end deffn A @pxref{13,,gcc_jit_rvalue *} is an expression that can be computed. It can be simple, e.g.: @quotation @itemize * @item an integer value e.g. @cite{0} or @cite{42} @item a string literal e.g. @cite{"Hello world"} @item a variable e.g. @cite{i}. These are also lvalues (see below). @end itemize @end quotation or compound e.g.: @quotation @itemize * @item a unary expression e.g. @cite{!cond} @item a binary expression e.g. @cite{(a + b)} @item a function call e.g. @cite{get_distance (&player_ship@comma{} &target)} @item etc. @end itemize @end quotation Every rvalue has an associated type, and the API will check to ensure that types match up correctly (otherwise the context will emit an error). @geindex gcc_jit_rvalue_get_type (C function) @anchor{topics/expressions gcc_jit_rvalue_get_type}@anchor{85} @deffn {C Function} gcc_jit_type *gcc_jit_rvalue_get_type (gcc_jit_rvalue@w{ }*rvalue) Get the type of this rvalue. @end deffn @geindex gcc_jit_rvalue_as_object (C function) @anchor{topics/expressions gcc_jit_rvalue_as_object}@anchor{14} @deffn {C Function} gcc_jit_object *gcc_jit_rvalue_as_object (gcc_jit_rvalue@w{ }*rvalue) Upcast the given rvalue to be an object. @end deffn @menu * Simple expressions:: * Unary Operations:: * Binary Operations:: * Comparisons:: * Function calls:: * Type-coercion:: @end menu @node Simple expressions,Unary Operations,,Rvalues @anchor{topics/expressions simple-expressions}@anchor{86} @subsubsection Simple expressions @geindex gcc_jit_context_new_rvalue_from_int (C function) @anchor{topics/expressions gcc_jit_context_new_rvalue_from_int}@anchor{30} @deffn {C Function} gcc_jit_rvalue * gcc_jit_context_new_rvalue_from_int (gcc_jit_context@w{ }*ctxt, gcc_jit_type@w{ }*numeric_type, int@w{ }value) Given a numeric type (integer or floating point), build an rvalue for the given constant @code{int} value. @end deffn @geindex gcc_jit_context_new_rvalue_from_long (C function) @anchor{topics/expressions gcc_jit_context_new_rvalue_from_long}@anchor{87} @deffn {C Function} gcc_jit_rvalue * gcc_jit_context_new_rvalue_from_long (gcc_jit_context@w{ }*ctxt, gcc_jit_type@w{ }*numeric_type, long@w{ }value) Given a numeric type (integer or floating point), build an rvalue for the given constant @code{long} value. @end deffn @geindex gcc_jit_context_zero (C function) @anchor{topics/expressions gcc_jit_context_zero}@anchor{2b} @deffn {C Function} gcc_jit_rvalue *gcc_jit_context_zero (gcc_jit_context@w{ }*ctxt, gcc_jit_type@w{ }*numeric_type) Given a numeric type (integer or floating point), get the rvalue for zero. Essentially this is just a shortcut for: @example gcc_jit_context_new_rvalue_from_int (ctxt, numeric_type, 0) @end example @noindent @end deffn @geindex gcc_jit_context_one (C function) @anchor{topics/expressions gcc_jit_context_one}@anchor{2f} @deffn {C Function} gcc_jit_rvalue *gcc_jit_context_one (gcc_jit_context@w{ }*ctxt, gcc_jit_type@w{ }*numeric_type) Given a numeric type (integer or floating point), get the rvalue for zero. Essentially this is just a shortcut for: @example gcc_jit_context_new_rvalue_from_int (ctxt, numeric_type, 1) @end example @noindent @end deffn @geindex gcc_jit_context_new_rvalue_from_double (C function) @anchor{topics/expressions gcc_jit_context_new_rvalue_from_double}@anchor{31} @deffn {C Function} gcc_jit_rvalue * gcc_jit_context_new_rvalue_from_double (gcc_jit_context@w{ }*ctxt, gcc_jit_type@w{ }*numeric_type, double@w{ }value) Given a numeric type (integer or floating point), build an rvalue for the given constant @code{double} value. @end deffn @geindex gcc_jit_context_new_rvalue_from_ptr (C function) @anchor{topics/expressions gcc_jit_context_new_rvalue_from_ptr}@anchor{88} @deffn {C Function} gcc_jit_rvalue * gcc_jit_context_new_rvalue_from_ptr (gcc_jit_context@w{ }*ctxt, gcc_jit_type@w{ }*pointer_type, void@w{ }*value) Given a pointer type, build an rvalue for the given address. @end deffn @geindex gcc_jit_context_null (C function) @anchor{topics/expressions gcc_jit_context_null}@anchor{89} @deffn {C Function} gcc_jit_rvalue *gcc_jit_context_null (gcc_jit_context@w{ }*ctxt, gcc_jit_type@w{ }*pointer_type) Given a pointer type, build an rvalue for @code{NULL}. Essentially this is just a shortcut for: @example gcc_jit_context_new_rvalue_from_ptr (ctxt, pointer_type, NULL) @end example @noindent @end deffn @geindex gcc_jit_context_new_string_literal (C function) @anchor{topics/expressions gcc_jit_context_new_string_literal}@anchor{8a} @deffn {C Function} gcc_jit_rvalue * gcc_jit_context_new_string_literal (gcc_jit_context@w{ }*ctxt, const char@w{ }*value) Generate an rvalue for the given NIL-terminated string, of type @code{GCC_JIT_TYPE_CONST_CHAR_PTR}. @end deffn @node Unary Operations,Binary Operations,Simple expressions,Rvalues @anchor{topics/expressions unary-operations}@anchor{8b} @subsubsection Unary Operations @geindex gcc_jit_context_new_unary_op (C function) @anchor{topics/expressions gcc_jit_context_new_unary_op}@anchor{8c} @deffn {C Function} gcc_jit_rvalue * gcc_jit_context_new_unary_op (gcc_jit_context@w{ }*ctxt, gcc_jit_location@w{ }*loc, enum gcc_jit_unary_op@w{ }op, gcc_jit_type@w{ }*result_type, gcc_jit_rvalue@w{ }*rvalue) Build a unary operation out of an input rvalue. @end deffn @geindex gcc_jit_unary_op (C type) @anchor{topics/expressions gcc_jit_unary_op}@anchor{8d} @deffn {C Type} enum gcc_jit_unary_op @end deffn The available unary operations are: @multitable {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} {xxxxxxxxxxxxxx} @headitem Unary Operation @tab C equivalent @item @pxref{8e,,GCC_JIT_UNARY_OP_MINUS} @tab @cite{-(EXPR)} @item @pxref{8f,,GCC_JIT_UNARY_OP_BITWISE_NEGATE} @tab @cite{~(EXPR)} @item @pxref{90,,GCC_JIT_UNARY_OP_LOGICAL_NEGATE} @tab @cite{!(EXPR)} @item @pxref{91,,GCC_JIT_UNARY_OP_ABS} @tab @cite{abs (EXPR)} @end multitable @geindex GCC_JIT_UNARY_OP_MINUS (C macro) @anchor{topics/expressions GCC_JIT_UNARY_OP_MINUS}@anchor{8e} @deffn {C Macro} GCC_JIT_UNARY_OP_MINUS Negate an arithmetic value; analogous to: @example -(EXPR) @end example @noindent in C. @end deffn @geindex GCC_JIT_UNARY_OP_BITWISE_NEGATE (C macro) @anchor{topics/expressions GCC_JIT_UNARY_OP_BITWISE_NEGATE}@anchor{8f} @deffn {C Macro} GCC_JIT_UNARY_OP_BITWISE_NEGATE Bitwise negation of an integer value (one's complement); analogous to: @example ~(EXPR) @end example @noindent in C. @end deffn @geindex GCC_JIT_UNARY_OP_LOGICAL_NEGATE (C macro) @anchor{topics/expressions GCC_JIT_UNARY_OP_LOGICAL_NEGATE}@anchor{90} @deffn {C Macro} GCC_JIT_UNARY_OP_LOGICAL_NEGATE Logical negation of an arithmetic or pointer value; analogous to: @example !(EXPR) @end example @noindent in C. @end deffn @geindex GCC_JIT_UNARY_OP_ABS (C macro) @anchor{topics/expressions GCC_JIT_UNARY_OP_ABS}@anchor{91} @deffn {C Macro} GCC_JIT_UNARY_OP_ABS Absolute value of an arithmetic expression; analogous to: @example abs (EXPR) @end example @noindent in C. @end deffn @node Binary Operations,Comparisons,Unary Operations,Rvalues @anchor{topics/expressions binary-operations}@anchor{92} @subsubsection Binary Operations @geindex gcc_jit_context_new_binary_op (C function) @anchor{topics/expressions gcc_jit_context_new_binary_op}@anchor{12} @deffn {C Function} gcc_jit_rvalue *gcc_jit_context_new_binary_op (gcc_jit_context@w{ }*ctxt, gcc_jit_location@w{ }*loc, enum gcc_jit_binary_op@w{ }op, gcc_jit_type@w{ }*result_type, gcc_jit_rvalue@w{ }*a, gcc_jit_rvalue@w{ }*b) Build a binary operation out of two constituent rvalues. @end deffn @geindex gcc_jit_binary_op (C type) @anchor{topics/expressions gcc_jit_binary_op}@anchor{93} @deffn {C Type} enum gcc_jit_binary_op @end deffn The available binary operations are: @multitable {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} {xxxxxxxxxxxxxx} @headitem Binary Operation @tab C equivalent @item @pxref{94,,GCC_JIT_BINARY_OP_PLUS} @tab @cite{x + y} @item @pxref{95,,GCC_JIT_BINARY_OP_MINUS} @tab @cite{x - y} @item @pxref{96,,GCC_JIT_BINARY_OP_MULT} @tab @cite{x * y} @item @pxref{97,,GCC_JIT_BINARY_OP_DIVIDE} @tab @cite{x / y} @item @pxref{98,,GCC_JIT_BINARY_OP_MODULO} @tab @cite{x % y} @item @pxref{99,,GCC_JIT_BINARY_OP_BITWISE_AND} @tab @cite{x & y} @item @pxref{9a,,GCC_JIT_BINARY_OP_BITWISE_XOR} @tab @cite{x ^ y} @item @pxref{9b,,GCC_JIT_BINARY_OP_BITWISE_OR} @tab @cite{x | y} @item @pxref{9c,,GCC_JIT_BINARY_OP_LOGICAL_AND} @tab @cite{x && y} @item @pxref{9d,,GCC_JIT_BINARY_OP_LOGICAL_OR} @tab @cite{x || y} @item @pxref{9e,,GCC_JIT_BINARY_OP_LSHIFT} @tab @cite{x << y} @item @pxref{9f,,GCC_JIT_BINARY_OP_RSHIFT} @tab @cite{x >> y} @end multitable @geindex GCC_JIT_BINARY_OP_PLUS (C macro) @anchor{topics/expressions GCC_JIT_BINARY_OP_PLUS}@anchor{94} @deffn {C Macro} GCC_JIT_BINARY_OP_PLUS Addition of arithmetic values; analogous to: @example (EXPR_A) + (EXPR_B) @end example @noindent in C. For pointer addition, use @pxref{a0,,gcc_jit_context_new_array_access()}. @end deffn @geindex GCC_JIT_BINARY_OP_MINUS (C macro) @anchor{topics/expressions GCC_JIT_BINARY_OP_MINUS}@anchor{95} @deffn {C Macro} GCC_JIT_BINARY_OP_MINUS Subtraction of arithmetic values; analogous to: @example (EXPR_A) - (EXPR_B) @end example @noindent in C. @end deffn @geindex GCC_JIT_BINARY_OP_MULT (C macro) @anchor{topics/expressions GCC_JIT_BINARY_OP_MULT}@anchor{96} @deffn {C Macro} GCC_JIT_BINARY_OP_MULT Multiplication of a pair of arithmetic values; analogous to: @example (EXPR_A) * (EXPR_B) @end example @noindent in C. @end deffn @geindex GCC_JIT_BINARY_OP_DIVIDE (C macro) @anchor{topics/expressions GCC_JIT_BINARY_OP_DIVIDE}@anchor{97} @deffn {C Macro} GCC_JIT_BINARY_OP_DIVIDE Quotient of division of arithmetic values; analogous to: @example (EXPR_A) / (EXPR_B) @end example @noindent in C. The result type affects the kind of division: if the result type is integer-based, then the result is truncated towards zero, whereas a floating-point result type indicates floating-point division. @end deffn @geindex GCC_JIT_BINARY_OP_MODULO (C macro) @anchor{topics/expressions GCC_JIT_BINARY_OP_MODULO}@anchor{98} @deffn {C Macro} GCC_JIT_BINARY_OP_MODULO Remainder of division of arithmetic values; analogous to: @example (EXPR_A) % (EXPR_B) @end example @noindent in C. @end deffn @geindex GCC_JIT_BINARY_OP_BITWISE_AND (C macro) @anchor{topics/expressions GCC_JIT_BINARY_OP_BITWISE_AND}@anchor{99} @deffn {C Macro} GCC_JIT_BINARY_OP_BITWISE_AND Bitwise AND; analogous to: @example (EXPR_A) & (EXPR_B) @end example @noindent in C. @end deffn @geindex GCC_JIT_BINARY_OP_BITWISE_XOR (C macro) @anchor{topics/expressions GCC_JIT_BINARY_OP_BITWISE_XOR}@anchor{9a} @deffn {C Macro} GCC_JIT_BINARY_OP_BITWISE_XOR Bitwise exclusive OR; analogous to: @example (EXPR_A) ^ (EXPR_B) @end example @noindent in C. @end deffn @geindex GCC_JIT_BINARY_OP_BITWISE_OR (C macro) @anchor{topics/expressions GCC_JIT_BINARY_OP_BITWISE_OR}@anchor{9b} @deffn {C Macro} GCC_JIT_BINARY_OP_BITWISE_OR Bitwise inclusive OR; analogous to: @example (EXPR_A) | (EXPR_B) @end example @noindent in C. @end deffn @geindex GCC_JIT_BINARY_OP_LOGICAL_AND (C macro) @anchor{topics/expressions GCC_JIT_BINARY_OP_LOGICAL_AND}@anchor{9c} @deffn {C Macro} GCC_JIT_BINARY_OP_LOGICAL_AND Logical AND; analogous to: @example (EXPR_A) && (EXPR_B) @end example @noindent in C. @end deffn @geindex GCC_JIT_BINARY_OP_LOGICAL_OR (C macro) @anchor{topics/expressions GCC_JIT_BINARY_OP_LOGICAL_OR}@anchor{9d} @deffn {C Macro} GCC_JIT_BINARY_OP_LOGICAL_OR Logical OR; analogous to: @example (EXPR_A) || (EXPR_B) @end example @noindent in C. @end deffn @geindex GCC_JIT_BINARY_OP_LSHIFT (C macro) @anchor{topics/expressions GCC_JIT_BINARY_OP_LSHIFT}@anchor{9e} @deffn {C Macro} GCC_JIT_BINARY_OP_LSHIFT Left shift; analogous to: @example (EXPR_A) << (EXPR_B) @end example @noindent in C. @end deffn @geindex GCC_JIT_BINARY_OP_RSHIFT (C macro) @anchor{topics/expressions GCC_JIT_BINARY_OP_RSHIFT}@anchor{9f} @deffn {C Macro} GCC_JIT_BINARY_OP_RSHIFT Right shift; analogous to: @example (EXPR_A) >> (EXPR_B) @end example @noindent in C. @end deffn @node Comparisons,Function calls,Binary Operations,Rvalues @anchor{topics/expressions comparisons}@anchor{a1} @subsubsection Comparisons @geindex gcc_jit_context_new_comparison (C function) @anchor{topics/expressions gcc_jit_context_new_comparison}@anchor{2c} @deffn {C Function} gcc_jit_rvalue * gcc_jit_context_new_comparison (gcc_jit_context@w{ }*ctxt, gcc_jit_location@w{ }*loc, enum gcc_jit_comparison@w{ }op, gcc_jit_rvalue@w{ }*a, gcc_jit_rvalue@w{ }*b) Build a boolean rvalue out of the comparison of two other rvalues. @end deffn @geindex gcc_jit_comparison (C type) @anchor{topics/expressions gcc_jit_comparison}@anchor{a2} @deffn {C Type} enum gcc_jit_comparison @end deffn @multitable {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} {xxxxxxxxxxxxxx} @headitem Comparison @tab C equivalent @item @code{GCC_JIT_COMPARISON_EQ} @tab @cite{x == y} @item @code{GCC_JIT_COMPARISON_NE} @tab @cite{x != y} @item @code{GCC_JIT_COMPARISON_LT} @tab @cite{x < y} @item @code{GCC_JIT_COMPARISON_LE} @tab @cite{x <= y} @item @code{GCC_JIT_COMPARISON_GT} @tab @cite{x > y} @item @code{GCC_JIT_COMPARISON_GE} @tab @cite{x >= y} @end multitable @node Function calls,Type-coercion,Comparisons,Rvalues @anchor{topics/expressions function-calls}@anchor{a3} @subsubsection Function calls @geindex gcc_jit_context_new_call (C function) @anchor{topics/expressions gcc_jit_context_new_call}@anchor{a4} @deffn {C Function} gcc_jit_rvalue * gcc_jit_context_new_call (gcc_jit_context@w{ }*ctxt, gcc_jit_location@w{ }*loc, gcc_jit_function@w{ }*func, int@w{ }numargs, gcc_jit_rvalue@w{ }**args) Given a function and the given table of argument rvalues, construct a call to the function, with the result as an rvalue. @cartouche @quotation Note @pxref{a4,,gcc_jit_context_new_call()} merely builds a @pxref{13,,gcc_jit_rvalue} i.e. an expression that can be evaluated, perhaps as part of a more complicated expression. The call @emph{won't} happen unless you add a statement to a function that evaluates the expression. For example, if you want to call a function and discard the result (or to call a function with @code{void} return type), use @pxref{a5,,gcc_jit_block_add_eval()}: @example /* Add "(void)printf (arg0, arg1);". */ gcc_jit_block_add_eval ( block, NULL, gcc_jit_context_new_call ( ctxt, NULL, printf_func, 2, args)); @end example @noindent @end quotation @end cartouche @end deffn @node Type-coercion,,Function calls,Rvalues @anchor{topics/expressions type-coercion}@anchor{a6} @subsubsection Type-coercion @geindex gcc_jit_context_new_cast (C function) @anchor{topics/expressions gcc_jit_context_new_cast}@anchor{a7} @deffn {C Function} gcc_jit_rvalue * gcc_jit_context_new_cast (gcc_jit_context@w{ }*ctxt, gcc_jit_location@w{ }*loc, gcc_jit_rvalue@w{ }*rvalue, gcc_jit_type@w{ }*type) Given an rvalue of T, construct another rvalue of another type. Currently only a limited set of conversions are possible: @quotation @itemize * @item int <-> float @item int <-> bool @item P* <-> Q*, for pointer types P and Q @end itemize @end quotation @end deffn @node Lvalues,Working with pointers structs and unions,Rvalues,Expressions @anchor{topics/expressions lvalues}@anchor{a8} @subsection Lvalues @geindex gcc_jit_lvalue (C type) @anchor{topics/expressions gcc_jit_lvalue}@anchor{24} @deffn {C Type} gcc_jit_lvalue @end deffn An lvalue is something that can of the @emph{left}-hand side of an assignment: a storage area (such as a variable). It is also usable as an rvalue, where the rvalue is computed by reading from the storage area. @geindex gcc_jit_lvalue_as_object (C function) @anchor{topics/expressions gcc_jit_lvalue_as_object}@anchor{a9} @deffn {C Function} gcc_jit_object * gcc_jit_lvalue_as_object (gcc_jit_lvalue@w{ }*lvalue) Upcast an lvalue to be an object. @end deffn @geindex gcc_jit_lvalue_as_rvalue (C function) @anchor{topics/expressions gcc_jit_lvalue_as_rvalue}@anchor{aa} @deffn {C Function} gcc_jit_rvalue * gcc_jit_lvalue_as_rvalue (gcc_jit_lvalue@w{ }*lvalue) Upcast an lvalue to be an rvalue. @end deffn @geindex gcc_jit_lvalue_get_address (C function) @anchor{topics/expressions gcc_jit_lvalue_get_address}@anchor{ab} @deffn {C Function} gcc_jit_rvalue * gcc_jit_lvalue_get_address (gcc_jit_lvalue@w{ }*lvalue, gcc_jit_location@w{ }*loc) Take the address of an lvalue; analogous to: @example &(EXPR) @end example @noindent in C. @end deffn @menu * Global variables:: @end menu @node Global variables,,,Lvalues @anchor{topics/expressions global-variables}@anchor{ac} @subsubsection Global variables @geindex gcc_jit_context_new_global (C function) @anchor{topics/expressions gcc_jit_context_new_global}@anchor{ad} @deffn {C Function} gcc_jit_lvalue * gcc_jit_context_new_global (gcc_jit_context@w{ }*ctxt, gcc_jit_location@w{ }*loc, enum gcc_jit_global_kind@w{ }kind, gcc_jit_type@w{ }*type, const char@w{ }*name) Add a new global variable of the given type and name to the context. The "kind" parameter determines the visibility of the "global" outside of the @pxref{16,,gcc_jit_result}: @geindex gcc_jit_global_kind (C type) @anchor{topics/expressions gcc_jit_global_kind}@anchor{ae} @deffn {C Type} enum gcc_jit_global_kind @end deffn @geindex GCC_JIT_GLOBAL_EXPORTED (C macro) @anchor{topics/expressions GCC_JIT_GLOBAL_EXPORTED}@anchor{af} @deffn {C Macro} GCC_JIT_GLOBAL_EXPORTED Global is defined by the client code and is visible by name outside of this JIT context via @pxref{b0,,gcc_jit_result_get_global()} (and this value is required for the global to be accessible via that entrypoint). @end deffn @geindex GCC_JIT_GLOBAL_INTERNAL (C macro) @anchor{topics/expressions GCC_JIT_GLOBAL_INTERNAL}@anchor{b1} @deffn {C Macro} GCC_JIT_GLOBAL_INTERNAL Global is defined by the client code, but is invisible outside of it. Analogous to a "static" global within a .c file. Specifically, the variable will only be visible within this context and within child contexts. @end deffn @geindex GCC_JIT_GLOBAL_IMPORTED (C macro) @anchor{topics/expressions GCC_JIT_GLOBAL_IMPORTED}@anchor{b2} @deffn {C Macro} GCC_JIT_GLOBAL_IMPORTED Global is not defined by the client code; we're merely referring to it. Analogous to using an "extern" global from a header file. @end deffn @end deffn @node Working with pointers structs and unions,,Lvalues,Expressions @anchor{topics/expressions working-with-pointers-structs-and-unions}@anchor{b3} @subsection Working with pointers, structs and unions @geindex gcc_jit_rvalue_dereference (C function) @anchor{topics/expressions gcc_jit_rvalue_dereference}@anchor{b4} @deffn {C Function} gcc_jit_lvalue * gcc_jit_rvalue_dereference (gcc_jit_rvalue@w{ }*rvalue, gcc_jit_location@w{ }*loc) Given an rvalue of pointer type @code{T *}, dereferencing the pointer, getting an lvalue of type @code{T}. Analogous to: @example *(EXPR) @end example @noindent in C. @end deffn Field access is provided separately for both lvalues and rvalues. @geindex gcc_jit_lvalue_access_field (C function) @anchor{topics/expressions gcc_jit_lvalue_access_field}@anchor{b5} @deffn {C Function} gcc_jit_lvalue * gcc_jit_lvalue_access_field (gcc_jit_lvalue@w{ }*struct_, gcc_jit_location@w{ }*loc, gcc_jit_field@w{ }*field) Given an lvalue of struct or union type, access the given field, getting an lvalue of the field's type. Analogous to: @example (EXPR).field = ...; @end example @noindent in C. @end deffn @geindex gcc_jit_rvalue_access_field (C function) @anchor{topics/expressions gcc_jit_rvalue_access_field}@anchor{b6} @deffn {C Function} gcc_jit_rvalue * gcc_jit_rvalue_access_field (gcc_jit_rvalue@w{ }*struct_, gcc_jit_location@w{ }*loc, gcc_jit_field@w{ }*field) Given an rvalue of struct or union type, access the given field as an rvalue. Analogous to: @example (EXPR).field @end example @noindent in C. @end deffn @geindex gcc_jit_rvalue_dereference_field (C function) @anchor{topics/expressions gcc_jit_rvalue_dereference_field}@anchor{b7} @deffn {C Function} gcc_jit_lvalue * gcc_jit_rvalue_dereference_field (gcc_jit_rvalue@w{ }*ptr, gcc_jit_location@w{ }*loc, gcc_jit_field@w{ }*field) Given an rvalue of pointer type @code{T *} where T is of struct or union type, access the given field as an lvalue. Analogous to: @example (EXPR)->field @end example @noindent in C, itself equivalent to @code{(*EXPR).FIELD}. @end deffn @geindex gcc_jit_context_new_array_access (C function) @anchor{topics/expressions gcc_jit_context_new_array_access}@anchor{a0} @deffn {C Function} gcc_jit_lvalue * gcc_jit_context_new_array_access (gcc_jit_context@w{ }*ctxt, gcc_jit_location@w{ }*loc, gcc_jit_rvalue@w{ }*ptr, gcc_jit_rvalue@w{ }*index) Given an rvalue of pointer type @code{T *}, get at the element @cite{T} at the given index, using standard C array indexing rules i.e. each increment of @code{index} corresponds to @code{sizeof(T)} bytes. Analogous to: @example PTR[INDEX] @end example @noindent in C (or, indeed, to @code{PTR + INDEX}). @end deffn @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Creating and using functions,Source Locations,Expressions,Topic Reference @anchor{topics/functions doc}@anchor{b8}@anchor{topics/functions creating-and-using-functions}@anchor{b9} @section Creating and using functions @menu * Params:: * Functions:: * Blocks:: * Statements:: @end menu @node Params,Functions,,Creating and using functions @anchor{topics/functions params}@anchor{ba} @subsection Params @geindex gcc_jit_param (C type) @anchor{topics/functions gcc_jit_param}@anchor{25} @deffn {C Type} gcc_jit_param A @cite{gcc_jit_param} represents a parameter to a function. @end deffn @geindex gcc_jit_context_new_param (C function) @anchor{topics/functions gcc_jit_context_new_param}@anchor{10} @deffn {C Function} gcc_jit_param * gcc_jit_context_new_param (gcc_jit_context@w{ }*ctxt, gcc_jit_location@w{ }*loc, gcc_jit_type@w{ }*type, const char@w{ }*name) In preparation for creating a function, create a new parameter of the given type and name. @end deffn Parameters are lvalues, and thus are also rvalues (and objects), so the following upcasts are available: @geindex gcc_jit_param_as_lvalue (C function) @anchor{topics/functions gcc_jit_param_as_lvalue}@anchor{bb} @deffn {C Function} gcc_jit_lvalue * gcc_jit_param_as_lvalue (gcc_jit_param@w{ }*param) Upcasting from param to lvalue. @end deffn @geindex gcc_jit_param_as_rvalue (C function) @anchor{topics/functions gcc_jit_param_as_rvalue}@anchor{bc} @deffn {C Function} gcc_jit_rvalue * gcc_jit_param_as_rvalue (gcc_jit_param@w{ }*param) Upcasting from param to rvalue. @end deffn @geindex gcc_jit_param_as_object (C function) @anchor{topics/functions gcc_jit_param_as_object}@anchor{bd} @deffn {C Function} gcc_jit_object * gcc_jit_param_as_object (gcc_jit_param@w{ }*param) Upcasting from param to object. @end deffn @node Functions,Blocks,Params,Creating and using functions @anchor{topics/functions functions}@anchor{be} @subsection Functions @geindex gcc_jit_function (C type) @anchor{topics/functions gcc_jit_function}@anchor{29} @deffn {C Type} gcc_jit_function A @cite{gcc_jit_function} represents a function - either one that we're creating ourselves, or one that we're referencing. @end deffn @geindex gcc_jit_context_new_function (C function) @anchor{topics/functions gcc_jit_context_new_function}@anchor{11} @deffn {C Function} gcc_jit_function * gcc_jit_context_new_function (gcc_jit_context@w{ }*ctxt, gcc_jit_location@w{ }*loc, enum gcc_jit_function_kind@w{ }kind, gcc_jit_type@w{ }*return_type, const char@w{ }*name, int@w{ }num_params, gcc_jit_param@w{ }**params, int@w{ }is_variadic) Create a gcc_jit_function with the given name and parameters. @geindex gcc_jit_function_kind (C type) @anchor{topics/functions gcc_jit_function_kind}@anchor{bf} @deffn {C Type} enum gcc_jit_function_kind @end deffn This enum controls the kind of function created, and has the following values: @quotation @geindex GCC_JIT_FUNCTION_EXPORTED (C macro) @anchor{topics/functions GCC_JIT_FUNCTION_EXPORTED}@anchor{c0} @deffn {C Macro} GCC_JIT_FUNCTION_EXPORTED Function is defined by the client code and visible by name outside of the JIT. This value is required if you want to extract machine code for this function from a @pxref{16,,gcc_jit_result} via @pxref{17,,gcc_jit_result_get_code()}. @end deffn @geindex GCC_JIT_FUNCTION_INTERNAL (C macro) @anchor{topics/functions GCC_JIT_FUNCTION_INTERNAL}@anchor{c1} @deffn {C Macro} GCC_JIT_FUNCTION_INTERNAL Function is defined by the client code, but is invisible outside of the JIT. Analogous to a "static" function. @end deffn @geindex GCC_JIT_FUNCTION_IMPORTED (C macro) @anchor{topics/functions GCC_JIT_FUNCTION_IMPORTED}@anchor{c2} @deffn {C Macro} GCC_JIT_FUNCTION_IMPORTED Function is not defined by the client code; we're merely referring to it. Analogous to using an "extern" function from a header file. @end deffn @geindex GCC_JIT_FUNCTION_ALWAYS_INLINE (C macro) @anchor{topics/functions GCC_JIT_FUNCTION_ALWAYS_INLINE}@anchor{c3} @deffn {C Macro} GCC_JIT_FUNCTION_ALWAYS_INLINE Function is only ever inlined into other functions, and is invisible outside of the JIT. Analogous to prefixing with @code{inline} and adding @code{__attribute__((always_inline))} Inlining will only occur when the optimization level is above 0; when optimization is off, this is essentially the same as GCC_JIT_FUNCTION_INTERNAL. @end deffn @end quotation @end deffn @geindex gcc_jit_context_get_builtin_function (C function) @anchor{topics/functions gcc_jit_context_get_builtin_function}@anchor{c4} @deffn {C Function} gcc_jit_function *gcc_jit_context_get_builtin_function (gcc_jit_context@w{ }*ctxt, const char@w{ }*name) @end deffn @geindex gcc_jit_function_as_object (C function) @anchor{topics/functions gcc_jit_function_as_object}@anchor{c5} @deffn {C Function} gcc_jit_object * gcc_jit_function_as_object (gcc_jit_function@w{ }*func) Upcasting from function to object. @end deffn @geindex gcc_jit_function_get_param (C function) @anchor{topics/functions gcc_jit_function_get_param}@anchor{c6} @deffn {C Function} gcc_jit_param * gcc_jit_function_get_param (gcc_jit_function@w{ }*func, int@w{ }index) Get the param of the given index (0-based). @end deffn @geindex gcc_jit_function_dump_to_dot (C function) @anchor{topics/functions gcc_jit_function_dump_to_dot}@anchor{33} @deffn {C Function} void gcc_jit_function_dump_to_dot (gcc_jit_function@w{ }*func, const char@w{ }*path) Emit the function in graphviz format to the given path. @end deffn @geindex gcc_jit_function_new_local (C function) @anchor{topics/functions gcc_jit_function_new_local}@anchor{26} @deffn {C Function} gcc_jit_lvalue * gcc_jit_function_new_local (gcc_jit_function@w{ }*func, gcc_jit_location@w{ }*loc, gcc_jit_type@w{ }*type, const char@w{ }*name) Create a new local variable within the function, of the given type and name. @end deffn @node Blocks,Statements,Functions,Creating and using functions @anchor{topics/functions blocks}@anchor{c7} @subsection Blocks @geindex gcc_jit_block (C type) @anchor{topics/functions gcc_jit_block}@anchor{28} @deffn {C Type} gcc_jit_block A @cite{gcc_jit_block} represents a basic block within a function i.e. a sequence of statements with a single entry point and a single exit point. The first basic block that you create within a function will be the entrypoint. Each basic block that you create within a function must be terminated, either with a conditional, a jump, or a return. It's legal to have multiple basic blocks that return within one function. @end deffn @geindex gcc_jit_function_new_block (C function) @anchor{topics/functions gcc_jit_function_new_block}@anchor{c8} @deffn {C Function} gcc_jit_block * gcc_jit_function_new_block (gcc_jit_function@w{ }*func, const char@w{ }*name) Create a basic block of the given name. The name may be NULL, but providing meaningful names is often helpful when debugging: it may show up in dumps of the internal representation, and in error messages. @end deffn @geindex gcc_jit_block_as_object (C function) @anchor{topics/functions gcc_jit_block_as_object}@anchor{c9} @deffn {C Function} gcc_jit_object * gcc_jit_block_as_object (gcc_jit_block@w{ }*block) Upcast from block to object. @end deffn @geindex gcc_jit_block_get_function (C function) @anchor{topics/functions gcc_jit_block_get_function}@anchor{ca} @deffn {C Function} gcc_jit_function * gcc_jit_block_get_function (gcc_jit_block@w{ }*block) Which function is this block within? @end deffn @node Statements,,Blocks,Creating and using functions @anchor{topics/functions statements}@anchor{cb} @subsection Statements @geindex gcc_jit_block_add_eval (C function) @anchor{topics/functions gcc_jit_block_add_eval}@anchor{a5} @deffn {C Function} void gcc_jit_block_add_eval (gcc_jit_block@w{ }*block, gcc_jit_location@w{ }*loc, gcc_jit_rvalue@w{ }*rvalue) Add evaluation of an rvalue, discarding the result (e.g. a function call that "returns" void). This is equivalent to this C code: @example (void)expression; @end example @noindent @end deffn @geindex gcc_jit_block_add_assignment (C function) @anchor{topics/functions gcc_jit_block_add_assignment}@anchor{2a} @deffn {C Function} void gcc_jit_block_add_assignment (gcc_jit_block@w{ }*block, gcc_jit_location@w{ }*loc, gcc_jit_lvalue@w{ }*lvalue, gcc_jit_rvalue@w{ }*rvalue) Add evaluation of an rvalue, assigning the result to the given lvalue. This is roughly equivalent to this C code: @example lvalue = rvalue; @end example @noindent @end deffn @geindex gcc_jit_block_add_assignment_op (C function) @anchor{topics/functions gcc_jit_block_add_assignment_op}@anchor{2e} @deffn {C Function} void gcc_jit_block_add_assignment_op (gcc_jit_block@w{ }*block, gcc_jit_location@w{ }*loc, gcc_jit_lvalue@w{ }*lvalue, enum gcc_jit_binary_op@w{ }op, gcc_jit_rvalue@w{ }*rvalue) Add evaluation of an rvalue, using the result to modify an lvalue. This is analogous to "+=" and friends: @example lvalue += rvalue; lvalue *= rvalue; lvalue /= rvalue; @end example @noindent etc. For example: @example /* "i++" */ gcc_jit_block_add_assignment_op ( loop_body, NULL, i, GCC_JIT_BINARY_OP_PLUS, gcc_jit_context_one (ctxt, int_type)); @end example @noindent @end deffn @geindex gcc_jit_block_add_comment (C function) @anchor{topics/functions gcc_jit_block_add_comment}@anchor{3d} @deffn {C Function} void gcc_jit_block_add_comment (gcc_jit_block@w{ }*block, gcc_jit_location@w{ }*loc, const char@w{ }*text) Add a no-op textual comment to the internal representation of the code. It will be optimized away, but will be visible in the dumps seen via @pxref{66,,GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE} and @pxref{1c,,GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE}, and thus may be of use when debugging how your project's internal representation gets converted to the libgccjit IR. @end deffn @geindex gcc_jit_block_end_with_conditional (C function) @anchor{topics/functions gcc_jit_block_end_with_conditional}@anchor{2d} @deffn {C Function} void gcc_jit_block_end_with_conditional (gcc_jit_block@w{ }*block, gcc_jit_location@w{ }*loc, gcc_jit_rvalue@w{ }*boolval, gcc_jit_block@w{ }*on_true, gcc_jit_block@w{ }*on_false) Terminate a block by adding evaluation of an rvalue, branching on the result to the appropriate successor block. This is roughly equivalent to this C code: @example if (boolval) goto on_true; else goto on_false; @end example @noindent block, boolval, on_true, and on_false must be non-NULL. @end deffn @geindex gcc_jit_block_end_with_jump (C function) @anchor{topics/functions gcc_jit_block_end_with_jump}@anchor{cc} @deffn {C Function} void gcc_jit_block_end_with_jump (gcc_jit_block@w{ }*block, gcc_jit_location@w{ }*loc, gcc_jit_block@w{ }*target) Terminate a block by adding a jump to the given target block. This is roughly equivalent to this C code: @example goto target; @end example @noindent @end deffn @geindex gcc_jit_block_end_with_return (C function) @anchor{topics/functions gcc_jit_block_end_with_return}@anchor{cd} @deffn {C Function} void gcc_jit_block_end_with_return (gcc_jit_block@w{ }*block, gcc_jit_location@w{ }*loc, gcc_jit_rvalue@w{ }*rvalue) Terminate a block by adding evaluation of an rvalue, returning the value. This is roughly equivalent to this C code: @example return expression; @end example @noindent @end deffn @geindex gcc_jit_block_end_with_void_return (C function) @anchor{topics/functions gcc_jit_block_end_with_void_return}@anchor{ce} @deffn {C Function} void gcc_jit_block_end_with_void_return (gcc_jit_block@w{ }*block, gcc_jit_location@w{ }*loc) Terminate a block by adding a valueless return, for use within a function with "void" return type. This is equivalent to this C code: @example return; @end example @noindent @end deffn @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Source Locations,Compiling a context,Creating and using functions,Topic Reference @anchor{topics/locations source-locations}@anchor{cf}@anchor{topics/locations doc}@anchor{d0} @section Source Locations @geindex gcc_jit_location (C type) @anchor{topics/locations gcc_jit_location}@anchor{3b} @deffn {C Type} gcc_jit_location A @cite{gcc_jit_location} encapsulates a source code location, so that you can (optionally) associate locations in your language with statements in the JIT-compiled code, allowing the debugger to single-step through your language. @cite{gcc_jit_location} instances are optional: you can always pass NULL to any API entrypoint accepting one. You can construct them using @pxref{41,,gcc_jit_context_new_location()}. You need to enable @pxref{42,,GCC_JIT_BOOL_OPTION_DEBUGINFO} on the @pxref{8,,gcc_jit_context} for these locations to actually be usable by the debugger: @example gcc_jit_context_set_bool_option ( ctxt, GCC_JIT_BOOL_OPTION_DEBUGINFO, 1); @end example @noindent @end deffn @geindex gcc_jit_context_new_location (C function) @anchor{topics/locations gcc_jit_context_new_location}@anchor{41} @deffn {C Function} gcc_jit_location * gcc_jit_context_new_location (gcc_jit_context@w{ }*ctxt, const char@w{ }*filename, int@w{ }line, int@w{ }column) Create a @cite{gcc_jit_location} instance representing the given source location. @end deffn @menu * Faking it:: @end menu @node Faking it,,,Source Locations @anchor{topics/locations faking-it}@anchor{d1} @subsection Faking it If you don't have source code for your internal representation, but need to debug, you can generate a C-like representation of the functions in your context using @pxref{5a,,gcc_jit_context_dump_to_file()}: @example gcc_jit_context_dump_to_file (ctxt, "/tmp/something.c", 1 /* update_locations */); @end example @noindent This will dump C-like code to the given path. If the @cite{update_locations} argument is true, this will also set up @cite{gcc_jit_location} information throughout the context, pointing at the dump file as if it were a source file, giving you @emph{something} you can step through in the debugger. @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Compiling a context,,Source Locations,Topic Reference @anchor{topics/compilation compiling-a-context}@anchor{d2}@anchor{topics/compilation doc}@anchor{d3} @section Compiling a context Once populated, a @pxref{8,,gcc_jit_context *} can be compiled to machine code, either in-memory via @pxref{15,,gcc_jit_context_compile()} or to disk via @pxref{4a,,gcc_jit_context_compile_to_file()}. You can compile a context multiple times (using either form of compilation), although any errors that occur on the context will prevent any future compilation of that context. @menu * In-memory compilation:: * Ahead-of-time compilation:: @end menu @node In-memory compilation,Ahead-of-time compilation,,Compiling a context @anchor{topics/compilation in-memory-compilation}@anchor{d4} @subsection In-memory compilation @geindex gcc_jit_context_compile (C function) @anchor{topics/compilation gcc_jit_context_compile}@anchor{15} @deffn {C Function} gcc_jit_result * gcc_jit_context_compile (gcc_jit_context@w{ }*ctxt) This calls into GCC and builds the code, returning a @cite{gcc_jit_result *}. If the result is non-NULL, the caller becomes responsible for calling @pxref{39,,gcc_jit_result_release()} on it once they're done with it. @end deffn @geindex gcc_jit_result (C type) @anchor{topics/compilation gcc_jit_result}@anchor{16} @deffn {C Type} gcc_jit_result A @cite{gcc_jit_result} encapsulates the result of compiling a context in-memory, and the lifetimes of any machine code functions or globals that are within the result. @end deffn @geindex gcc_jit_result_get_code (C function) @anchor{topics/compilation gcc_jit_result_get_code}@anchor{17} @deffn {C Function} void * gcc_jit_result_get_code (gcc_jit_result@w{ }*result, const char@w{ }*funcname) Locate a given function within the built machine code. Functions are looked up by name. For this to succeed, a function with a name matching @cite{funcname} must have been created on @cite{result}'s context (or a parent context) via a call to @pxref{11,,gcc_jit_context_new_function()} with @cite{kind} @pxref{c0,,GCC_JIT_FUNCTION_EXPORTED}: @example gcc_jit_context_new_function (ctxt, any_location, /* or NULL */ /* Required for func to be visible to gcc_jit_result_get_code: */ GCC_JIT_FUNCTION_EXPORTED, any_return_type, /* Must string-compare equal: */ funcname, /* etc */); @end example @noindent If such a function is not found (or @cite{result} or @cite{funcname} are @code{NULL}), an error message will be emitted on stderr and @code{NULL} will be returned. If the function is found, the result will need to be cast to a function pointer of the correct type before it can be called. Note that the resulting machine code becomes invalid after @pxref{39,,gcc_jit_result_release()} is called on the @pxref{16,,gcc_jit_result *}; attempting to call it after that may lead to a segmentation fault. @end deffn @geindex gcc_jit_result_get_global (C function) @anchor{topics/compilation gcc_jit_result_get_global}@anchor{b0} @deffn {C Function} void * gcc_jit_result_get_global (gcc_jit_result@w{ }*result, const char@w{ }*name) Locate a given global within the built machine code. Globals are looked up by name. For this to succeed, a global with a name matching @cite{name} must have been created on @cite{result}'s context (or a parent context) via a call to @pxref{ad,,gcc_jit_context_new_global()} with @cite{kind} @pxref{af,,GCC_JIT_GLOBAL_EXPORTED}. If the global is found, the result will need to be cast to a pointer of the correct type before it can be called. This is a @emph{pointer} to the global, so e.g. for an @code{int} this is an @code{int *}. For example, given an @code{int foo;} created this way: @example gcc_jit_lvalue *exported_global = gcc_jit_context_new_global (ctxt, any_location, /* or NULL */ GCC_JIT_GLOBAL_EXPORTED, int_type, "foo"); @end example @noindent we can access it like this: @example int *ptr_to_foo = (int *)gcc_jit_result_get_global (result, "foo"); @end example @noindent If such a global is not found (or @cite{result} or @cite{name} are @code{NULL}), an error message will be emitted on stderr and @code{NULL} will be returned. Note that the resulting address becomes invalid after @pxref{39,,gcc_jit_result_release()} is called on the @pxref{16,,gcc_jit_result *}; attempting to use it after that may lead to a segmentation fault. @end deffn @geindex gcc_jit_result_release (C function) @anchor{topics/compilation gcc_jit_result_release}@anchor{39} @deffn {C Function} void gcc_jit_result_release (gcc_jit_result@w{ }*result) Once we're done with the code, this unloads the built .so file. This cleans up the result; after calling this, it's no longer valid to use the result, or any code or globals that were obtained by calling @pxref{17,,gcc_jit_result_get_code()} or @pxref{b0,,gcc_jit_result_get_global()} on it. @end deffn @node Ahead-of-time compilation,,In-memory compilation,Compiling a context @anchor{topics/compilation ahead-of-time-compilation}@anchor{d5} @subsection Ahead-of-time compilation Although libgccjit is primarily aimed at just-in-time compilation, it can also be used for implementing more traditional ahead-of-time compilers, via the @pxref{4a,,gcc_jit_context_compile_to_file()} API entrypoint. @geindex gcc_jit_context_compile_to_file (C function) @anchor{topics/compilation gcc_jit_context_compile_to_file}@anchor{4a} @deffn {C Function} void gcc_jit_context_compile_to_file (gcc_jit_context@w{ }*ctxt, enum gcc_jit_output_kind@w{ }output_kind, const char@w{ }*output_path) Compile the @pxref{8,,gcc_jit_context *} to a file of the given kind. @end deffn @pxref{4a,,gcc_jit_context_compile_to_file()} ignores the suffix of @code{output_path}, and insteads uses the given @code{enum gcc_jit_output_kind} to decide what to do. @cartouche @quotation Note This is different from the @code{gcc} program, which does make use of the suffix of the output file when determining what to do. @end quotation @end cartouche @geindex gcc_jit_output_kind (C type) @anchor{topics/compilation gcc_jit_output_kind}@anchor{d6} @deffn {C Type} enum gcc_jit_output_kind @end deffn The available kinds of output are: @multitable {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} {xxxxxxxxxxxxxxxx} @headitem Output kind @tab Typical suffix @item @pxref{d7,,GCC_JIT_OUTPUT_KIND_ASSEMBLER} @tab .s @item @pxref{d8,,GCC_JIT_OUTPUT_KIND_OBJECT_FILE} @tab .o @item @pxref{d9,,GCC_JIT_OUTPUT_KIND_DYNAMIC_LIBRARY} @tab .so or .dll @item @pxref{da,,GCC_JIT_OUTPUT_KIND_EXECUTABLE} @tab None, or .exe @end multitable @geindex GCC_JIT_OUTPUT_KIND_ASSEMBLER (C macro) @anchor{topics/compilation GCC_JIT_OUTPUT_KIND_ASSEMBLER}@anchor{d7} @deffn {C Macro} GCC_JIT_OUTPUT_KIND_ASSEMBLER Compile the context to an assembler file. @end deffn @geindex GCC_JIT_OUTPUT_KIND_OBJECT_FILE (C macro) @anchor{topics/compilation GCC_JIT_OUTPUT_KIND_OBJECT_FILE}@anchor{d8} @deffn {C Macro} GCC_JIT_OUTPUT_KIND_OBJECT_FILE Compile the context to an object file. @end deffn @geindex GCC_JIT_OUTPUT_KIND_DYNAMIC_LIBRARY (C macro) @anchor{topics/compilation GCC_JIT_OUTPUT_KIND_DYNAMIC_LIBRARY}@anchor{d9} @deffn {C Macro} GCC_JIT_OUTPUT_KIND_DYNAMIC_LIBRARY Compile the context to a dynamic library. There is currently no support for specifying other libraries to link against. @end deffn @geindex GCC_JIT_OUTPUT_KIND_EXECUTABLE (C macro) @anchor{topics/compilation GCC_JIT_OUTPUT_KIND_EXECUTABLE}@anchor{da} @deffn {C Macro} GCC_JIT_OUTPUT_KIND_EXECUTABLE Compile the context to an executable. There is currently no support for specifying libraries to link against. @end deffn @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node C++ bindings for libgccjit,Internals,Topic Reference,Top @anchor{cp/index c-bindings-for-libgccjit}@anchor{db}@anchor{cp/index doc}@anchor{dc} @chapter C++ bindings for libgccjit This document describes the C++ bindings to libgccjit@footnote{http://gcc.gnu.org/wiki/JIT}, an API for embedding GCC inside programs and libraries. The C++ bindings consist of a single header file @code{libgccjit++.h}. This is a collection of "thin" wrapper classes around the C API. Everything is an inline function, implemented in terms of the C API, so there is nothing extra to link against. Note that libgccjit is currently of "Alpha" quality; the APIs are not yet set in stone, and they shouldn't be used in production yet. Contents: @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @menu * Tutorial: Tutorial<2>. * Topic Reference: Topic Reference<2>. Tutorial * Tutorial part 1; "Hello world": Tutorial part 1 "Hello world"<2>. * Tutorial part 2; Creating a trivial machine code function: Tutorial part 2 Creating a trivial machine code function<2>. * Tutorial part 3; Loops and variables: Tutorial part 3 Loops and variables<2>. * Tutorial part 4; Adding JIT-compilation to a toy interpreter: Tutorial part 4 Adding JIT-compilation to a toy interpreter<2>. Tutorial part 2: Creating a trivial machine code function * Options: Options<3>. * Full example: Full example<3>. Tutorial part 3: Loops and variables * Expressions; lvalues and rvalues: Expressions lvalues and rvalues<2>. * Control flow: Control flow<2>. * Visualizing the control flow graph: Visualizing the control flow graph<2>. * Full example: Full example<4>. Tutorial part 4: Adding JIT-compilation to a toy interpreter * Our toy interpreter: Our toy interpreter<2>. * Compiling to machine code: Compiling to machine code<2>. * Setting things up: Setting things up<2>. * Populating the function: Populating the function<2>. * Verifying the control flow graph: Verifying the control flow graph<2>. * Compiling the context: Compiling the context<2>. * Single-stepping through the generated code: Single-stepping through the generated code<2>. * Examining the generated code: Examining the generated code<2>. * Putting it all together: Putting it all together<2>. * Behind the curtain; How does our code get optimized?: Behind the curtain How does our code get optimized?<2>. Behind the curtain: How does our code get optimized? * Optimizing away stack manipulation: Optimizing away stack manipulation<2>. * Elimination of tail recursion: Elimination of tail recursion<2>. Topic Reference * Compilation contexts: Compilation contexts<2>. * Objects: Objects<2>. * Types: Types<2>. * Expressions: Expressions<2>. * Creating and using functions: Creating and using functions<2>. * Source Locations: Source Locations<2>. * Compiling a context: Compiling a context<2>. Compilation contexts * Lifetime-management: Lifetime-management<2>. * Thread-safety: Thread-safety<2>. * Error-handling: Error-handling<3>. * Debugging: Debugging<2>. * Options: Options<4>. Options * String Options: String Options<2>. * Boolean options: Boolean options<2>. * Integer options: Integer options<2>. Types * Standard types: Standard types<2>. * Pointers@comma{} const@comma{} and volatile: Pointers const and volatile<2>. * Structures and unions: Structures and unions<2>. Expressions * Rvalues: Rvalues<2>. * Lvalues: Lvalues<2>. * Working with pointers@comma{} structs and unions: Working with pointers structs and unions<2>. Rvalues * Simple expressions: Simple expressions<2>. * Unary Operations: Unary Operations<2>. * Binary Operations: Binary Operations<2>. * Comparisons: Comparisons<2>. * Function calls: Function calls<2>. * Type-coercion: Type-coercion<2>. Lvalues * Global variables: Global variables<2>. Creating and using functions * Params: Params<2>. * Functions: Functions<2>. * Blocks: Blocks<2>. * Statements: Statements<2>. Source Locations * Faking it: Faking it<2>. Compiling a context * In-memory compilation: In-memory compilation<2>. * Ahead-of-time compilation: Ahead-of-time compilation<2>. @end menu @node Tutorial<2>,Topic Reference<2>,,C++ bindings for libgccjit @anchor{cp/intro/index doc}@anchor{dd}@anchor{cp/intro/index tutorial}@anchor{de} @section Tutorial @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @menu * Tutorial part 1; "Hello world": Tutorial part 1 "Hello world"<2>. * Tutorial part 2; Creating a trivial machine code function: Tutorial part 2 Creating a trivial machine code function<2>. * Tutorial part 3; Loops and variables: Tutorial part 3 Loops and variables<2>. * Tutorial part 4; Adding JIT-compilation to a toy interpreter: Tutorial part 4 Adding JIT-compilation to a toy interpreter<2>. @end menu @node Tutorial part 1 "Hello world"<2>,Tutorial part 2 Creating a trivial machine code function<2>,,Tutorial<2> @anchor{cp/intro/tutorial01 doc}@anchor{df}@anchor{cp/intro/tutorial01 tutorial-part-1-hello-world}@anchor{e0} @subsection Tutorial part 1: "Hello world" Before we look at the details of the API, let's look at building and running programs that use the library. Here's a toy "hello world" program that uses the library's C++ API to synthesize a call to @cite{printf} and uses it to write a message to stdout. Don't worry about the content of the program for now; we'll cover the details in later parts of this tutorial. @quotation @example /* Smoketest example for libgccjit.so C++ API Copyright (C) 2014-2015 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include #include #include static void create_code (gccjit::context ctxt) @{ /* Let's try to inject the equivalent of this C code: void greet (const char *name) @{ printf ("hello %s\n", name); @} */ gccjit::type void_type = ctxt.get_type (GCC_JIT_TYPE_VOID); gccjit::type const_char_ptr_type = ctxt.get_type (GCC_JIT_TYPE_CONST_CHAR_PTR); gccjit::param param_name = ctxt.new_param (const_char_ptr_type, "name"); std::vector func_params; func_params.push_back (param_name); gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED, void_type, "greet", func_params, 0); gccjit::param param_format = ctxt.new_param (const_char_ptr_type, "format"); std::vector printf_params; printf_params.push_back (param_format); gccjit::function printf_func = ctxt.new_function (GCC_JIT_FUNCTION_IMPORTED, ctxt.get_type (GCC_JIT_TYPE_INT), "printf", printf_params, 1); gccjit::block block = func.new_block (); block.add_eval (ctxt.new_call (printf_func, ctxt.new_rvalue ("hello %s\n"), param_name)); block.end_with_return (); @} int main (int argc, char **argv) @{ gccjit::context ctxt; gcc_jit_result *result; /* Get a "context" object for working with the library. */ ctxt = gccjit::context::acquire (); /* Set some options on the context. Turn this on to see the code being generated, in assembler form. */ ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, 0); /* Populate the context. */ create_code (ctxt); /* Compile the code. */ result = ctxt.compile (); if (!result) @{ fprintf (stderr, "NULL result"); exit (1); @} ctxt.release (); /* Extract the generated code from "result". */ typedef void (*fn_type) (const char *); fn_type greet = (fn_type)gcc_jit_result_get_code (result, "greet"); if (!greet) @{ fprintf (stderr, "NULL greet"); exit (1); @} /* Now call the generated function: */ greet ("world"); fflush (stdout); gcc_jit_result_release (result); return 0; @} @end example @noindent @end quotation Copy the above to @cite{tut01-hello-world.cc}. Assuming you have the jit library installed, build the test program using: @example $ gcc \ tut01-hello-world.cc \ -o tut01-hello-world \ -lgccjit @end example @noindent You should then be able to run the built program: @example $ ./tut01-hello-world hello world @end example @noindent @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Tutorial part 2 Creating a trivial machine code function<2>,Tutorial part 3 Loops and variables<2>,Tutorial part 1 "Hello world"<2>,Tutorial<2> @anchor{cp/intro/tutorial02 doc}@anchor{e1}@anchor{cp/intro/tutorial02 tutorial-part-2-creating-a-trivial-machine-code-function}@anchor{e2} @subsection Tutorial part 2: Creating a trivial machine code function Consider this C function: @example int square (int i) @{ return i * i; @} @end example @noindent How can we construct this at run-time using libgccjit's C++ API? First we need to include the relevant header: @example #include @end example @noindent All state associated with compilation is associated with a @code{gccjit::context}, which is a thin C++ wrapper around the C API's @pxref{8,,gcc_jit_context *}. Create one using @pxref{e3,,gccjit;;context;;acquire()}: @example gccjit::context ctxt; ctxt = gccjit::context::acquire (); @end example @noindent The JIT library has a system of types. It is statically-typed: every expression is of a specific type, fixed at compile-time. In our example, all of the expressions are of the C @cite{int} type, so let's obtain this from the context, as a @code{gccjit::type}, using @pxref{e4,,gccjit;;context;;get_type()}: @example gccjit::type int_type = ctxt.get_type (GCC_JIT_TYPE_INT); @end example @noindent @code{gccjit::type} is an example of a "contextual" object: every entity in the API is associated with a @code{gccjit::context}. Memory management is easy: all such "contextual" objects are automatically cleaned up for you when the context is released, using @pxref{e5,,gccjit;;context;;release()}: @example ctxt.release (); @end example @noindent so you don't need to manually track and cleanup all objects, just the contexts. All of the C++ classes in the API are thin wrappers around pointers to types in the C API. The C++ class hierarchy within the @code{gccjit} namespace looks like this: @example +- object +- location +- type +- struct +- field +- function +- block +- rvalue +- lvalue +- param @end example @noindent One thing you can do with a @code{gccjit::object} is to ask it for a human-readable description as a @code{std::string}, using @pxref{e6,,gccjit;;object;;get_debug_string()}: @example printf ("obj: %s\n", obj.get_debug_string ().c_str ()); @end example @noindent giving this text on stdout: @example obj: int @end example @noindent This is invaluable when debugging. Let's create the function. To do so, we first need to construct its single parameter, specifying its type and giving it a name, using @pxref{e7,,gccjit;;context;;new_param()}: @example gccjit::param param_i = ctxt.new_param (int_type, "i"); @end example @noindent and we can then make a vector of all of the params of the function, in this case just one: @example std::vector params; params.push_back (param_i); @end example @noindent Now we can create the function, using @code{gccjit::context::new_function()}: @example gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED, int_type, "square", params, 0); @end example @noindent To define the code within the function, we must create basic blocks containing statements. Every basic block contains a list of statements, eventually terminated by a statement that either returns, or jumps to another basic block. Our function has no control-flow, so we just need one basic block: @example gccjit::block block = func.new_block (); @end example @noindent Our basic block is relatively simple: it immediately terminates by returning the value of an expression. We can build the expression using @pxref{e8,,gccjit;;context;;new_binary_op()}: @example gccjit::rvalue expr = ctxt.new_binary_op ( GCC_JIT_BINARY_OP_MULT, int_type, param_i, param_i); @end example @noindent A @code{gccjit::rvalue} is another example of a @code{gccjit::object} subclass. As before, we can print it with @pxref{e6,,gccjit;;object;;get_debug_string()}. @example printf ("expr: %s\n", expr.get_debug_string ().c_str ()); @end example @noindent giving this output: @example expr: i * i @end example @noindent Note that @code{gccjit::rvalue} provides numerous overloaded operators which can be used to dramatically reduce the amount of typing needed. We can build the above binary operation more directly with this one-liner: @example gccjit::rvalue expr = param_i * param_i; @end example @noindent Creating the expression in itself doesn't do anything; we have to add this expression to a statement within the block. In this case, we use it to build a return statement, which terminates the basic block: @example block.end_with_return (expr); @end example @noindent OK, we've populated the context. We can now compile it using @pxref{e9,,gccjit;;context;;compile()}: @example gcc_jit_result *result; result = ctxt.compile (); @end example @noindent and get a @pxref{16,,gcc_jit_result *}. We can now use @pxref{17,,gcc_jit_result_get_code()} to look up a specific machine code routine within the result, in this case, the function we created above. @example void *fn_ptr = gcc_jit_result_get_code (result, "square"); if (!fn_ptr) @{ fprintf (stderr, "NULL fn_ptr"); goto error; @} @end example @noindent We can now cast the pointer to an appropriate function pointer type, and then call it: @example typedef int (*fn_type) (int); fn_type square = (fn_type)fn_ptr; printf ("result: %d", square (5)); @end example @noindent @example result: 25 @end example @noindent @menu * Options: Options<3>. * Full example: Full example<3>. @end menu @node Options<3>,Full example<3>,,Tutorial part 2 Creating a trivial machine code function<2> @anchor{cp/intro/tutorial02 options}@anchor{ea} @subsubsection Options To get more information on what's going on, you can set debugging flags on the context using @pxref{eb,,gccjit;;context;;set_bool_option()}. @c (I'm deliberately not mentioning @c :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE` here since I think @c it's probably more of use to implementors than to users) Setting @pxref{1c,,GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE} will dump a C-like representation to stderr when you compile (GCC's "GIMPLE" representation): @example ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, 1); result = ctxt.compile (); @end example @noindent @example square (signed int i) @{ signed int D.260; entry: D.260 = i * i; return D.260; @} @end example @noindent We can see the generated machine code in assembler form (on stderr) by setting @pxref{1d,,GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE} on the context before compiling: @example ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, 1); result = ctxt.compile (); @end example @noindent @example .file "fake.c" .text .globl square .type square, @@function square: .LFB6: .cfi_startproc pushq %rbp .cfi_def_cfa_offset 16 .cfi_offset 6, -16 movq %rsp, %rbp .cfi_def_cfa_register 6 movl %edi, -4(%rbp) .L14: movl -4(%rbp), %eax imull -4(%rbp), %eax popq %rbp .cfi_def_cfa 7, 8 ret .cfi_endproc .LFE6: .size square, .-square .ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2-0.5.1920c315ff984892399893b380305ab36e07b455.fc20)" .section .note.GNU-stack,"",@@progbits @end example @noindent By default, no optimizations are performed, the equivalent of GCC's @cite{-O0} option. We can turn things up to e.g. @cite{-O3} by calling @pxref{ec,,gccjit;;context;;set_int_option()} with @pxref{1f,,GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL}: @example ctxt.set_int_option (GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3); @end example @noindent @example .file "fake.c" .text .p2align 4,,15 .globl square .type square, @@function square: .LFB7: .cfi_startproc .L16: movl %edi, %eax imull %edi, %eax ret .cfi_endproc .LFE7: .size square, .-square .ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2-0.5.1920c315ff984892399893b380305ab36e07b455.fc20)" .section .note.GNU-stack,"",@@progbits @end example @noindent Naturally this has only a small effect on such a trivial function. @node Full example<3>,,Options<3>,Tutorial part 2 Creating a trivial machine code function<2> @anchor{cp/intro/tutorial02 full-example}@anchor{ed} @subsubsection Full example Here's what the above looks like as a complete program: @quotation @example /* Usage example for libgccjit.so's C++ API Copyright (C) 2014-2015 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include #include #include void create_code (gccjit::context ctxt) @{ /* Let's try to inject the equivalent of this C code: int square (int i) @{ return i * i; @} */ gccjit::type int_type = ctxt.get_type (GCC_JIT_TYPE_INT); gccjit::param param_i = ctxt.new_param (int_type, "i"); std::vector params; params.push_back (param_i); gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED, int_type, "square", params, 0); gccjit::block block = func.new_block (); gccjit::rvalue expr = ctxt.new_binary_op (GCC_JIT_BINARY_OP_MULT, int_type, param_i, param_i); block.end_with_return (expr); @} int main (int argc, char **argv) @{ /* Get a "context" object for working with the library. */ gccjit::context ctxt = gccjit::context::acquire (); /* Set some options on the context. Turn this on to see the code being generated, in assembler form. */ ctxt.set_bool_option ( GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, 0); /* Populate the context. */ create_code (ctxt); /* Compile the code. */ gcc_jit_result *result = ctxt.compile (); /* We're done with the context; we can release it: */ ctxt.release (); if (!result) @{ fprintf (stderr, "NULL result"); return 1; @} /* Extract the generated code from "result". */ void *fn_ptr = gcc_jit_result_get_code (result, "square"); if (!fn_ptr) @{ fprintf (stderr, "NULL fn_ptr"); gcc_jit_result_release (result); return 1; @} typedef int (*fn_type) (int); fn_type square = (fn_type)fn_ptr; printf ("result: %d\n", square (5)); gcc_jit_result_release (result); return 0; @} @end example @noindent @end quotation Building and running it: @example $ gcc \ tut02-square.cc \ -o tut02-square \ -lgccjit # Run the built program: $ ./tut02-square result: 25 @end example @noindent @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Tutorial part 3 Loops and variables<2>,Tutorial part 4 Adding JIT-compilation to a toy interpreter<2>,Tutorial part 2 Creating a trivial machine code function<2>,Tutorial<2> @anchor{cp/intro/tutorial03 tutorial-part-3-loops-and-variables}@anchor{ee}@anchor{cp/intro/tutorial03 doc}@anchor{ef} @subsection Tutorial part 3: Loops and variables Consider this C function: @quotation @example int loop_test (int n) @{ int sum = 0; for (int i = 0; i < n; i++) sum += i * i; return sum; @} @end example @noindent @end quotation This example demonstrates some more features of libgccjit, with local variables and a loop. To break this down into libgccjit terms, it's usually easier to reword the @cite{for} loop as a @cite{while} loop, giving: @quotation @example int loop_test (int n) @{ int sum = 0; int i = 0; while (i < n) @{ sum += i * i; i++; @} return sum; @} @end example @noindent @end quotation Here's what the final control flow graph will look like: @quotation @float Figure @image{sum-of-squares,,,image of a control flow graph,png} @end float @end quotation As before, we include the libgccjit++ header and make a @code{gccjit::context}. @example #include void test (void) @{ gccjit::context ctxt; ctxt = gccjit::context::acquire (); @end example @noindent The function works with the C @cite{int} type. In the previous tutorial we acquired this via @example gccjit::type the_type = ctxt.get_type (ctxt, GCC_JIT_TYPE_INT); @end example @noindent though we could equally well make it work on, say, @cite{double}: @example gccjit::type the_type = ctxt.get_type (ctxt, GCC_JIT_TYPE_DOUBLE); @end example @noindent For integer types we can use @code{gccjit::context::get_int_type} to directly bind a specific type: @example gccjit::type the_type = ctxt.get_int_type (); @end example @noindent Let's build the function: @example gcc_jit_param n = ctxt.new_param (the_type, "n"); std::vector params; params.push_back (n); gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED, return_type, "loop_test", params, 0); @end example @noindent @menu * Expressions; lvalues and rvalues: Expressions lvalues and rvalues<2>. * Control flow: Control flow<2>. * Visualizing the control flow graph: Visualizing the control flow graph<2>. * Full example: Full example<4>. @end menu @node Expressions lvalues and rvalues<2>,Control flow<2>,,Tutorial part 3 Loops and variables<2> @anchor{cp/intro/tutorial03 expressions-lvalues-and-rvalues}@anchor{f0} @subsubsection Expressions: lvalues and rvalues The base class of expression is the @code{gccjit::rvalue}, representing an expression that can be on the @emph{right}-hand side of an assignment: a value that can be computed somehow, and assigned @emph{to} a storage area (such as a variable). It has a specific @code{gccjit::type}. Anothe important class is @code{gccjit::lvalue}. A @code{gccjit::lvalue}. is something that can of the @emph{left}-hand side of an assignment: a storage area (such as a variable). In other words, every assignment can be thought of as: @example LVALUE = RVALUE; @end example @noindent Note that @code{gccjit::lvalue} is a subclass of @code{gccjit::rvalue}, where in an assignment of the form: @example LVALUE_A = LVALUE_B; @end example @noindent the @cite{LVALUE_B} implies reading the current value of that storage area, assigning it into the @cite{LVALUE_A}. So far the only expressions we've seen are from the previous tutorial: @enumerate @item the multiplication @cite{i * i}: @end enumerate @quotation @example gccjit::rvalue expr = ctxt.new_binary_op ( GCC_JIT_BINARY_OP_MULT, int_type, param_i, param_i); /* Alternatively, using operator-overloading: */ gccjit::rvalue expr = param_i * param_i; @end example @noindent which is a @code{gccjit::rvalue}, and @end quotation @enumerate 2 @item the various function parameters: @cite{param_i} and @cite{param_n}, instances of @code{gccjit::param}, which is a subclass of @code{gccjit::lvalue} (and, in turn, of @code{gccjit::rvalue}): we can both read from and write to function parameters within the body of a function. @end enumerate Our new example has a new kind of expression: we have two local variables. We create them by calling @pxref{f1,,gccjit;;function;;new_local()}, supplying a type and a name: @example /* Build locals: */ gccjit::lvalue i = func.new_local (the_type, "i"); gccjit::lvalue sum = func.new_local (the_type, "sum"); @end example @noindent These are instances of @code{gccjit::lvalue} - they can be read from and written to. Note that there is no precanned way to create @emph{and} initialize a variable like in C: @example int i = 0; @end example @noindent Instead, having added the local to the function, we have to separately add an assignment of @cite{0} to @cite{local_i} at the beginning of the function. @node Control flow<2>,Visualizing the control flow graph<2>,Expressions lvalues and rvalues<2>,Tutorial part 3 Loops and variables<2> @anchor{cp/intro/tutorial03 control-flow}@anchor{f2} @subsubsection Control flow This function has a loop, so we need to build some basic blocks to handle the control flow. In this case, we need 4 blocks: @enumerate @item before the loop (initializing the locals) @item the conditional at the top of the loop (comparing @cite{i < n}) @item the body of the loop @item after the loop terminates (@cite{return sum}) @end enumerate so we create these as @code{gccjit::block} instances within the @code{gccjit::function}: @example gccjit::block b_initial = func.new_block ("initial"); gccjit::block b_loop_cond = func.new_block ("loop_cond"); gccjit::block b_loop_body = func.new_block ("loop_body"); gccjit::block b_after_loop = func.new_block ("after_loop"); @end example @noindent We now populate each block with statements. The entry block @cite{b_initial} consists of initializations followed by a jump to the conditional. We assign @cite{0} to @cite{i} and to @cite{sum}, using @pxref{f3,,gccjit;;block;;add_assignment()} to add an assignment statement, and using @pxref{f4,,gccjit;;context;;zero()} to get the constant value @cite{0} for the relevant type for the right-hand side of the assignment: @example /* sum = 0; */ b_initial.add_assignment (sum, ctxt.zero (the_type)); /* i = 0; */ b_initial.add_assignment (i, ctxt.zero (the_type)); @end example @noindent We can then terminate the entry block by jumping to the conditional: @example b_initial.end_with_jump (b_loop_cond); @end example @noindent The conditional block is equivalent to the line @cite{while (i < n)} from our C example. It contains a single statement: a conditional, which jumps to one of two destination blocks depending on a boolean @code{gccjit::rvalue}, in this case the comparison of @cite{i} and @cite{n}. We could build the comparison using @pxref{f5,,gccjit;;context;;new_comparison()}: @example gccjit::rvalue guard = ctxt.new_comparison (GCC_JIT_COMPARISON_GE, i, n); @end example @noindent and can then use this to add @cite{b_loop_cond}'s sole statement, via @pxref{f6,,gccjit;;block;;end_with_conditional()}: @example b_loop_cond.end_with_conditional (guard, b_after_loop, // on_true b_loop_body); // on_false @end example @noindent However @code{gccjit::rvalue} has overloaded operators for this, so we express the conditional as @example gccjit::rvalue guard = (i >= n); @end example @noindent and hence we can write the block more concisely as: @example b_loop_cond.end_with_conditional ( i >= n, b_after_loop, // on_true b_loop_body); // on_false @end example @noindent Next, we populate the body of the loop. The C statement @cite{sum += i * i;} is an assignment operation, where an lvalue is modified "in-place". We use @pxref{f7,,gccjit;;block;;add_assignment_op()} to handle these operations: @example /* sum += i * i */ b_loop_body.add_assignment_op (sum, GCC_JIT_BINARY_OP_PLUS, i * i); @end example @noindent The @cite{i++} can be thought of as @cite{i += 1}, and can thus be handled in a similar way. We use @pxref{2f,,gcc_jit_context_one()} to get the constant value @cite{1} (for the relevant type) for the right-hand side of the assignment. @example /* i++ */ b_loop_body.add_assignment_op (i, GCC_JIT_BINARY_OP_PLUS, ctxt.one (the_type)); @end example @noindent @cartouche @quotation Note For numeric constants other than 0 or 1, we could use @pxref{f8,,gccjit;;context;;new_rvalue()}, which has overloads for both @code{int} and @code{double}. @end quotation @end cartouche The loop body completes by jumping back to the conditional: @example b_loop_body.end_with_jump (b_loop_cond); @end example @noindent Finally, we populate the @cite{b_after_loop} block, reached when the loop conditional is false. We want to generate the equivalent of: @example return sum; @end example @noindent so the block is just one statement: @example /* return sum */ b_after_loop.end_with_return (sum); @end example @noindent @cartouche @quotation Note You can intermingle block creation with statement creation, but given that the terminator statements generally include references to other blocks, I find it's clearer to create all the blocks, @emph{then} all the statements. @end quotation @end cartouche We've finished populating the function. As before, we can now compile it to machine code: @example gcc_jit_result *result; result = ctxt.compile (); ctxt.release (); if (!result) @{ fprintf (stderr, "NULL result"); return 1; @} typedef int (*loop_test_fn_type) (int); loop_test_fn_type loop_test = (loop_test_fn_type)gcc_jit_result_get_code (result, "loop_test"); if (!loop_test) @{ fprintf (stderr, "NULL loop_test"); gcc_jit_result_release (result); return 1; @} printf ("result: %d", loop_test (10)); @end example @noindent @example result: 285 @end example @noindent @node Visualizing the control flow graph<2>,Full example<4>,Control flow<2>,Tutorial part 3 Loops and variables<2> @anchor{cp/intro/tutorial03 visualizing-the-control-flow-graph}@anchor{f9} @subsubsection Visualizing the control flow graph You can see the control flow graph of a function using @pxref{fa,,gccjit;;function;;dump_to_dot()}: @example func.dump_to_dot ("/tmp/sum-of-squares.dot"); @end example @noindent giving a .dot file in GraphViz format. You can convert this to an image using @cite{dot}: @example $ dot -Tpng /tmp/sum-of-squares.dot -o /tmp/sum-of-squares.png @end example @noindent or use a viewer (my preferred one is xdot.py; see @indicateurl{https://github.com/jrfonseca/xdot.py}; on Fedora you can install it with @cite{yum install python-xdot}): @quotation @float Figure @image{sum-of-squares,,,image of a control flow graph,png} @end float @end quotation @node Full example<4>,,Visualizing the control flow graph<2>,Tutorial part 3 Loops and variables<2> @anchor{cp/intro/tutorial03 full-example}@anchor{fb} @subsubsection Full example @quotation @example /* Usage example for libgccjit.so's C++ API Copyright (C) 2014-2015 Free Software Foundation, Inc. This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include #include #include void create_code (gccjit::context ctxt) @{ /* Simple sum-of-squares, to test conditionals and looping int loop_test (int n) @{ int i; int sum = 0; for (i = 0; i < n ; i ++) @{ sum += i * i; @} return sum; */ gccjit::type the_type = ctxt.get_int_type (); gccjit::type return_type = the_type; gccjit::param n = ctxt.new_param (the_type, "n"); std::vector params; params.push_back (n); gccjit::function func = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED, return_type, "loop_test", params, 0); /* Build locals: */ gccjit::lvalue i = func.new_local (the_type, "i"); gccjit::lvalue sum = func.new_local (the_type, "sum"); gccjit::block b_initial = func.new_block ("initial"); gccjit::block b_loop_cond = func.new_block ("loop_cond"); gccjit::block b_loop_body = func.new_block ("loop_body"); gccjit::block b_after_loop = func.new_block ("after_loop"); /* sum = 0; */ b_initial.add_assignment (sum, ctxt.zero (the_type)); /* i = 0; */ b_initial.add_assignment (i, ctxt.zero (the_type)); b_initial.end_with_jump (b_loop_cond); /* if (i >= n) */ b_loop_cond.end_with_conditional ( i >= n, b_after_loop, b_loop_body); /* sum += i * i */ b_loop_body.add_assignment_op (sum, GCC_JIT_BINARY_OP_PLUS, i * i); /* i++ */ b_loop_body.add_assignment_op (i, GCC_JIT_BINARY_OP_PLUS, ctxt.one (the_type)); b_loop_body.end_with_jump (b_loop_cond); /* return sum */ b_after_loop.end_with_return (sum); @} int main (int argc, char **argv) @{ gccjit::context ctxt; gcc_jit_result *result = NULL; /* Get a "context" object for working with the library. */ ctxt = gccjit::context::acquire (); /* Set some options on the context. Turn this on to see the code being generated, in assembler form. */ ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, 0); /* Populate the context. */ create_code (ctxt); /* Compile the code. */ result = ctxt.compile (); ctxt.release (); if (!result) @{ fprintf (stderr, "NULL result"); return 1; @} /* Extract the generated code from "result". */ typedef int (*loop_test_fn_type) (int); loop_test_fn_type loop_test = (loop_test_fn_type)gcc_jit_result_get_code (result, "loop_test"); if (!loop_test) @{ fprintf (stderr, "NULL loop_test"); gcc_jit_result_release (result); return 1; @} /* Run the generated code. */ int val = loop_test (10); printf("loop_test returned: %d\n", val); gcc_jit_result_release (result); return 0; @} @end example @noindent @end quotation Building and running it: @example $ gcc \ tut03-sum-of-squares.cc \ -o tut03-sum-of-squares \ -lgccjit # Run the built program: $ ./tut03-sum-of-squares loop_test returned: 285 @end example @noindent @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Tutorial part 4 Adding JIT-compilation to a toy interpreter<2>,,Tutorial part 3 Loops and variables<2>,Tutorial<2> @anchor{cp/intro/tutorial04 tutorial-part-4-adding-jit-compilation-to-a-toy-interpreter}@anchor{fc}@anchor{cp/intro/tutorial04 doc}@anchor{fd} @subsection Tutorial part 4: Adding JIT-compilation to a toy interpreter In this example we construct a "toy" interpreter, and add JIT-compilation to it. @menu * Our toy interpreter: Our toy interpreter<2>. * Compiling to machine code: Compiling to machine code<2>. * Setting things up: Setting things up<2>. * Populating the function: Populating the function<2>. * Verifying the control flow graph: Verifying the control flow graph<2>. * Compiling the context: Compiling the context<2>. * Single-stepping through the generated code: Single-stepping through the generated code<2>. * Examining the generated code: Examining the generated code<2>. * Putting it all together: Putting it all together<2>. * Behind the curtain; How does our code get optimized?: Behind the curtain How does our code get optimized?<2>. @end menu @node Our toy interpreter<2>,Compiling to machine code<2>,,Tutorial part 4 Adding JIT-compilation to a toy interpreter<2> @anchor{cp/intro/tutorial04 our-toy-interpreter}@anchor{fe} @subsubsection Our toy interpreter It's a stack-based interpreter, and is intended as a (very simple) example of the kind of bytecode interpreter seen in dynamic languages such as Python, Ruby etc. For the sake of simplicity, our toy virtual machine is very limited: @quotation @itemize * @item The only data type is @cite{int} @item It can only work on one function at a time (so that the only function call that can be made is to recurse). @item Functions can only take one parameter. @item Functions have a stack of @cite{int} values. @item We'll implement function call within the interpreter by calling a function in our implementation, rather than implementing our own frame stack. @item The parser is only good enough to get the examples to work. @end itemize @end quotation Naturally, a real interpreter would be much more complicated that this. The following operations are supported: @multitable {xxxxxxxxxxxxxxxxxxxxxxxx} {xxxxxxxxxxxxxxxxxxxxxxxxxx} {xxxxxxxxxxxxxxxxx} {xxxxxxxxxxxxxxxxxx} @headitem Operation @tab Meaning @tab Old Stack @tab New Stack @item DUP @tab Duplicate top of stack. @tab @code{[..., x]} @tab @code{[..., x, x]} @item ROT @tab Swap top two elements of stack. @tab @code{[..., x, y]} @tab @code{[..., y, x]} @item BINARY_ADD @tab Add the top two elements on the stack. @tab @code{[..., x, y]} @tab @code{[..., (x+y)]} @item BINARY_SUBTRACT @tab Likewise, but subtract. @tab @code{[..., x, y]} @tab @code{[..., (x-y)]} @item BINARY_MULT @tab Likewise, but multiply. @tab @code{[..., x, y]} @tab @code{[..., (x*y)]} @item BINARY_COMPARE_LT @tab Compare the top two elements on the stack and push a nonzero/zero if (xop_opcode) @{ /* Ops taking no operand. */ case DUP: x = POP (); PUSH (x); PUSH (x); break; case ROT: y = POP (); x = POP (); PUSH (y); PUSH (x); break; case BINARY_ADD: y = POP (); x = POP (); PUSH (x + y); break; case BINARY_SUBTRACT: y = POP (); x = POP (); PUSH (x - y); break; case BINARY_MULT: y = POP (); x = POP (); PUSH (x * y); break; case BINARY_COMPARE_LT: y = POP (); x = POP (); PUSH (x < y); break; case RECURSE: x = POP (); x = interpret (x, trace); PUSH (x); break; case RETURN: return POP (); /* Ops taking an operand. */ case PUSH_CONST: PUSH (op->op_operand); break; case JUMP_ABS_IF_TRUE: x = POP (); if (x) frame.frm_pc = op->op_operand; break; default: assert (0); /* unknown opcode */ @} /* end of switch on opcode */ @} /* end of while loop */ #undef PUSH #undef POP @} @end example @noindent @end quotation @node Compiling to machine code<2>,Setting things up<2>,Our toy interpreter<2>,Tutorial part 4 Adding JIT-compilation to a toy interpreter<2> @anchor{cp/intro/tutorial04 compiling-to-machine-code}@anchor{ff} @subsubsection Compiling to machine code We want to generate machine code that can be cast to this type and then directly executed in-process: @quotation @example typedef int (*toyvm_compiled_func) (int); @end example @noindent @end quotation Our compiler isn't very sophisticated; it takes the implementation of each opcode above, and maps it directly to the operations supported by the libgccjit API. How should we handle the stack? In theory we could calculate what the stack depth will be at each opcode, and optimize away the stack manipulation "by hand". We'll see below that libgccjit is able to do this for us, so we'll implement stack manipulation in a direct way, by creating a @code{stack} array and @code{stack_depth} variables, local within the generated function, equivalent to this C code: @example int stack_depth; int stack[MAX_STACK_DEPTH]; @end example @noindent We'll also have local variables @code{x} and @code{y} for use when implementing the opcodes, equivalent to this: @example int x; int y; @end example @noindent This means our compiler has the following state: @quotation @example toyvm_function &toyvmfn; gccjit::context ctxt; gccjit::type int_type; gccjit::type bool_type; gccjit::type stack_type; /* int[MAX_STACK_DEPTH] */ gccjit::rvalue const_one; gccjit::function fn; gccjit::param param_arg; gccjit::lvalue stack; gccjit::lvalue stack_depth; gccjit::lvalue x; gccjit::lvalue y; gccjit::location op_locs[MAX_OPS]; gccjit::block initial_block; gccjit::block op_blocks[MAX_OPS]; @end example @noindent @end quotation @node Setting things up<2>,Populating the function<2>,Compiling to machine code<2>,Tutorial part 4 Adding JIT-compilation to a toy interpreter<2> @anchor{cp/intro/tutorial04 setting-things-up}@anchor{100} @subsubsection Setting things up First we create our types: @quotation @example void compilation_state::create_types () @{ /* Create types. */ int_type = ctxt.get_type (GCC_JIT_TYPE_INT); bool_type = ctxt.get_type (GCC_JIT_TYPE_BOOL); stack_type = ctxt.new_array_type (int_type, MAX_STACK_DEPTH); @end example @noindent @end quotation along with extracting a useful @cite{int} constant: @quotation @example const_one = ctxt.one (int_type); @} @end example @noindent @end quotation We'll implement push and pop in terms of the @code{stack} array and @code{stack_depth}. Here are helper functions for adding statements to a block, implementing pushing and popping values: @quotation @example void compilation_state::add_push (gccjit::block block, gccjit::rvalue rvalue, gccjit::location loc) @{ /* stack[stack_depth] = RVALUE */ block.add_assignment ( /* stack[stack_depth] */ ctxt.new_array_access ( stack, stack_depth, loc), rvalue, loc); /* "stack_depth++;". */ block.add_assignment_op ( stack_depth, GCC_JIT_BINARY_OP_PLUS, const_one, loc); @} void compilation_state::add_pop (gccjit::block block, gccjit::lvalue lvalue, gccjit::location loc) @{ /* "--stack_depth;". */ block.add_assignment_op ( stack_depth, GCC_JIT_BINARY_OP_MINUS, const_one, loc); /* "LVALUE = stack[stack_depth];". */ block.add_assignment ( lvalue, /* stack[stack_depth] */ ctxt.new_array_access (stack, stack_depth, loc), loc); @} @end example @noindent @end quotation We will support single-stepping through the generated code in the debugger, so we need to create @code{gccjit::location} instances, one per operation in the source code. These will reference the lines of e.g. @code{factorial.toy}. @quotation @example void compilation_state::create_locations () @{ for (int pc = 0; pc < toyvmfn.fn_num_ops; pc++) @{ toyvm_op *op = &toyvmfn.fn_ops[pc]; op_locs[pc] = ctxt.new_location (toyvmfn.fn_filename, op->op_linenum, 0); /* column */ @} @} @end example @noindent @end quotation Let's create the function itself. As usual, we create its parameter first, then use the parameter to create the function: @quotation @example void compilation_state::create_function (const char *funcname) @{ std::vector params; param_arg = ctxt.new_param (int_type, "arg", op_locs[0]); params.push_back (param_arg); fn = ctxt.new_function (GCC_JIT_FUNCTION_EXPORTED, int_type, funcname, params, 0, op_locs[0]); @end example @noindent @end quotation We create the locals within the function. @quotation @example stack = fn.new_local (stack_type, "stack"); stack_depth = fn.new_local (int_type, "stack_depth"); x = fn.new_local (int_type, "x"); y = fn.new_local (int_type, "y"); @end example @noindent @end quotation @node Populating the function<2>,Verifying the control flow graph<2>,Setting things up<2>,Tutorial part 4 Adding JIT-compilation to a toy interpreter<2> @anchor{cp/intro/tutorial04 populating-the-function}@anchor{101} @subsubsection Populating the function There's some one-time initialization, and the API treats the first block you create as the entrypoint of the function, so we need to create that block first: @quotation @example initial_block = fn.new_block ("initial"); @end example @noindent @end quotation We can now create blocks for each of the operations. Most of these will be consolidated into larger blocks when the optimizer runs. @quotation @example for (int pc = 0; pc < toyvmfn.fn_num_ops; pc++) @{ char buf[16]; sprintf (buf, "instr%i", pc); op_blocks[pc] = fn.new_block (buf); @} @end example @noindent @end quotation Now that we have a block it can jump to when it's done, we can populate the initial block: @quotation @example /* "stack_depth = 0;". */ initial_block.add_assignment (stack_depth, ctxt.zero (int_type), op_locs[0]); /* "PUSH (arg);". */ add_push (initial_block, param_arg, op_locs[0]); /* ...and jump to insn 0. */ initial_block.end_with_jump (op_blocks[0], op_locs[0]); @end example @noindent @end quotation We can now populate the blocks for the individual operations. We loop through them, adding instructions to their blocks: @quotation @example for (int pc = 0; pc < toyvmfn.fn_num_ops; pc++) @{ gccjit::location loc = op_locs[pc]; gccjit::block block = op_blocks[pc]; gccjit::block next_block = (pc < toyvmfn.fn_num_ops ? op_blocks[pc + 1] : NULL); toyvm_op *op; op = &toyvmfn.fn_ops[pc]; @end example @noindent @end quotation We're going to have another big @code{switch} statement for implementing the opcodes, this time for compiling them, rather than interpreting them. It's helpful to have macros for implementing push and pop, so that we can make the @code{switch} statement that's coming up look as much as possible like the one above within the interpreter: @example #define X_EQUALS_POP()\ add_pop (block, x, loc) #define Y_EQUALS_POP()\ add_pop (block, y, loc) #define PUSH_RVALUE(RVALUE)\ add_push (block, (RVALUE), loc) #define PUSH_X()\ PUSH_RVALUE (x) #define PUSH_Y() \ PUSH_RVALUE (y) @end example @noindent @cartouche @quotation Note A particularly clever implementation would have an @emph{identical} @code{switch} statement shared by the interpreter and the compiler, with some preprocessor "magic". We're not doing that here, for the sake of simplicity. @end quotation @end cartouche When I first implemented this compiler, I accidentally missed an edit when copying and pasting the @code{Y_EQUALS_POP} macro, so that popping the stack into @code{y} instead erroneously assigned it to @code{x}, leaving @code{y} uninitialized. To track this kind of thing down, we can use @pxref{102,,gccjit;;block;;add_comment()} to add descriptive comments to the internal representation. This is invaluable when looking through the generated IR for, say @code{factorial}: @quotation @example block.add_comment (opcode_names[op->op_opcode], loc); @end example @noindent @end quotation We can now write the big @code{switch} statement that implements the individual opcodes, populating the relevant block with statements: @quotation @example switch (op->op_opcode) @{ case DUP: X_EQUALS_POP (); PUSH_X (); PUSH_X (); break; case ROT: Y_EQUALS_POP (); X_EQUALS_POP (); PUSH_Y (); PUSH_X (); break; case BINARY_ADD: Y_EQUALS_POP (); X_EQUALS_POP (); PUSH_RVALUE ( ctxt.new_binary_op ( GCC_JIT_BINARY_OP_PLUS, int_type, x, y, loc)); break; case BINARY_SUBTRACT: Y_EQUALS_POP (); X_EQUALS_POP (); PUSH_RVALUE ( ctxt.new_binary_op ( GCC_JIT_BINARY_OP_MINUS, int_type, x, y, loc)); break; case BINARY_MULT: Y_EQUALS_POP (); X_EQUALS_POP (); PUSH_RVALUE ( ctxt.new_binary_op ( GCC_JIT_BINARY_OP_MULT, int_type, x, y, loc)); break; case BINARY_COMPARE_LT: Y_EQUALS_POP (); X_EQUALS_POP (); PUSH_RVALUE ( /* cast of bool to int */ ctxt.new_cast ( /* (x < y) as a bool */ ctxt.new_comparison ( GCC_JIT_COMPARISON_LT, x, y, loc), int_type, loc)); break; case RECURSE: @{ X_EQUALS_POP (); PUSH_RVALUE ( ctxt.new_call ( fn, x, loc)); break; @} case RETURN: X_EQUALS_POP (); block.end_with_return (x, loc); break; /* Ops taking an operand. */ case PUSH_CONST: PUSH_RVALUE ( ctxt.new_rvalue (int_type, op->op_operand)); break; case JUMP_ABS_IF_TRUE: X_EQUALS_POP (); block.end_with_conditional ( /* "(bool)x". */ ctxt.new_cast (x, bool_type, loc), op_blocks[op->op_operand], /* on_true */ next_block, /* on_false */ loc); break; default: assert(0); @} /* end of switch on opcode */ @end example @noindent @end quotation Every block must be terminated, via a call to one of the @code{gccjit::block::end_with_} entrypoints. This has been done for two of the opcodes, but we need to do it for the other ones, by jumping to the next block. @quotation @example if (op->op_opcode != JUMP_ABS_IF_TRUE && op->op_opcode != RETURN) block.end_with_jump (next_block, loc); @end example @noindent @end quotation This is analogous to simply incrementing the program counter. @node Verifying the control flow graph<2>,Compiling the context<2>,Populating the function<2>,Tutorial part 4 Adding JIT-compilation to a toy interpreter<2> @anchor{cp/intro/tutorial04 verifying-the-control-flow-graph}@anchor{103} @subsubsection Verifying the control flow graph Having finished looping over the blocks, the context is complete. As before, we can verify that the control flow and statements are sane by using @pxref{fa,,gccjit;;function;;dump_to_dot()}: @example fn.dump_to_dot ("/tmp/factorial.dot"); @end example @noindent and viewing the result. Note how the label names, comments, and variable names show up in the dump, to make it easier to spot errors in our compiler. @quotation @float Figure @image{factorial,,,image of a control flow graph,png} @end float @end quotation @node Compiling the context<2>,Single-stepping through the generated code<2>,Verifying the control flow graph<2>,Tutorial part 4 Adding JIT-compilation to a toy interpreter<2> @anchor{cp/intro/tutorial04 compiling-the-context}@anchor{104} @subsubsection Compiling the context Having finished looping over the blocks and populating them with statements, the context is complete. We can now compile it, and extract machine code from the result: @quotation @example gcc_jit_result *result = state.compile (); return (toyvm_compiled_func)gcc_jit_result_get_code (result, funcname); @end example @noindent @end quotation We can now run the result: @quotation @example toyvm_compiled_func code = fn->compile (); printf ("compiler result: %d\n", code (atoi (argv[2]))); @end example @noindent @end quotation @node Single-stepping through the generated code<2>,Examining the generated code<2>,Compiling the context<2>,Tutorial part 4 Adding JIT-compilation to a toy interpreter<2> @anchor{cp/intro/tutorial04 single-stepping-through-the-generated-code}@anchor{105} @subsubsection Single-stepping through the generated code It's possible to debug the generated code. To do this we need to both: @quotation @itemize * @item Set up source code locations for our statements, so that we can meaningfully step through the code. We did this above by calling @pxref{106,,gccjit;;context;;new_location()} and using the results. @item Enable the generation of debugging information, by setting @pxref{42,,GCC_JIT_BOOL_OPTION_DEBUGINFO} on the @code{gccjit::context} via @pxref{eb,,gccjit;;context;;set_bool_option()}: @example ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DEBUGINFO, 1); @end example @noindent @end itemize @end quotation Having done this, we can put a breakpoint on the generated function: @example $ gdb --args ./toyvm factorial.toy 10 (gdb) break factorial Function "factorial" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 1 (factorial) pending. (gdb) run Breakpoint 1, factorial (arg=10) at factorial.toy:14 14 DUP @end example @noindent We've set up location information, which references @code{factorial.toy}. This allows us to use e.g. @code{list} to see where we are in the script: @example (gdb) list 9 10 # Initial state: 11 # stack: [arg] 12 13 # 0: 14 DUP 15 # stack: [arg, arg] 16 17 # 1: 18 PUSH_CONST 2 @end example @noindent and to step through the function, examining the data: @example (gdb) n 18 PUSH_CONST 2 (gdb) n 22 BINARY_COMPARE_LT (gdb) print stack $5 = @{10, 10, 2, 0, -7152, 32767, 0, 0@} (gdb) print stack_depth $6 = 3 @end example @noindent You'll see that the parts of the @code{stack} array that haven't been touched yet are uninitialized. @cartouche @quotation Note Turning on optimizations may lead to unpredictable results when stepping through the generated code: the execution may appear to "jump around" the source code. This is analogous to turning up the optimization level in a regular compiler. @end quotation @end cartouche @node Examining the generated code<2>,Putting it all together<2>,Single-stepping through the generated code<2>,Tutorial part 4 Adding JIT-compilation to a toy interpreter<2> @anchor{cp/intro/tutorial04 examining-the-generated-code}@anchor{107} @subsubsection Examining the generated code How good is the optimized code? We can turn up optimizations, by calling @pxref{ec,,gccjit;;context;;set_int_option()} with @pxref{1f,,GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL}: @example ctxt.set_int_option (GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 3); @end example @noindent One of GCC's internal representations is called "gimple". A dump of the initial gimple representation of the code can be seen by setting: @example ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE, 1); @end example @noindent With optimization on and source locations displayed, this gives: @c We'll use "c" for gimple dumps @example factorial (signed int arg) @{ D.80; signed int D.81; signed int D.82; signed int D.83; signed int D.84; signed int D.85; signed int y; signed int x; signed int stack_depth; signed int stack[8]; try @{ initial: stack_depth = 0; stack[stack_depth] = arg; stack_depth = stack_depth + 1; goto instr0; instr0: /* DUP */: stack_depth = stack_depth + -1; x = stack[stack_depth]; stack[stack_depth] = x; stack_depth = stack_depth + 1; stack[stack_depth] = x; stack_depth = stack_depth + 1; goto instr1; instr1: /* PUSH_CONST */: stack[stack_depth] = 2; stack_depth = stack_depth + 1; goto instr2; /* etc */ @end example @noindent You can see the generated machine code in assembly form via: @example ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE, 1); result = ctxt.compile (); @end example @noindent which shows that (on this x86_64 box) the compiler has unrolled the loop and is using MMX instructions to perform several multiplications simultaneously: @example .file "fake.c" .text .Ltext0: .p2align 4,,15 .globl factorial .type factorial, @@function factorial: .LFB0: .file 1 "factorial.toy" .loc 1 14 0 .cfi_startproc .LVL0: .L2: .loc 1 26 0 cmpl $1, %edi jle .L13 leal -1(%rdi), %edx movl %edx, %ecx shrl $2, %ecx leal 0(,%rcx,4), %esi testl %esi, %esi je .L14 cmpl $9, %edx jbe .L14 leal -2(%rdi), %eax movl %eax, -16(%rsp) leal -3(%rdi), %eax movd -16(%rsp), %xmm0 movl %edi, -16(%rsp) movl %eax, -12(%rsp) movd -16(%rsp), %xmm1 xorl %eax, %eax movl %edx, -16(%rsp) movd -12(%rsp), %xmm4 movd -16(%rsp), %xmm6 punpckldq %xmm4, %xmm0 movdqa .LC1(%rip), %xmm4 punpckldq %xmm6, %xmm1 punpcklqdq %xmm0, %xmm1 movdqa .LC0(%rip), %xmm0 jmp .L5 # etc - edited for brevity @end example @noindent This is clearly overkill for a function that will likely overflow the @code{int} type before the vectorization is worthwhile - but then again, this is a toy example. Turning down the optimization level to 2: @example ctxt.set_int_option (GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL, 2); @end example @noindent yields this code, which is simple enough to quote in its entirety: @example .file "fake.c" .text .p2align 4,,15 .globl factorial .type factorial, @@function factorial: .LFB0: .cfi_startproc .L2: cmpl $1, %edi jle .L8 movl $1, %edx jmp .L4 .p2align 4,,10 .p2align 3 .L6: movl %eax, %edi .L4: .L5: leal -1(%rdi), %eax imull %edi, %edx cmpl $1, %eax jne .L6 .L3: .L7: imull %edx, %eax ret .L8: movl %edi, %eax movl $1, %edx jmp .L7 .cfi_endproc .LFE0: .size factorial, .-factorial .ident "GCC: (GNU) 4.9.0 20131023 (Red Hat 0.2-%@{gcc_release@})" .section .note.GNU-stack,"",@@progbits @end example @noindent Note that the stack pushing and popping have been eliminated, as has the recursive call (in favor of an iteration). @node Putting it all together<2>,Behind the curtain How does our code get optimized?<2>,Examining the generated code<2>,Tutorial part 4 Adding JIT-compilation to a toy interpreter<2> @anchor{cp/intro/tutorial04 putting-it-all-together}@anchor{108} @subsubsection Putting it all together The complete example can be seen in the source tree at @code{gcc/jit/docs/examples/tut04-toyvm/toyvm.cc} along with a Makefile and a couple of sample .toy scripts: @example $ ls -al drwxrwxr-x. 2 david david 4096 Sep 19 17:46 . drwxrwxr-x. 3 david david 4096 Sep 19 15:26 .. -rw-rw-r--. 1 david david 615 Sep 19 12:43 factorial.toy -rw-rw-r--. 1 david david 834 Sep 19 13:08 fibonacci.toy -rw-rw-r--. 1 david david 238 Sep 19 14:22 Makefile -rw-rw-r--. 1 david david 16457 Sep 19 17:07 toyvm.cc $ make toyvm g++ -Wall -g -o toyvm toyvm.cc -lgccjit $ ./toyvm factorial.toy 10 interpreter result: 3628800 compiler result: 3628800 $ ./toyvm fibonacci.toy 10 interpreter result: 55 compiler result: 55 @end example @noindent @node Behind the curtain How does our code get optimized?<2>,,Putting it all together<2>,Tutorial part 4 Adding JIT-compilation to a toy interpreter<2> @anchor{cp/intro/tutorial04 behind-the-curtain-how-does-our-code-get-optimized}@anchor{109} @subsubsection Behind the curtain: How does our code get optimized? Our example is done, but you may be wondering about exactly how the compiler turned what we gave it into the machine code seen above. We can examine what the compiler is doing in detail by setting: @example state.ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING, 1); state.ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES, 1); @end example @noindent This will dump detailed information about the compiler's state to a directory under @code{/tmp}, and keep it from being cleaned up. The precise names and their formats of these files is subject to change. Higher optimization levels lead to more files. Here's what I saw (edited for brevity; there were almost 200 files): @example intermediate files written to /tmp/libgccjit-KPQbGw $ ls /tmp/libgccjit-KPQbGw/ fake.c.000i.cgraph fake.c.000i.type-inheritance fake.c.004t.gimple fake.c.007t.omplower fake.c.008t.lower fake.c.011t.eh fake.c.012t.cfg fake.c.014i.visibility fake.c.015i.early_local_cleanups fake.c.016t.ssa # etc @end example @noindent The gimple code is converted into Static Single Assignment form, with annotations for use when generating the debuginfo: @example $ less /tmp/libgccjit-KPQbGw/fake.c.016t.ssa @end example @noindent @example ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0) factorial (signed int arg) @{ signed int stack[8]; signed int stack_depth; signed int x; signed int y; _20; signed int _21; signed int _38; signed int _44; signed int _51; signed int _56; initial: stack_depth_3 = 0; # DEBUG stack_depth => stack_depth_3 stack[stack_depth_3] = arg_5(D); stack_depth_7 = stack_depth_3 + 1; # DEBUG stack_depth => stack_depth_7 # DEBUG instr0 => NULL # DEBUG /* DUP */ => NULL stack_depth_8 = stack_depth_7 + -1; # DEBUG stack_depth => stack_depth_8 x_9 = stack[stack_depth_8]; # DEBUG x => x_9 stack[stack_depth_8] = x_9; stack_depth_11 = stack_depth_8 + 1; # DEBUG stack_depth => stack_depth_11 stack[stack_depth_11] = x_9; stack_depth_13 = stack_depth_11 + 1; # DEBUG stack_depth => stack_depth_13 # DEBUG instr1 => NULL # DEBUG /* PUSH_CONST */ => NULL stack[stack_depth_13] = 2; /* etc; edited for brevity */ @end example @noindent We can perhaps better see the code by turning off @pxref{42,,GCC_JIT_BOOL_OPTION_DEBUGINFO} to suppress all those @code{DEBUG} statements, giving: @example $ less /tmp/libgccjit-1Hywc0/fake.c.016t.ssa @end example @noindent @example ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0) factorial (signed int arg) @{ signed int stack[8]; signed int stack_depth; signed int x; signed int y; _20; signed int _21; signed int _38; signed int _44; signed int _51; signed int _56; initial: stack_depth_3 = 0; stack[stack_depth_3] = arg_5(D); stack_depth_7 = stack_depth_3 + 1; stack_depth_8 = stack_depth_7 + -1; x_9 = stack[stack_depth_8]; stack[stack_depth_8] = x_9; stack_depth_11 = stack_depth_8 + 1; stack[stack_depth_11] = x_9; stack_depth_13 = stack_depth_11 + 1; stack[stack_depth_13] = 2; stack_depth_15 = stack_depth_13 + 1; stack_depth_16 = stack_depth_15 + -1; y_17 = stack[stack_depth_16]; stack_depth_18 = stack_depth_16 + -1; x_19 = stack[stack_depth_18]; _20 = x_19 < y_17; _21 = (signed int) _20; stack[stack_depth_18] = _21; stack_depth_23 = stack_depth_18 + 1; stack_depth_24 = stack_depth_23 + -1; x_25 = stack[stack_depth_24]; if (x_25 != 0) goto (instr9); else goto (instr4); instr4: /* DUP */: stack_depth_26 = stack_depth_24 + -1; x_27 = stack[stack_depth_26]; stack[stack_depth_26] = x_27; stack_depth_29 = stack_depth_26 + 1; stack[stack_depth_29] = x_27; stack_depth_31 = stack_depth_29 + 1; stack[stack_depth_31] = 1; stack_depth_33 = stack_depth_31 + 1; stack_depth_34 = stack_depth_33 + -1; y_35 = stack[stack_depth_34]; stack_depth_36 = stack_depth_34 + -1; x_37 = stack[stack_depth_36]; _38 = x_37 - y_35; stack[stack_depth_36] = _38; stack_depth_40 = stack_depth_36 + 1; stack_depth_41 = stack_depth_40 + -1; x_42 = stack[stack_depth_41]; _44 = factorial (x_42); stack[stack_depth_41] = _44; stack_depth_46 = stack_depth_41 + 1; stack_depth_47 = stack_depth_46 + -1; y_48 = stack[stack_depth_47]; stack_depth_49 = stack_depth_47 + -1; x_50 = stack[stack_depth_49]; _51 = x_50 * y_48; stack[stack_depth_49] = _51; stack_depth_53 = stack_depth_49 + 1; # stack_depth_1 = PHI instr9: /* RETURN */: stack_depth_54 = stack_depth_1 + -1; x_55 = stack[stack_depth_54]; _56 = x_55; stack =@{v@} @{CLOBBER@}; return _56; @} @end example @noindent Note in the above how all the @code{gccjit::block} instances we created have been consolidated into just 3 blocks in GCC's internal representation: @code{initial}, @code{instr4} and @code{instr9}. @menu * Optimizing away stack manipulation: Optimizing away stack manipulation<2>. * Elimination of tail recursion: Elimination of tail recursion<2>. @end menu @node Optimizing away stack manipulation<2>,Elimination of tail recursion<2>,,Behind the curtain How does our code get optimized?<2> @anchor{cp/intro/tutorial04 optimizing-away-stack-manipulation}@anchor{10a} @subsubsection Optimizing away stack manipulation Recall our simple implementation of stack operations. Let's examine how the stack operations are optimized away. After a pass of constant-propagation, the depth of the stack at each opcode can be determined at compile-time: @example $ less /tmp/libgccjit-1Hywc0/fake.c.021t.ccp1 @end example @noindent @example ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0) factorial (signed int arg) @{ signed int stack[8]; signed int stack_depth; signed int x; signed int y; _20; signed int _21; signed int _38; signed int _44; signed int _51; initial: stack[0] = arg_5(D); x_9 = stack[0]; stack[0] = x_9; stack[1] = x_9; stack[2] = 2; y_17 = stack[2]; x_19 = stack[1]; _20 = x_19 < y_17; _21 = (signed int) _20; stack[1] = _21; x_25 = stack[1]; if (x_25 != 0) goto (instr9); else goto (instr4); instr4: /* DUP */: x_27 = stack[0]; stack[0] = x_27; stack[1] = x_27; stack[2] = 1; y_35 = stack[2]; x_37 = stack[1]; _38 = x_37 - y_35; stack[1] = _38; x_42 = stack[1]; _44 = factorial (x_42); stack[1] = _44; y_48 = stack[1]; x_50 = stack[0]; _51 = x_50 * y_48; stack[0] = _51; instr9: /* RETURN */: x_55 = stack[0]; x_56 = x_55; stack =@{v@} @{CLOBBER@}; return x_56; @} @end example @noindent Note how, in the above, all those @code{stack_depth} values are now just constants: we're accessing specific stack locations at each opcode. The "esra" pass ("Early Scalar Replacement of Aggregates") breaks out our "stack" array into individual elements: @example $ less /tmp/libgccjit-1Hywc0/fake.c.024t.esra @end example @noindent @example ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0) Created a replacement for stack offset: 0, size: 32: stack$0 Created a replacement for stack offset: 32, size: 32: stack$1 Created a replacement for stack offset: 64, size: 32: stack$2 Symbols to be put in SSA form @{ D.89 D.90 D.91 @} Incremental SSA update started at block: 0 Number of blocks in CFG: 5 Number of blocks to update: 4 ( 80%) factorial (signed int arg) @{ signed int stack$2; signed int stack$1; signed int stack$0; signed int stack[8]; signed int stack_depth; signed int x; signed int y; _20; signed int _21; signed int _38; signed int _44; signed int _51; initial: stack$0_45 = arg_5(D); x_9 = stack$0_45; stack$0_39 = x_9; stack$1_32 = x_9; stack$2_30 = 2; y_17 = stack$2_30; x_19 = stack$1_32; _20 = x_19 < y_17; _21 = (signed int) _20; stack$1_28 = _21; x_25 = stack$1_28; if (x_25 != 0) goto (instr9); else goto (instr4); instr4: /* DUP */: x_27 = stack$0_39; stack$0_22 = x_27; stack$1_14 = x_27; stack$2_12 = 1; y_35 = stack$2_12; x_37 = stack$1_14; _38 = x_37 - y_35; stack$1_10 = _38; x_42 = stack$1_10; _44 = factorial (x_42); stack$1_6 = _44; y_48 = stack$1_6; x_50 = stack$0_22; _51 = x_50 * y_48; stack$0_1 = _51; # stack$0_52 = PHI instr9: /* RETURN */: x_55 = stack$0_52; x_56 = x_55; stack =@{v@} @{CLOBBER@}; return x_56; @} @end example @noindent Hence at this point, all those pushes and pops of the stack are now simply assignments to specific temporary variables. After some copy propagation, the stack manipulation has been completely optimized away: @example $ less /tmp/libgccjit-1Hywc0/fake.c.026t.copyprop1 @end example @noindent @example ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0) factorial (signed int arg) @{ signed int stack$2; signed int stack$1; signed int stack$0; signed int stack[8]; signed int stack_depth; signed int x; signed int y; _20; signed int _21; signed int _38; signed int _44; signed int _51; initial: stack$0_39 = arg_5(D); _20 = arg_5(D) <= 1; _21 = (signed int) _20; if (_21 != 0) goto (instr9); else goto (instr4); instr4: /* DUP */: _38 = arg_5(D) + -1; _44 = factorial (_38); _51 = arg_5(D) * _44; stack$0_1 = _51; # stack$0_52 = PHI instr9: /* RETURN */: stack =@{v@} @{CLOBBER@}; return stack$0_52; @} @end example @noindent Later on, another pass finally eliminated @code{stack_depth} local and the unused parts of the @cite{stack`} array altogether: @example $ less /tmp/libgccjit-1Hywc0/fake.c.036t.release_ssa @end example @noindent @example ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0) Released 44 names, 314.29%, removed 44 holes factorial (signed int arg) @{ signed int stack$0; signed int mult_acc_1; _5; signed int _6; signed int _7; signed int mul_tmp_10; signed int mult_acc_11; signed int mult_acc_13; # arg_9 = PHI # mult_acc_13 = PHI <1(0)> initial: : # arg_4 = PHI # mult_acc_1 = PHI _5 = arg_4 <= 1; _6 = (signed int) _5; if (_6 != 0) goto (instr9); else goto (instr4); instr4: /* DUP */: _7 = arg_4 + -1; mult_acc_11 = mult_acc_1 * arg_4; goto ; # stack$0_12 = PHI instr9: /* RETURN */: mul_tmp_10 = mult_acc_1 * stack$0_12; return mul_tmp_10; @} @end example @noindent @node Elimination of tail recursion<2>,,Optimizing away stack manipulation<2>,Behind the curtain How does our code get optimized?<2> @anchor{cp/intro/tutorial04 elimination-of-tail-recursion}@anchor{10b} @subsubsection Elimination of tail recursion Another significant optimization is the detection that the call to @code{factorial} is tail recursion, which can be eliminated in favor of an iteration: @example $ less /tmp/libgccjit-1Hywc0/fake.c.030t.tailr1 @end example @noindent @example ;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0) Symbols to be put in SSA form @{ D.88 @} Incremental SSA update started at block: 0 Number of blocks in CFG: 5 Number of blocks to update: 4 ( 80%) factorial (signed int arg) @{ signed int stack$2; signed int stack$1; signed int stack$0; signed int stack[8]; signed int stack_depth; signed int x; signed int y; signed int mult_acc_1; _20; signed int _21; signed int _38; signed int mul_tmp_44; signed int mult_acc_51; # arg_5 = PHI # mult_acc_1 = PHI <1(0), mult_acc_51(3)> initial: _20 = arg_5 <= 1; _21 = (signed int) _20; if (_21 != 0) goto (instr9); else goto (instr4); instr4: /* DUP */: _38 = arg_5 + -1; mult_acc_51 = mult_acc_1 * arg_5; goto (initial); # stack$0_52 = PHI instr9: /* RETURN */: stack =@{v@} @{CLOBBER@}; mul_tmp_44 = mult_acc_1 * stack$0_52; return mul_tmp_44; @} @end example @noindent @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Topic Reference<2>,,Tutorial<2>,C++ bindings for libgccjit @anchor{cp/topics/index doc}@anchor{10c}@anchor{cp/topics/index topic-reference}@anchor{10d} @section Topic Reference @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @menu * Compilation contexts: Compilation contexts<2>. * Objects: Objects<2>. * Types: Types<2>. * Expressions: Expressions<2>. * Creating and using functions: Creating and using functions<2>. * Source Locations: Source Locations<2>. * Compiling a context: Compiling a context<2>. Compilation contexts * Lifetime-management: Lifetime-management<2>. * Thread-safety: Thread-safety<2>. * Error-handling: Error-handling<3>. * Debugging: Debugging<2>. * Options: Options<4>. Options * String Options: String Options<2>. * Boolean options: Boolean options<2>. * Integer options: Integer options<2>. Types * Standard types: Standard types<2>. * Pointers@comma{} const@comma{} and volatile: Pointers const and volatile<2>. * Structures and unions: Structures and unions<2>. Expressions * Rvalues: Rvalues<2>. * Lvalues: Lvalues<2>. * Working with pointers@comma{} structs and unions: Working with pointers structs and unions<2>. Rvalues * Simple expressions: Simple expressions<2>. * Unary Operations: Unary Operations<2>. * Binary Operations: Binary Operations<2>. * Comparisons: Comparisons<2>. * Function calls: Function calls<2>. * Type-coercion: Type-coercion<2>. Lvalues * Global variables: Global variables<2>. Creating and using functions * Params: Params<2>. * Functions: Functions<2>. * Blocks: Blocks<2>. * Statements: Statements<2>. Source Locations * Faking it: Faking it<2>. Compiling a context * In-memory compilation: In-memory compilation<2>. * Ahead-of-time compilation: Ahead-of-time compilation<2>. @end menu @node Compilation contexts<2>,Objects<2>,,Topic Reference<2> @anchor{cp/topics/contexts compilation-contexts}@anchor{10e}@anchor{cp/topics/contexts doc}@anchor{10f} @subsection Compilation contexts @geindex gccjit;;context (C++ class) @anchor{cp/topics/contexts gccjit context}@anchor{110} @deffn {C++ Class} gccjit::context @end deffn The top-level of the C++ API is the @pxref{110,,gccjit;;context} type. A @pxref{110,,gccjit;;context} instance encapsulates the state of a compilation. You can set up options on it, and add types, functions and code. Invoking @pxref{e9,,gccjit;;context;;compile()} on it gives you a @pxref{16,,gcc_jit_result *}. It is a thin wrapper around the C API's @pxref{8,,gcc_jit_context *}. @menu * Lifetime-management: Lifetime-management<2>. * Thread-safety: Thread-safety<2>. * Error-handling: Error-handling<3>. * Debugging: Debugging<2>. * Options: Options<4>. @end menu @node Lifetime-management<2>,Thread-safety<2>,,Compilation contexts<2> @anchor{cp/topics/contexts lifetime-management}@anchor{111} @subsubsection Lifetime-management Contexts are the unit of lifetime-management within the API: objects have their lifetime bounded by the context they are created within, and cleanup of such objects is done for you when the context is released. @geindex gccjit;;context;;acquire (C++ function) @anchor{cp/topics/contexts gccjit context acquire}@anchor{e3} @deffn {C++ Function} gccjit::context gccjit::context::acquire () This function acquires a new @pxref{110,,gccjit;;context} instance, which is independent of any others that may be present within this process. @end deffn @geindex gccjit;;context;;release (C++ function) @anchor{cp/topics/contexts gccjit context release}@anchor{e5} @deffn {C++ Function} void gccjit::context::release () This function releases all resources associated with the given context. Both the context itself and all of its @code{gccjit::object *} instances are cleaned up. It should be called exactly once on a given context. It is invalid to use the context or any of its "contextual" objects after calling this. @example ctxt.release (); @end example @noindent @end deffn @geindex gccjit;;context;;new_child_context (C++ function) @anchor{cp/topics/contexts gccjit context new_child_context}@anchor{112} @deffn {C++ Function} gccjit::context gccjit::context::new_child_context () Given an existing JIT context, create a child context. The child inherits a copy of all option-settings from the parent. The child can reference objects created within the parent, but not vice-versa. The lifetime of the child context must be bounded by that of the parent: you should release a child context before releasing the parent context. If you use a function from a parent context within a child context, you have to compile the parent context before you can compile the child context, and the gccjit::result of the parent context must outlive the gccjit::result of the child context. This allows caching of shared initializations. For example, you could create types and declarations of global functions in a parent context once within a process, and then create child contexts whenever a function or loop becomes hot. Each such child context can be used for JIT-compiling just one function or loop, but can reference types and helper functions created within the parent context. Contexts can be arbitrarily nested, provided the above rules are followed, but it's probably not worth going above 2 or 3 levels, and there will likely be a performance hit for such nesting. @end deffn @node Thread-safety<2>,Error-handling<3>,Lifetime-management<2>,Compilation contexts<2> @anchor{cp/topics/contexts thread-safety}@anchor{113} @subsubsection Thread-safety Instances of @pxref{110,,gccjit;;context} created via @pxref{e3,,gccjit;;context;;acquire()} are independent from each other: only one thread may use a given context at once, but multiple threads could each have their own contexts without needing locks. Contexts created via @pxref{112,,gccjit;;context;;new_child_context()} are related to their parent context. They can be partitioned by their ultimate ancestor into independent "family trees". Only one thread within a process may use a given "family tree" of such contexts at once, and if you're using multiple threads you should provide your own locking around entire such context partitions. @node Error-handling<3>,Debugging<2>,Thread-safety<2>,Compilation contexts<2> @anchor{cp/topics/contexts error-handling}@anchor{114} @subsubsection Error-handling @c FIXME: How does error-handling work for C++ API? You can only compile and get code from a context if no errors occur. In general, if an error occurs when using an API entrypoint, it returns NULL. You don't have to check everywhere for NULL results, since the API gracefully handles a NULL being passed in for any argument. Errors are printed on stderr and can be queried using @pxref{115,,gccjit;;context;;get_first_error()}. @geindex gccjit;;context;;get_first_error (C++ function) @anchor{cp/topics/contexts gccjit context get_first_error__gccjit contextP}@anchor{115} @deffn {C++ Function} const char* gccjit::context::get_first_error (gccjit::context* ctxt) Returns the first error message that occurred on the context. The returned string is valid for the rest of the lifetime of the context. If no errors occurred, this will be NULL. @end deffn @node Debugging<2>,Options<4>,Error-handling<3>,Compilation contexts<2> @anchor{cp/topics/contexts debugging}@anchor{116} @subsubsection Debugging @geindex gccjit;;context;;dump_to_file (C++ function) @anchor{cp/topics/contexts gccjit context dump_to_file__ssCR i}@anchor{117} @deffn {C++ Function} void gccjit::context::dump_to_file (const std::string& path, int update_locations) To help with debugging: dump a C-like representation to the given path, describing what's been set up on the context. If "update_locations" is true, then also set up @pxref{118,,gccjit;;location} information throughout the context, pointing at the dump file as if it were a source file. This may be of use in conjunction with @code{GCCJIT::BOOL_OPTION_DEBUGINFO} to allow stepping through the code in a debugger. @end deffn @geindex gccjit;;context;;dump_reproducer_to_file (C++ function) @anchor{cp/topics/contexts gccjit context dump_reproducer_to_file__gcc_jit_contextP cCP}@anchor{119} @deffn {C++ Function} void gccjit::context::dump_reproducer_to_file (gcc_jit_context* ctxt, const char* path) This is a thin wrapper around the C API @pxref{5d,,gcc_jit_context_dump_reproducer_to_file()}, and hence works the same way. Note that the generated source is C code, not C++; this might be of use for seeing what the C++ bindings are doing at the C level. @end deffn @node Options<4>,,Debugging<2>,Compilation contexts<2> @anchor{cp/topics/contexts options}@anchor{11a} @subsubsection Options @menu * String Options: String Options<2>. * Boolean options: Boolean options<2>. * Integer options: Integer options<2>. @end menu @node String Options<2>,Boolean options<2>,,Options<4> @anchor{cp/topics/contexts string-options}@anchor{11b} @subsubsection String Options @geindex gccjit;;context;;set_str_option (C++ function) @anchor{cp/topics/contexts gccjit context set_str_option__enum cCP}@anchor{11c} @deffn {C++ Function} void gccjit::context::set_str_option (enum gcc_jit_str_option, const char* value) Set a string option of the context. This is a thin wrapper around the C API @pxref{61,,gcc_jit_context_set_str_option()}; the options have the same meaning. @end deffn @node Boolean options<2>,Integer options<2>,String Options<2>,Options<4> @anchor{cp/topics/contexts boolean-options}@anchor{11d} @subsubsection Boolean options @geindex gccjit;;context;;set_bool_option (C++ function) @anchor{cp/topics/contexts gccjit context set_bool_option__enum i}@anchor{eb} @deffn {C++ Function} void gccjit::context::set_bool_option (enum gcc_jit_bool_option, int value) Set a boolean option of the context. This is a thin wrapper around the C API @pxref{1b,,gcc_jit_context_set_bool_option()}; the options have the same meaning. @end deffn @node Integer options<2>,,Boolean options<2>,Options<4> @anchor{cp/topics/contexts integer-options}@anchor{11e} @subsubsection Integer options @geindex gccjit;;context;;set_int_option (C++ function) @anchor{cp/topics/contexts gccjit context set_int_option__enum i}@anchor{ec} @deffn {C++ Function} void gccjit::context::set_int_option (enum gcc_jit_int_option, int value) Set an integer option of the context. This is a thin wrapper around the C API @pxref{1e,,gcc_jit_context_set_int_option()}; the options have the same meaning. @end deffn @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Objects<2>,Types<2>,Compilation contexts<2>,Topic Reference<2> @anchor{cp/topics/objects objects}@anchor{11f}@anchor{cp/topics/objects doc}@anchor{120} @subsection Objects @geindex gccjit;;object (C++ class) @anchor{cp/topics/objects gccjit object}@anchor{121} @deffn {C++ Class} gccjit::object @end deffn Almost every entity in the API (with the exception of @pxref{110,,gccjit;;context} and @pxref{16,,gcc_jit_result *}) is a "contextual" object, a @pxref{121,,gccjit;;object}. A JIT object: @quotation @itemize * @item is associated with a @pxref{110,,gccjit;;context}. @item is automatically cleaned up for you when its context is released so you don't need to manually track and cleanup all objects, just the contexts. @end itemize @end quotation The C++ class hierarchy within the @code{gccjit} namespace looks like this: @example +- object +- location +- type +- struct +- field +- function +- block +- rvalue +- lvalue +- param @end example @noindent The @pxref{121,,gccjit;;object} base class has the following operations: @geindex gccjit;;object;;get_context (C++ function) @anchor{cp/topics/objects gccjit object get_contextC}@anchor{122} @deffn {C++ Function} gccjit::context gccjit::object::get_context () const Which context is the obj within? @end deffn @geindex gccjit;;object;;get_debug_string (C++ function) @anchor{cp/topics/objects gccjit object get_debug_stringC}@anchor{e6} @deffn {C++ Function} std::string gccjit::object::get_debug_string () const Generate a human-readable description for the given object. For example, @example printf ("obj: %s\n", obj.get_debug_string ().c_str ()); @end example @noindent might give this text on stdout: @example obj: 4.0 * (float)i @end example @noindent @end deffn @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Types<2>,Expressions<2>,Objects<2>,Topic Reference<2> @anchor{cp/topics/types doc}@anchor{123}@anchor{cp/topics/types types}@anchor{124} @subsection Types @geindex gccjit;;type (C++ class) @anchor{cp/topics/types gccjit type}@anchor{125} @deffn {C++ Class} gccjit::type gccjit::type represents a type within the library. It is a subclass of @pxref{121,,gccjit;;object}. @end deffn Types can be created in several ways: @itemize * @item fundamental types can be accessed using @pxref{e4,,gccjit;;context;;get_type()}: @example gccjit::type int_type = ctxt.get_type (GCC_JIT_TYPE_INT); @end example @noindent or using the @code{gccjit::context::get_int_type} template: @example gccjit::type t = ctxt.get_int_type (); @end example @noindent See @pxref{b,,gcc_jit_context_get_type()} for the available types. @item derived types can be accessed by using functions such as @pxref{126,,gccjit;;type;;get_pointer()} and @pxref{127,,gccjit;;type;;get_const()}: @example gccjit::type const_int_star = int_type.get_const ().get_pointer (); gccjit::type int_const_star = int_type.get_pointer ().get_const (); @end example @noindent @item by creating structures (see below). @end itemize @menu * Standard types: Standard types<2>. * Pointers@comma{} const@comma{} and volatile: Pointers const and volatile<2>. * Structures and unions: Structures and unions<2>. @end menu @node Standard types<2>,Pointers const and volatile<2>,,Types<2> @anchor{cp/topics/types standard-types}@anchor{128} @subsubsection Standard types @geindex gccjit;;context;;get_type (C++ function) @anchor{cp/topics/types gccjit context get_type__enum}@anchor{e4} @deffn {C++ Function} gccjit::type gccjit::context::get_type (enum gcc_jit_types) Access a specific type. This is a thin wrapper around @pxref{b,,gcc_jit_context_get_type()}; the parameter has the same meaning. @end deffn @geindex gccjit;;context;;get_int_type (C++ function) @anchor{cp/topics/types gccjit context get_int_type__s i}@anchor{129} @deffn {C++ Function} gccjit::type gccjit::context::get_int_type (size_t num_bytes, int is_signed) Access the integer type of the given size. @end deffn @geindex gccjit;;context;;get_int_type (C++ function) @anchor{cp/topics/types gccjit context get_int_type T}@anchor{12a} @deffn {C++ Function} gccjit::type gccjit::context::get_int_type () Access the given integer type. For example, you could map the @code{unsigned short} type into a gccjit::type via: @example gccjit::type t = ctxt.get_int_type (); @end example @noindent @end deffn @node Pointers const and volatile<2>,Structures and unions<2>,Standard types<2>,Types<2> @anchor{cp/topics/types pointers-const-and-volatile}@anchor{12b} @subsubsection Pointers, @cite{const}, and @cite{volatile} @geindex gccjit;;type;;get_pointer (C++ function) @anchor{cp/topics/types gccjit type get_pointer}@anchor{126} @deffn {C++ Function} gccjit::type gccjit::type::get_pointer () Given type "T", get type "T*". @end deffn @c FIXME: get_const doesn't seem to exist @geindex gccjit;;type;;get_const (C++ function) @anchor{cp/topics/types gccjit type get_const}@anchor{127} @deffn {C++ Function} gccjit::type gccjit::type::get_const () Given type "T", get type "const T". @end deffn @geindex gccjit;;type;;get_volatile (C++ function) @anchor{cp/topics/types gccjit type get_volatile}@anchor{12c} @deffn {C++ Function} gccjit::type gccjit::type::get_volatile () Given type "T", get type "volatile T". @end deffn @geindex gccjit;;context;;new_array_type (C++ function) @anchor{cp/topics/types gccjit context new_array_type__gccjit type i gccjit location}@anchor{12d} @deffn {C++ Function} gccjit::type gccjit::context::new_array_type (gccjit::type element_type, int num_elements, gccjit::location loc) Given type "T", get type "T[N]" (for a constant N). Param "loc" is optional. @end deffn @node Structures and unions<2>,,Pointers const and volatile<2>,Types<2> @anchor{cp/topics/types structures-and-unions}@anchor{12e} @subsubsection Structures and unions @geindex gccjit;;struct_ (C++ class) @anchor{cp/topics/types gccjit struct_}@anchor{12f} @deffn {C++ Class} gccjit::struct_ @end deffn A compound type analagous to a C @cite{struct}. @pxref{12f,,gccjit;;struct_} is a subclass of @pxref{125,,gccjit;;type} (and thus of @pxref{121,,gccjit;;object} in turn). @geindex gccjit;;field (C++ class) @anchor{cp/topics/types gccjit field}@anchor{130} @deffn {C++ Class} gccjit::field @end deffn A field within a @pxref{12f,,gccjit;;struct_}. @pxref{130,,gccjit;;field} is a subclass of @pxref{121,,gccjit;;object}. You can model C @cite{struct} types by creating @pxref{12f,,gccjit;;struct_} and @pxref{130,,gccjit;;field} instances, in either order: @itemize * @item by creating the fields, then the structure. For example, to model: @example struct coord @{double x; double y; @}; @end example @noindent you could call: @example gccjit::field field_x = ctxt.new_field (double_type, "x"); gccjit::field field_y = ctxt.new_field (double_type, "y"); std::vector fields; fields.push_back (field_x); fields.push_back (field_y); gccjit::struct_ coord = ctxt.new_struct_type ("coord", fields); @end example @noindent @item by creating the structure, then populating it with fields, typically to allow modelling self-referential structs such as: @example struct node @{ int m_hash; struct node *m_next; @}; @end example @noindent like this: @example gccjit::struct_ node = ctxt.new_opaque_struct_type ("node"); gccjit::type node_ptr = node.get_pointer (); gccjit::field field_hash = ctxt.new_field (int_type, "m_hash"); gccjit::field field_next = ctxt.new_field (node_ptr, "m_next"); std::vector fields; fields.push_back (field_hash); fields.push_back (field_next); node.set_fields (fields); @end example @noindent @end itemize @c FIXME: the above API doesn't seem to exist yet @geindex gccjit;;context;;new_field (C++ function) @anchor{cp/topics/types gccjit context new_field__gccjit type cCP gccjit location}@anchor{131} @deffn {C++ Function} gccjit::field gccjit::context::new_field (gccjit::type type, const char* name, gccjit::location loc) Construct a new field, with the given type and name. @end deffn @geindex gccjit;;context;;new_struct_type (C++ function) @anchor{cp/topics/types gccjit context new_struct_type__ssCR std vector field R gccjit location}@anchor{132} @deffn {C++ Function} gccjit::struct_ gccjit::context::new_struct_type (const std::string& name, std::vector& fields, gccjit::location loc) @quotation Construct a new struct type, with the given name and fields. @end quotation @end deffn @geindex gccjit;;context;;new_opaque_struct (C++ function) @anchor{cp/topics/types gccjit context new_opaque_struct__ssCR gccjit location}@anchor{133} @deffn {C++ Function} gccjit::struct_ gccjit::context::new_opaque_struct (const std::string& name, gccjit::location loc) Construct a new struct type, with the given name, but without specifying the fields. The fields can be omitted (in which case the size of the struct is not known), or later specified using @pxref{80,,gcc_jit_struct_set_fields()}. @end deffn @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Expressions<2>,Creating and using functions<2>,Types<2>,Topic Reference<2> @anchor{cp/topics/expressions expressions}@anchor{134}@anchor{cp/topics/expressions doc}@anchor{135} @subsection Expressions @menu * Rvalues: Rvalues<2>. * Lvalues: Lvalues<2>. * Working with pointers@comma{} structs and unions: Working with pointers structs and unions<2>. Rvalues * Simple expressions: Simple expressions<2>. * Unary Operations: Unary Operations<2>. * Binary Operations: Binary Operations<2>. * Comparisons: Comparisons<2>. * Function calls: Function calls<2>. * Type-coercion: Type-coercion<2>. Lvalues * Global variables: Global variables<2>. @end menu @node Rvalues<2>,Lvalues<2>,,Expressions<2> @anchor{cp/topics/expressions rvalues}@anchor{136} @subsubsection Rvalues @geindex gccjit;;rvalue (C++ class) @anchor{cp/topics/expressions gccjit rvalue}@anchor{137} @deffn {C++ Class} gccjit::rvalue @end deffn A @pxref{137,,gccjit;;rvalue} is an expression that can be computed. It is a subclass of @pxref{121,,gccjit;;object}, and is a thin wrapper around @pxref{13,,gcc_jit_rvalue *} from the C API. It can be simple, e.g.: @quotation @itemize * @item an integer value e.g. @cite{0} or @cite{42} @item a string literal e.g. @cite{"Hello world"} @item a variable e.g. @cite{i}. These are also lvalues (see below). @end itemize @end quotation or compound e.g.: @quotation @itemize * @item a unary expression e.g. @cite{!cond} @item a binary expression e.g. @cite{(a + b)} @item a function call e.g. @cite{get_distance (&player_ship@comma{} &target)} @item etc. @end itemize @end quotation Every rvalue has an associated type, and the API will check to ensure that types match up correctly (otherwise the context will emit an error). @geindex gccjit;;rvalue;;get_type (C++ function) @anchor{cp/topics/expressions gccjit rvalue get_type}@anchor{138} @deffn {C++ Function} gccjit::type gccjit::rvalue::get_type () Get the type of this rvalue. @end deffn @menu * Simple expressions: Simple expressions<2>. * Unary Operations: Unary Operations<2>. * Binary Operations: Binary Operations<2>. * Comparisons: Comparisons<2>. * Function calls: Function calls<2>. * Type-coercion: Type-coercion<2>. @end menu @node Simple expressions<2>,Unary Operations<2>,,Rvalues<2> @anchor{cp/topics/expressions simple-expressions}@anchor{139} @subsubsection Simple expressions @geindex gccjit;;context;;new_rvalue (C++ function) @anchor{cp/topics/expressions gccjit context new_rvalue__gccjit type iC}@anchor{f8} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_rvalue (gccjit::type numeric_type, int value) const Given a numeric type (integer or floating point), build an rvalue for the given constant @code{int} value. @end deffn @geindex gccjit;;context;;new_rvalue (C++ function) @anchor{cp/topics/expressions gccjit context new_rvalue__gccjit type lC}@anchor{13a} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_rvalue (gccjit::type numeric_type, long value) const Given a numeric type (integer or floating point), build an rvalue for the given constant @code{long} value. @end deffn @geindex gccjit;;context;;zero (C++ function) @anchor{cp/topics/expressions gccjit context zero__gccjit typeC}@anchor{f4} @deffn {C++ Function} gccjit::rvalue gccjit::context::zero (gccjit::type numeric_type) const Given a numeric type (integer or floating point), get the rvalue for zero. Essentially this is just a shortcut for: @example ctxt.new_rvalue (numeric_type, 0) @end example @noindent @end deffn @geindex gccjit;;context;;one (C++ function) @anchor{cp/topics/expressions gccjit context one__gccjit typeC}@anchor{13b} @deffn {C++ Function} gccjit::rvalue gccjit::context::one (gccjit::type numeric_type) const Given a numeric type (integer or floating point), get the rvalue for zero. Essentially this is just a shortcut for: @example ctxt.new_rvalue (numeric_type, 1) @end example @noindent @end deffn @geindex gccjit;;context;;new_rvalue (C++ function) @anchor{cp/topics/expressions gccjit context new_rvalue__gccjit type doubleC}@anchor{13c} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_rvalue (gccjit::type numeric_type, double value) const Given a numeric type (integer or floating point), build an rvalue for the given constant @code{double} value. @end deffn @geindex gccjit;;context;;new_rvalue (C++ function) @anchor{cp/topics/expressions gccjit context new_rvalue__gccjit type voidPC}@anchor{13d} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_rvalue (gccjit::type pointer_type, void* value) const Given a pointer type, build an rvalue for the given address. @end deffn @geindex gccjit;;context;;new_rvalue (C++ function) @anchor{cp/topics/expressions gccjit context new_rvalue__ssCRC}@anchor{13e} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_rvalue (const std::string& value) const Generate an rvalue of type @code{GCC_JIT_TYPE_CONST_CHAR_PTR} for the given string. This is akin to a string literal. @end deffn @node Unary Operations<2>,Binary Operations<2>,Simple expressions<2>,Rvalues<2> @anchor{cp/topics/expressions unary-operations}@anchor{13f} @subsubsection Unary Operations @geindex gccjit;;context;;new_unary_op (C++ function) @anchor{cp/topics/expressions gccjit context new_unary_op__enum gccjit type gccjit rvalue gccjit location}@anchor{140} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_unary_op (enum gcc_jit_unary_op, gccjit::type result_type, gccjit::rvalue rvalue, gccjit::location loc) Build a unary operation out of an input rvalue. Parameter @code{loc} is optional. This is a thin wrapper around the C API's @pxref{8c,,gcc_jit_context_new_unary_op()} and the available unary operations are documented there. @end deffn There are shorter ways to spell the various specific kinds of unary operation: @geindex gccjit;;context;;new_minus (C++ function) @anchor{cp/topics/expressions gccjit context new_minus__gccjit type gccjit rvalue gccjit location}@anchor{141} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_minus (gccjit::type result_type, gccjit::rvalue a, gccjit::location loc) Negate an arithmetic value; for example: @example gccjit::rvalue negpi = ctxt.new_minus (t_double, pi); @end example @noindent builds the equivalent of this C expression: @example -pi @end example @noindent @end deffn @geindex new_bitwise_negate (C++ function) @anchor{cp/topics/expressions new_bitwise_negate__gccjit type gccjit rvalue gccjit location}@anchor{142} @deffn {C++ Function} gccjit::rvalue new_bitwise_negate (gccjit::type result_type, gccjit::rvalue a, gccjit::location loc) Bitwise negation of an integer value (one's complement); for example: @example gccjit::rvalue mask = ctxt.new_bitwise_negate (t_int, a); @end example @noindent builds the equivalent of this C expression: @example ~a @end example @noindent @end deffn @geindex new_logical_negate (C++ function) @anchor{cp/topics/expressions new_logical_negate__gccjit type gccjit rvalue gccjit location}@anchor{143} @deffn {C++ Function} gccjit::rvalue new_logical_negate (gccjit::type result_type, gccjit::rvalue a, gccjit::location loc) Logical negation of an arithmetic or pointer value; for example: @example gccjit::rvalue guard = ctxt.new_logical_negate (t_bool, cond); @end example @noindent builds the equivalent of this C expression: @example !cond @end example @noindent @end deffn The most concise way to spell them is with overloaded operators: @geindex operator- (C++ function) @anchor{cp/topics/expressions sub-operator__gccjit rvalue}@anchor{144} @deffn {C++ Function} gccjit::rvalue operator- (gccjit::rvalue a) @example gccjit::rvalue negpi = -pi; @end example @noindent @end deffn @geindex operator~ (C++ function) @anchor{cp/topics/expressions inv-operator__gccjit rvalue}@anchor{145} @deffn {C++ Function} gccjit::rvalue operator~ (gccjit::rvalue a) @example gccjit::rvalue mask = ~a; @end example @noindent @end deffn @geindex operator! (C++ function) @anchor{cp/topics/expressions not-operator__gccjit rvalue}@anchor{146} @deffn {C++ Function} gccjit::rvalue operator! (gccjit::rvalue a) @example gccjit::rvalue guard = !cond; @end example @noindent @end deffn @node Binary Operations<2>,Comparisons<2>,Unary Operations<2>,Rvalues<2> @anchor{cp/topics/expressions binary-operations}@anchor{147} @subsubsection Binary Operations @geindex gccjit;;context;;new_binary_op (C++ function) @anchor{cp/topics/expressions gccjit context new_binary_op__enum gccjit type gccjit rvalue gccjit rvalue gccjit location}@anchor{e8} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_binary_op (enum gcc_jit_binary_op, gccjit::type result_type, gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) Build a binary operation out of two constituent rvalues. Parameter @code{loc} is optional. This is a thin wrapper around the C API's @pxref{12,,gcc_jit_context_new_binary_op()} and the available binary operations are documented there. @end deffn There are shorter ways to spell the various specific kinds of binary operation: @geindex gccjit;;context;;new_plus (C++ function) @anchor{cp/topics/expressions gccjit context new_plus__gccjit type gccjit rvalue gccjit rvalue gccjit location}@anchor{148} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_plus (gccjit::type result_type, gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) @end deffn @geindex gccjit;;context;;new_minus (C++ function) @anchor{cp/topics/expressions gccjit context new_minus__gccjit type gccjit rvalue gccjit rvalue gccjit location}@anchor{149} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_minus (gccjit::type result_type, gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) @end deffn @geindex gccjit;;context;;new_mult (C++ function) @anchor{cp/topics/expressions gccjit context new_mult__gccjit type gccjit rvalue gccjit rvalue gccjit location}@anchor{14a} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_mult (gccjit::type result_type, gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) @end deffn @geindex gccjit;;context;;new_divide (C++ function) @anchor{cp/topics/expressions gccjit context new_divide__gccjit type gccjit rvalue gccjit rvalue gccjit location}@anchor{14b} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_divide (gccjit::type result_type, gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) @end deffn @geindex gccjit;;context;;new_modulo (C++ function) @anchor{cp/topics/expressions gccjit context new_modulo__gccjit type gccjit rvalue gccjit rvalue gccjit location}@anchor{14c} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_modulo (gccjit::type result_type, gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) @end deffn @geindex gccjit;;context;;new_bitwise_and (C++ function) @anchor{cp/topics/expressions gccjit context new_bitwise_and__gccjit type gccjit rvalue gccjit rvalue gccjit location}@anchor{14d} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_bitwise_and (gccjit::type result_type, gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) @end deffn @geindex gccjit;;context;;new_bitwise_xor (C++ function) @anchor{cp/topics/expressions gccjit context new_bitwise_xor__gccjit type gccjit rvalue gccjit rvalue gccjit location}@anchor{14e} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_bitwise_xor (gccjit::type result_type, gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) @end deffn @geindex gccjit;;context;;new_bitwise_or (C++ function) @anchor{cp/topics/expressions gccjit context new_bitwise_or__gccjit type gccjit rvalue gccjit rvalue gccjit location}@anchor{14f} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_bitwise_or (gccjit::type result_type, gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) @end deffn @geindex gccjit;;context;;new_logical_and (C++ function) @anchor{cp/topics/expressions gccjit context new_logical_and__gccjit type gccjit rvalue gccjit rvalue gccjit location}@anchor{150} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_logical_and (gccjit::type result_type, gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) @end deffn @geindex gccjit;;context;;new_logical_or (C++ function) @anchor{cp/topics/expressions gccjit context new_logical_or__gccjit type gccjit rvalue gccjit rvalue gccjit location}@anchor{151} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_logical_or (gccjit::type result_type, gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) @end deffn The most concise way to spell them is with overloaded operators: @geindex operator+ (C++ function) @anchor{cp/topics/expressions add-operator__gccjit rvalue gccjit rvalue}@anchor{152} @deffn {C++ Function} gccjit::rvalue operator+ (gccjit::rvalue a, gccjit::rvalue b) @example gccjit::rvalue sum = a + b; @end example @noindent @end deffn @geindex operator- (C++ function) @anchor{cp/topics/expressions sub-operator__gccjit rvalue gccjit rvalue}@anchor{153} @deffn {C++ Function} gccjit::rvalue operator- (gccjit::rvalue a, gccjit::rvalue b) @example gccjit::rvalue diff = a - b; @end example @noindent @end deffn @geindex operator* (C++ function) @anchor{cp/topics/expressions mul-operator__gccjit rvalue gccjit rvalue}@anchor{154} @deffn {C++ Function} gccjit::rvalue operator* (gccjit::rvalue a, gccjit::rvalue b) @example gccjit::rvalue prod = a * b; @end example @noindent @end deffn @geindex operator/ (C++ function) @anchor{cp/topics/expressions div-operator__gccjit rvalue gccjit rvalue}@anchor{155} @deffn {C++ Function} gccjit::rvalue operator/ (gccjit::rvalue a, gccjit::rvalue b) @example gccjit::rvalue result = a / b; @end example @noindent @end deffn @geindex operator% (C++ function) @anchor{cp/topics/expressions mod-operator__gccjit rvalue gccjit rvalue}@anchor{156} @deffn {C++ Function} gccjit::rvalue operator% (gccjit::rvalue a, gccjit::rvalue b) @example gccjit::rvalue mod = a % b; @end example @noindent @end deffn @geindex operator& (C++ function) @anchor{cp/topics/expressions and-operator__gccjit rvalue gccjit rvalue}@anchor{157} @deffn {C++ Function} gccjit::rvalue operator& (gccjit::rvalue a, gccjit::rvalue b) @example gccjit::rvalue x = a & b; @end example @noindent @end deffn @geindex operator^ (C++ function) @anchor{cp/topics/expressions xor-operator__gccjit rvalue gccjit rvalue}@anchor{158} @deffn {C++ Function} gccjit::rvalue operator^ (gccjit::rvalue a, gccjit::rvalue b) @example gccjit::rvalue x = a ^ b; @end example @noindent @end deffn @geindex operator| (C++ function) @anchor{cp/topics/expressions or-operator__gccjit rvalue gccjit rvalue}@anchor{159} @deffn {C++ Function} gccjit::rvalue operator| (gccjit::rvalue a, gccjit::rvalue b) @example gccjit::rvalue x = a | b; @end example @noindent @end deffn @geindex operator&& (C++ function) @anchor{cp/topics/expressions sand-operator__gccjit rvalue gccjit rvalue}@anchor{15a} @deffn {C++ Function} gccjit::rvalue operator&& (gccjit::rvalue a, gccjit::rvalue b) @example gccjit::rvalue cond = a && b; @end example @noindent @end deffn @geindex operator|| (C++ function) @anchor{cp/topics/expressions sor-operator__gccjit rvalue gccjit rvalue}@anchor{15b} @deffn {C++ Function} gccjit::rvalue operator|| (gccjit::rvalue a, gccjit::rvalue b) @example gccjit::rvalue cond = a || b; @end example @noindent @end deffn These can of course be combined, giving a terse way to build compound expressions: @quotation @example gccjit::rvalue discriminant = (b * b) - (four * a * c); @end example @noindent @end quotation @node Comparisons<2>,Function calls<2>,Binary Operations<2>,Rvalues<2> @anchor{cp/topics/expressions comparisons}@anchor{15c} @subsubsection Comparisons @geindex gccjit;;context;;new_comparison (C++ function) @anchor{cp/topics/expressions gccjit context new_comparison__enum gccjit rvalue gccjit rvalue gccjit location}@anchor{f5} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_comparison (enum gcc_jit_comparison, gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) Build a boolean rvalue out of the comparison of two other rvalues. Parameter @code{loc} is optional. This is a thin wrapper around the C API's @pxref{2c,,gcc_jit_context_new_comparison()} and the available kinds of comparison are documented there. @end deffn There are shorter ways to spell the various specific kinds of binary operation: @geindex gccjit;;context;;new_eq (C++ function) @anchor{cp/topics/expressions gccjit context new_eq__gccjit rvalue gccjit rvalue gccjit location}@anchor{15d} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_eq (gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) @end deffn @geindex gccjit;;context;;new_ne (C++ function) @anchor{cp/topics/expressions gccjit context new_ne__gccjit rvalue gccjit rvalue gccjit location}@anchor{15e} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_ne (gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) @end deffn @geindex gccjit;;context;;new_lt (C++ function) @anchor{cp/topics/expressions gccjit context new_lt__gccjit rvalue gccjit rvalue gccjit location}@anchor{15f} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_lt (gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) @end deffn @geindex gccjit;;context;;new_le (C++ function) @anchor{cp/topics/expressions gccjit context new_le__gccjit rvalue gccjit rvalue gccjit location}@anchor{160} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_le (gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) @end deffn @geindex gccjit;;context;;new_gt (C++ function) @anchor{cp/topics/expressions gccjit context new_gt__gccjit rvalue gccjit rvalue gccjit location}@anchor{161} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_gt (gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) @end deffn @geindex gccjit;;context;;new_ge (C++ function) @anchor{cp/topics/expressions gccjit context new_ge__gccjit rvalue gccjit rvalue gccjit location}@anchor{162} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_ge (gccjit::rvalue a, gccjit::rvalue b, gccjit::location loc) @end deffn The most concise way to spell them is with overloaded operators: @geindex operator== (C++ function) @anchor{cp/topics/expressions eq-operator__gccjit rvalue gccjit rvalue}@anchor{163} @deffn {C++ Function} gccjit::rvalue operator== (gccjit::rvalue a, gccjit::rvalue b) @example gccjit::rvalue cond = (a == ctxt.zero (t_int)); @end example @noindent @end deffn @geindex operator!= (C++ function) @anchor{cp/topics/expressions neq-operator__gccjit rvalue gccjit rvalue}@anchor{164} @deffn {C++ Function} gccjit::rvalue operator!= (gccjit::rvalue a, gccjit::rvalue b) @example gccjit::rvalue cond = (i != j); @end example @noindent @end deffn @geindex operator< (C++ function) @anchor{cp/topics/expressions lt-operator__gccjit rvalue gccjit rvalue}@anchor{165} @deffn {C++ Function} gccjit::rvalue operator< (gccjit::rvalue a, gccjit::rvalue b) @example gccjit::rvalue cond = i < n; @end example @noindent @end deffn @geindex operator<= (C++ function) @anchor{cp/topics/expressions lte-operator__gccjit rvalue gccjit rvalue}@anchor{166} @deffn {C++ Function} gccjit::rvalue operator<= (gccjit::rvalue a, gccjit::rvalue b) @example gccjit::rvalue cond = i <= n; @end example @noindent @end deffn @geindex operator> (C++ function) @anchor{cp/topics/expressions gt-operator__gccjit rvalue gccjit rvalue}@anchor{167} @deffn {C++ Function} gccjit::rvalue operator> (gccjit::rvalue a, gccjit::rvalue b) @example gccjit::rvalue cond = (ch > limit); @end example @noindent @end deffn @geindex operator>= (C++ function) @anchor{cp/topics/expressions gte-operator__gccjit rvalue gccjit rvalue}@anchor{168} @deffn {C++ Function} gccjit::rvalue operator>= (gccjit::rvalue a, gccjit::rvalue b) @example gccjit::rvalue cond = (score >= ctxt.new_rvalue (t_int, 100)); @end example @noindent @end deffn @c TODO: beyond this point @node Function calls<2>,Type-coercion<2>,Comparisons<2>,Rvalues<2> @anchor{cp/topics/expressions function-calls}@anchor{169} @subsubsection Function calls @geindex gcc_jit_context_new_call (C++ function) @anchor{cp/topics/expressions gcc_jit_context_new_call__gcc_jit_contextP gcc_jit_locationP gcc_jit_functionP i gcc_jit_rvaluePP}@anchor{16a} @deffn {C++ Function} gcc_jit_rvalue* gcc_jit_context_new_call (gcc_jit_context* ctxt, gcc_jit_location* loc, gcc_jit_function* func, int numargs, gcc_jit_rvalue** args) Given a function and the given table of argument rvalues, construct a call to the function, with the result as an rvalue. @cartouche @quotation Note @code{gccjit::context::new_call()} merely builds a @pxref{137,,gccjit;;rvalue} i.e. an expression that can be evaluated, perhaps as part of a more complicated expression. The call @emph{won't} happen unless you add a statement to a function that evaluates the expression. For example, if you want to call a function and discard the result (or to call a function with @code{void} return type), use @pxref{16b,,gccjit;;block;;add_eval()}: @example /* Add "(void)printf (arg0, arg1);". */ block.add_eval (ctxt.new_call (printf_func, arg0, arg1)); @end example @noindent @end quotation @end cartouche @end deffn @node Type-coercion<2>,,Function calls<2>,Rvalues<2> @anchor{cp/topics/expressions type-coercion}@anchor{16c} @subsubsection Type-coercion @geindex gccjit;;context;;new_cast (C++ function) @anchor{cp/topics/expressions gccjit context new_cast__gccjit rvalue gccjit type gccjit location}@anchor{16d} @deffn {C++ Function} gccjit::rvalue gccjit::context::new_cast (gccjit::rvalue rvalue, gccjit::type type, gccjit::location loc) Given an rvalue of T, construct another rvalue of another type. Currently only a limited set of conversions are possible: @quotation @itemize * @item int <-> float @item int <-> bool @item P* <-> Q*, for pointer types P and Q @end itemize @end quotation @end deffn @node Lvalues<2>,Working with pointers structs and unions<2>,Rvalues<2>,Expressions<2> @anchor{cp/topics/expressions lvalues}@anchor{16e} @subsubsection Lvalues @geindex gccjit;;lvalue (C++ class) @anchor{cp/topics/expressions gccjit lvalue}@anchor{16f} @deffn {C++ Class} gccjit::lvalue @end deffn An lvalue is something that can of the @emph{left}-hand side of an assignment: a storage area (such as a variable). It is a subclass of @pxref{137,,gccjit;;rvalue}, where the rvalue is computed by reading from the storage area. It iss a thin wrapper around @pxref{24,,gcc_jit_lvalue *} from the C API. @geindex gccjit;;lvalue;;get_address (C++ function) @anchor{cp/topics/expressions gccjit lvalue get_address__gccjit location}@anchor{170} @deffn {C++ Function} gccjit::rvalue gccjit::lvalue::get_address (gccjit::location loc) Take the address of an lvalue; analogous to: @example &(EXPR) @end example @noindent in C. Parameter "loc" is optional. @end deffn @menu * Global variables: Global variables<2>. @end menu @node Global variables<2>,,,Lvalues<2> @anchor{cp/topics/expressions global-variables}@anchor{171} @subsubsection Global variables @geindex gccjit;;context;;new_global (C++ function) @anchor{cp/topics/expressions gccjit context new_global__enum gccjit type cCP gccjit location}@anchor{172} @deffn {C++ Function} gccjit::lvalue gccjit::context::new_global (enum gcc_jit_global_kind, gccjit::type type, const char* name, gccjit::location loc) Add a new global variable of the given type and name to the context. This is a thin wrapper around @pxref{ad,,gcc_jit_context_new_global()} from the C API; the "kind" parameter has the same meaning as there. @end deffn @node Working with pointers structs and unions<2>,,Lvalues<2>,Expressions<2> @anchor{cp/topics/expressions working-with-pointers-structs-and-unions}@anchor{173} @subsubsection Working with pointers, structs and unions @geindex gccjit;;rvalue;;dereference (C++ function) @anchor{cp/topics/expressions gccjit rvalue dereference__gccjit location}@anchor{174} @deffn {C++ Function} gccjit::lvalue gccjit::rvalue::dereference (gccjit::location loc) Given an rvalue of pointer type @code{T *}, dereferencing the pointer, getting an lvalue of type @code{T}. Analogous to: @example *(EXPR) @end example @noindent in C. Parameter "loc" is optional. @end deffn If you don't need to specify the location, this can also be expressed using an overloaded operator: @deffn {C++ Function} gccjit::lvalue gccjit::rvalue::operator* (); @example gccjit::lvalue content = *ptr; @end example @noindent @end deffn Field access is provided separately for both lvalues and rvalues: @geindex gccjit;;lvalue;;access_field (C++ function) @anchor{cp/topics/expressions gccjit lvalue access_field__gccjit field gccjit location}@anchor{175} @deffn {C++ Function} gccjit::lvalue gccjit::lvalue::access_field (gccjit::field field, gccjit::location loc) Given an lvalue of struct or union type, access the given field, getting an lvalue of the field's type. Analogous to: @example (EXPR).field = ...; @end example @noindent in C. @end deffn @geindex gccjit;;rvalue;;access_field (C++ function) @anchor{cp/topics/expressions gccjit rvalue access_field__gccjit field gccjit location}@anchor{176} @deffn {C++ Function} gccjit::rvalue gccjit::rvalue::access_field (gccjit::field field, gccjit::location loc) Given an rvalue of struct or union type, access the given field as an rvalue. Analogous to: @example (EXPR).field @end example @noindent in C. @end deffn @geindex gccjit;;rvalue;;dereference_field (C++ function) @anchor{cp/topics/expressions gccjit rvalue dereference_field__gccjit field gccjit location}@anchor{177} @deffn {C++ Function} gccjit::lvalue gccjit::rvalue::dereference_field (gccjit::field field, gccjit::location loc) Given an rvalue of pointer type @code{T *} where T is of struct or union type, access the given field as an lvalue. Analogous to: @example (EXPR)->field @end example @noindent in C, itself equivalent to @code{(*EXPR).FIELD}. @end deffn @geindex gccjit;;context;;new_array_access (C++ function) @anchor{cp/topics/expressions gccjit context new_array_access__gccjit rvalue gccjit rvalue gccjit location}@anchor{178} @deffn {C++ Function} gccjit::lvalue gccjit::context::new_array_access (gccjit::rvalue ptr, gccjit::rvalue index, gccjit::location loc) Given an rvalue of pointer type @code{T *}, get at the element @cite{T} at the given index, using standard C array indexing rules i.e. each increment of @code{index} corresponds to @code{sizeof(T)} bytes. Analogous to: @example PTR[INDEX] @end example @noindent in C (or, indeed, to @code{PTR + INDEX}). Parameter "loc" is optional. @end deffn For array accesses where you don't need to specify a @pxref{118,,gccjit;;location}, two overloaded operators are available: @quotation gccjit::lvalue gccjit::rvalue::operator[] (gccjit::rvalue index) @example gccjit::lvalue element = array[idx]; @end example @noindent gccjit::lvalue gccjit::rvalue::operator[] (int index) @example gccjit::lvalue element = array[0]; @end example @noindent @end quotation @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Creating and using functions<2>,Source Locations<2>,Expressions<2>,Topic Reference<2> @anchor{cp/topics/functions doc}@anchor{179}@anchor{cp/topics/functions creating-and-using-functions}@anchor{17a} @subsection Creating and using functions @menu * Params: Params<2>. * Functions: Functions<2>. * Blocks: Blocks<2>. * Statements: Statements<2>. @end menu @node Params<2>,Functions<2>,,Creating and using functions<2> @anchor{cp/topics/functions params}@anchor{17b} @subsubsection Params @geindex gccjit;;param (C++ class) @anchor{cp/topics/functions gccjit param}@anchor{17c} @deffn {C++ Class} gccjit::param A @cite{gccjit::param} represents a parameter to a function. @end deffn @geindex gccjit;;context;;new_param (C++ function) @anchor{cp/topics/functions gccjit context new_param__gccjit type cCP gccjit location}@anchor{e7} @deffn {C++ Function} gccjit::param gccjit::context::new_param (gccjit::type type, const char* name, gccjit::location loc) In preparation for creating a function, create a new parameter of the given type and name. @end deffn @pxref{17c,,gccjit;;param} is a subclass of @pxref{16f,,gccjit;;lvalue} (and thus of @pxref{137,,gccjit;;rvalue} and @pxref{121,,gccjit;;object}). It is a thin wrapper around the C API's @pxref{25,,gcc_jit_param *}. @node Functions<2>,Blocks<2>,Params<2>,Creating and using functions<2> @anchor{cp/topics/functions functions}@anchor{17d} @subsubsection Functions @geindex gccjit;;function (C++ class) @anchor{cp/topics/functions gccjit function}@anchor{17e} @deffn {C++ Class} gccjit::function A @cite{gccjit::function} represents a function - either one that we're creating ourselves, or one that we're referencing. @end deffn @deffn {C++ Function} gccjit::function gccjit::context::new_function (enum gcc_jit_function_kind, gccjit::type return_type, const char *name, std::vector ¶ms, int is_variadic, gccjit::location loc) \ Create a gcc_jit_function with the given name and parameters. Parameters "is_variadic" and "loc" are optional. This is a wrapper around the C API's @pxref{11,,gcc_jit_context_new_function()}. @end deffn @geindex gccjit;;context;;get_builtin_function (C++ function) @anchor{cp/topics/functions gccjit context get_builtin_function__cCP}@anchor{17f} @deffn {C++ Function} gccjit::function gccjit::context::get_builtin_function (const char* name) This is a wrapper around the C API's @pxref{c4,,gcc_jit_context_get_builtin_function()}. @end deffn @geindex gccjit;;function;;get_param (C++ function) @anchor{cp/topics/functions gccjit function get_param__iC}@anchor{180} @deffn {C++ Function} gccjit::param gccjit::function::get_param (int index) const Get the param of the given index (0-based). @end deffn @geindex gccjit;;function;;dump_to_dot (C++ function) @anchor{cp/topics/functions gccjit function dump_to_dot__cCP}@anchor{fa} @deffn {C++ Function} void gccjit::function::dump_to_dot (const char* path) Emit the function in graphviz format to the given path. @end deffn @geindex gccjit;;function;;new_local (C++ function) @anchor{cp/topics/functions gccjit function new_local__gccjit type cCP gccjit location}@anchor{f1} @deffn {C++ Function} gccjit::lvalue gccjit::function::new_local (gccjit::type type, const char* name, gccjit::location loc) Create a new local variable within the function, of the given type and name. @end deffn @node Blocks<2>,Statements<2>,Functions<2>,Creating and using functions<2> @anchor{cp/topics/functions blocks}@anchor{181} @subsubsection Blocks @geindex gccjit;;block (C++ class) @anchor{cp/topics/functions gccjit block}@anchor{182} @deffn {C++ Class} gccjit::block A @cite{gccjit::block} represents a basic block within a function i.e. a sequence of statements with a single entry point and a single exit point. @pxref{182,,gccjit;;block} is a subclass of @pxref{121,,gccjit;;object}. The first basic block that you create within a function will be the entrypoint. Each basic block that you create within a function must be terminated, either with a conditional, a jump, or a return. It's legal to have multiple basic blocks that return within one function. @end deffn @geindex gccjit;;function;;new_block (C++ function) @anchor{cp/topics/functions gccjit function new_block__cCP}@anchor{183} @deffn {C++ Function} gccjit::block gccjit::function::new_block (const char* name) Create a basic block of the given name. The name may be NULL, but providing meaningful names is often helpful when debugging: it may show up in dumps of the internal representation, and in error messages. @end deffn @node Statements<2>,,Blocks<2>,Creating and using functions<2> @anchor{cp/topics/functions statements}@anchor{184} @subsubsection Statements @geindex gccjit;;block;;add_eval (C++ function) @anchor{cp/topics/functions gccjit block add_eval__gccjit rvalue gccjit location}@anchor{16b} @deffn {C++ Function} void gccjit::block::add_eval (gccjit::rvalue rvalue, gccjit::location loc) Add evaluation of an rvalue, discarding the result (e.g. a function call that "returns" void). This is equivalent to this C code: @example (void)expression; @end example @noindent @end deffn @geindex gccjit;;block;;add_assignment (C++ function) @anchor{cp/topics/functions gccjit block add_assignment__gccjit lvalue gccjit rvalue gccjit location}@anchor{f3} @deffn {C++ Function} void gccjit::block::add_assignment (gccjit::lvalue lvalue, gccjit::rvalue rvalue, gccjit::location loc) Add evaluation of an rvalue, assigning the result to the given lvalue. This is roughly equivalent to this C code: @example lvalue = rvalue; @end example @noindent @end deffn @geindex gccjit;;block;;add_assignment_op (C++ function) @anchor{cp/topics/functions gccjit block add_assignment_op__gccjit lvalue enum gccjit rvalue gccjit location}@anchor{f7} @deffn {C++ Function} void gccjit::block::add_assignment_op (gccjit::lvalue lvalue, enum gcc_jit_binary_op, gccjit::rvalue rvalue, gccjit::location loc) Add evaluation of an rvalue, using the result to modify an lvalue. This is analogous to "+=" and friends: @example lvalue += rvalue; lvalue *= rvalue; lvalue /= rvalue; @end example @noindent etc. For example: @example /* "i++" */ loop_body.add_assignment_op ( i, GCC_JIT_BINARY_OP_PLUS, ctxt.one (int_type)); @end example @noindent @end deffn @geindex gccjit;;block;;add_comment (C++ function) @anchor{cp/topics/functions gccjit block add_comment__cCP gccjit location}@anchor{102} @deffn {C++ Function} void gccjit::block::add_comment (const char* text, gccjit::location loc) Add a no-op textual comment to the internal representation of the code. It will be optimized away, but will be visible in the dumps seen via @pxref{66,,GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE} and @pxref{1c,,GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE}, and thus may be of use when debugging how your project's internal representation gets converted to the libgccjit IR. Parameter "loc" is optional. @end deffn @geindex gccjit;;block;;end_with_conditional (C++ function) @anchor{cp/topics/functions gccjit block end_with_conditional__gccjit rvalue gccjit block gccjit block gccjit location}@anchor{f6} @deffn {C++ Function} void gccjit::block::end_with_conditional (gccjit::rvalue boolval, gccjit::block on_true, gccjit::block on_false, gccjit::location loc) Terminate a block by adding evaluation of an rvalue, branching on the result to the appropriate successor block. This is roughly equivalent to this C code: @example if (boolval) goto on_true; else goto on_false; @end example @noindent block, boolval, on_true, and on_false must be non-NULL. @end deffn @geindex gccjit;;block;;end_with_jump (C++ function) @anchor{cp/topics/functions gccjit block end_with_jump__gccjit block gccjit location}@anchor{185} @deffn {C++ Function} void gccjit::block::end_with_jump (gccjit::block target, gccjit::location loc) Terminate a block by adding a jump to the given target block. This is roughly equivalent to this C code: @example goto target; @end example @noindent @end deffn @geindex gccjit;;block;;end_with_return (C++ function) @anchor{cp/topics/functions gccjit block end_with_return__gccjit rvalue gccjit location}@anchor{186} @deffn {C++ Function} void gccjit::block::end_with_return (gccjit::rvalue rvalue, gccjit::location loc) Terminate a block. Both params are optional. An rvalue must be provided for a function returning non-void, and must not be provided by a function "returning" @cite{void}. If an rvalue is provided, the block is terminated by evaluating the rvalue and returning the value. This is roughly equivalent to this C code: @example return expression; @end example @noindent If an rvalue is not provided, the block is terminated by adding a valueless return, for use within a function with "void" return type. This is equivalent to this C code: @example return; @end example @noindent @end deffn @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Source Locations<2>,Compiling a context<2>,Creating and using functions<2>,Topic Reference<2> @anchor{cp/topics/locations source-locations}@anchor{187}@anchor{cp/topics/locations doc}@anchor{188} @subsection Source Locations @geindex gccjit;;location (C++ class) @anchor{cp/topics/locations gccjit location}@anchor{118} @deffn {C++ Class} gccjit::location A @cite{gccjit::location} encapsulates a source code location, so that you can (optionally) associate locations in your language with statements in the JIT-compiled code, allowing the debugger to single-step through your language. @cite{gccjit::location} instances are optional: you can always omit them from any C++ API entrypoint accepting one. You can construct them using @pxref{106,,gccjit;;context;;new_location()}. You need to enable @pxref{42,,GCC_JIT_BOOL_OPTION_DEBUGINFO} on the @pxref{110,,gccjit;;context} for these locations to actually be usable by the debugger: @example ctxt.set_bool_option (GCC_JIT_BOOL_OPTION_DEBUGINFO, 1); @end example @noindent @end deffn @geindex gccjit;;context;;new_location (C++ function) @anchor{cp/topics/locations gccjit context new_location__cCP i i}@anchor{106} @deffn {C++ Function} gccjit::location gccjit::context::new_location (const char* filename, int line, int column) Create a @cite{gccjit::location} instance representing the given source location. @end deffn @menu * Faking it: Faking it<2>. @end menu @node Faking it<2>,,,Source Locations<2> @anchor{cp/topics/locations faking-it}@anchor{189} @subsubsection Faking it If you don't have source code for your internal representation, but need to debug, you can generate a C-like representation of the functions in your context using @pxref{117,,gccjit;;context;;dump_to_file()}: @example ctxt.dump_to_file ("/tmp/something.c", 1 /* update_locations */); @end example @noindent This will dump C-like code to the given path. If the @cite{update_locations} argument is true, this will also set up @cite{gccjit::location} information throughout the context, pointing at the dump file as if it were a source file, giving you @emph{something} you can step through in the debugger. @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Compiling a context<2>,,Source Locations<2>,Topic Reference<2> @anchor{cp/topics/compilation compiling-a-context}@anchor{18a}@anchor{cp/topics/compilation doc}@anchor{18b} @subsection Compiling a context Once populated, a @pxref{110,,gccjit;;context} can be compiled to machine code, either in-memory via @pxref{e9,,gccjit;;context;;compile()} or to disk via @pxref{18c,,gccjit;;context;;compile_to_file()}. You can compile a context multiple times (using either form of compilation), although any errors that occur on the context will prevent any future compilation of that context. @menu * In-memory compilation: In-memory compilation<2>. * Ahead-of-time compilation: Ahead-of-time compilation<2>. @end menu @node In-memory compilation<2>,Ahead-of-time compilation<2>,,Compiling a context<2> @anchor{cp/topics/compilation in-memory-compilation}@anchor{18d} @subsubsection In-memory compilation @geindex gccjit;;context;;compile (C++ function) @anchor{cp/topics/compilation gccjit context compile}@anchor{e9} @deffn {C++ Function} gcc_jit_result* gccjit::context::compile () This calls into GCC and builds the code, returning a @cite{gcc_jit_result *}. This is a thin wrapper around the @pxref{15,,gcc_jit_context_compile()} API entrypoint. @end deffn @node Ahead-of-time compilation<2>,,In-memory compilation<2>,Compiling a context<2> @anchor{cp/topics/compilation ahead-of-time-compilation}@anchor{18e} @subsubsection Ahead-of-time compilation Although libgccjit is primarily aimed at just-in-time compilation, it can also be used for implementing more traditional ahead-of-time compilers, via the @pxref{18c,,gccjit;;context;;compile_to_file()} method. @geindex gccjit;;context;;compile_to_file (C++ function) @anchor{cp/topics/compilation gccjit context compile_to_file__enum cCP}@anchor{18c} @deffn {C++ Function} void gccjit::context::compile_to_file (enum gcc_jit_output_kind, const char* output_path) Compile the @pxref{110,,gccjit;;context} to a file of the given kind. This is a thin wrapper around the @pxref{4a,,gcc_jit_context_compile_to_file()} API entrypoint. @end deffn @c Copyright (C) 2014-2015 Free Software Foundation, Inc. @c Originally contributed by David Malcolm @c @c This is free software: you can redistribute it and/or modify it @c under the terms of the GNU General Public License as published by @c the Free Software Foundation, either version 3 of the License, or @c (at your option) any later version. @c @c This program is distributed in the hope that it will be useful, but @c WITHOUT ANY WARRANTY; without even the implied warranty of @c MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU @c General Public License for more details. @c @c You should have received a copy of the GNU General Public License @c along with this program. If not, see @c . @node Internals,Indices and tables,C++ bindings for libgccjit,Top @anchor{internals/index internals}@anchor{18f}@anchor{internals/index doc}@anchor{190} @chapter Internals @menu * Working on the JIT library:: * Running the test suite:: * Environment variables:: * Packaging notes:: * Overview of code structure:: * Design notes:: @end menu @node Working on the JIT library,Running the test suite,,Internals @anchor{internals/index working-on-the-jit-library}@anchor{191} @section Working on the JIT library Having checked out the source code (to "src"), you can configure and build the JIT library like this: @example mkdir build mkdir install PREFIX=$(pwd)/install cd build ../src/configure \ --enable-host-shared \ --enable-languages=jit,c++ \ --disable-bootstrap \ --enable-checking=release \ --prefix=$PREFIX nice make -j4 # altering the "4" to however many cores you have @end example @noindent This should build a libgccjit.so within jit/build/gcc: @example [build] $ file gcc/libgccjit.so* gcc/libgccjit.so: symbolic link to `libgccjit.so.0' gcc/libgccjit.so.0: symbolic link to `libgccjit.so.0.0.1' gcc/libgccjit.so.0.0.1: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, not stripped @end example @noindent Here's what those configuration options mean: @geindex command line option; --enable-host-shared @anchor{internals/index cmdoption--enable-host-shared}@anchor{192} @deffn {Option} --enable-host-shared Configuring with this option means that the compiler is built as position-independent code, which incurs a slight performance hit, but it necessary for a shared library. @end deffn @geindex command line option; --enable-languages=jit@comma{}c++ @anchor{internals/index cmdoption--enable-languages}@anchor{193} @deffn {Option} --enable-languages=jit,c++ This specifies which frontends to build. The JIT library looks like a frontend to the rest of the code. The C++ portion of the JIT test suite requires the C++ frontend to be enabled at configure-time, or you may see errors like this when running the test suite: @example xgcc: error: /home/david/jit/src/gcc/testsuite/jit.dg/test-quadratic.cc: C++ compiler not installed on this system c++: error trying to exec 'cc1plus': execvp: No such file or directory @end example @noindent @end deffn @geindex command line option; --disable-bootstrap @anchor{internals/index cmdoption--disable-bootstrap}@anchor{194} @deffn {Option} --disable-bootstrap For hacking on the "jit" subdirectory, performing a full bootstrap can be overkill, since it's unused by a bootstrap. However, when submitting patches, you should remove this option, to ensure that the compiler can still bootstrap itself. @end deffn @geindex command line option; --enable-checking=release @anchor{internals/index cmdoption--enable-checking}@anchor{195} @deffn {Option} --enable-checking=release The compile can perform extensive self-checking as it runs, useful when debugging, but slowing things down. For maximum speed, configure with @code{--enable-checking=release} to disable this self-checking. @end deffn @node Running the test suite,Environment variables,Working on the JIT library,Internals @anchor{internals/index running-the-test-suite}@anchor{196} @section Running the test suite @example [build] $ cd gcc [gcc] $ make check-jit RUNTESTFLAGS="-v -v -v" @end example @noindent A summary of the tests can then be seen in: @example jit/build/gcc/testsuite/jit/jit.sum @end example @noindent and detailed logs in: @example jit/build/gcc/testsuite/jit/jit.log @end example @noindent The test executables can be seen as: @example jit/build/gcc/testsuite/jit/*.exe @end example @noindent which can be run independently. You can compile and run individual tests by passing "jit.exp=TESTNAME" to RUNTESTFLAGS e.g.: @example [gcc] $ make check-jit RUNTESTFLAGS="-v -v -v jit.exp=test-factorial.c" @end example @noindent and once a test has been compiled, you can debug it directly: @example [gcc] $ PATH=.:$PATH \ LD_LIBRARY_PATH=. \ LIBRARY_PATH=. \ gdb --args \ testsuite/jit/test-factorial.c.exe @end example @noindent @menu * Running under valgrind:: @end menu @node Running under valgrind,,,Running the test suite @anchor{internals/index running-under-valgrind}@anchor{197} @subsection Running under valgrind The jit testsuite detects if RUN_UNDER_VALGRIND is present in the environment (with any value). If it is present, it runs the test client code under valgrind@footnote{http://valgrind.org}, specifcally, the default memcheck@footnote{http://valgrind.org/docs/manual/mc-manual.html} tool with --leak-check=full@footnote{http://valgrind.org/docs/manual/mc-manual.html#opt.leak-check}. It automatically parses the output from valgrind, injecting XFAIL results if any issues are found, or PASS results if the output is clean. The output is saved to @code{TESTNAME.exe.valgrind.txt}. For example, the following invocation verbosely runs the testcase @code{test-sum-of-squares.c} under valgrind, showing an issue: @example $ RUN_UNDER_VALGRIND= \ make check-jit \ RUNTESTFLAGS="-v -v -v jit.exp=test-sum-of-squares.c" (...verbose log contains detailed valgrind errors, if any...) === jit Summary === # of expected passes 28 # of expected failures 2 $ less testsuite/jit/jit.sum (...other results...) XFAIL: jit.dg/test-sum-of-squares.c: test-sum-of-squares.c.exe.valgrind.txt: definitely lost: 8 bytes in 1 blocks XFAIL: jit.dg/test-sum-of-squares.c: test-sum-of-squares.c.exe.valgrind.txt: unsuppressed errors: 1 (...other results...) $ less testsuite/jit/test-sum-of-squares.c.exe.valgrind.txt (...shows full valgrind report for this test case...) @end example @noindent When running under valgrind, it's best to have configured gcc with @code{--enable-valgrind-annotations}, which automatically suppresses various known false positives. @node Environment variables,Packaging notes,Running the test suite,Internals @anchor{internals/index environment-variables}@anchor{198} @section Environment variables When running client code against a locally-built libgccjit, three environment variables need to be set up: @geindex environment variable; LD_LIBRARY_PATH @anchor{internals/index envvar-LD_LIBRARY_PATH}@anchor{199} @deffn {Environment Variable} LD_LIBRARY_PATH @quotation @cite{libgccjit.so} is dynamically linked into client code, so if running against a locally-built library, @code{LD_LIBRARY_PATH} needs to be set up appropriately. The library can be found within the "gcc" subdirectory of the build tree: @end quotation @example $ file libgccjit.so* libgccjit.so: symbolic link to `libgccjit.so.0' libgccjit.so.0: symbolic link to `libgccjit.so.0.0.1' libgccjit.so.0.0.1: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, not stripped @end example @noindent @end deffn @geindex environment variable; PATH @anchor{internals/index envvar-PATH}@anchor{19a} @deffn {Environment Variable} PATH The library uses a driver executable for converting from .s assembler files to .so shared libraries. Specifically, it looks for a name expanded from @code{$@{target_noncanonical@}-gcc-$@{gcc_BASEVER@}$@{exeext@}} such as @code{x86_64-unknown-linux-gnu-gcc-5.0.0}. Hence @code{PATH} needs to include a directory where the library can locate this executable. The executable is normally installed to the installation bindir (e.g. /usr/bin), but a copy is also created within the "gcc" subdirectory of the build tree for running the testsuite, and for ease of development. @end deffn @geindex environment variable; LIBRARY_PATH @anchor{internals/index envvar-LIBRARY_PATH}@anchor{19b} @deffn {Environment Variable} LIBRARY_PATH The driver executable invokes the linker, and the latter needs to locate support libraries needed by the generated code, or you will see errors like: @example ld: cannot find crtbeginS.o: No such file or directory ld: cannot find -lgcc ld: cannot find -lgcc_s @end example @noindent Hence if running directly from a locally-built copy (without installing), @code{LIBRARY_PATH} needs to contain the "gcc" subdirectory of the build tree. @end deffn For example, to run a binary that uses the library against a non-installed build of the library in LIBGCCJIT_BUILD_DIR you need an invocation of the client code like this, to preprend the dir to each of the environment variables: @example $ LD_LIBRARY_PATH=$(LIBGCCJIT_BUILD_DIR):$(LD_LIBRARY_PATH) \ PATH=$(LIBGCCJIT_BUILD_DIR):$(PATH) \ LIBRARY_PATH=$(LIBGCCJIT_BUILD_DIR):$(LIBRARY_PATH) \ ./jit-hello-world hello world @end example @noindent @node Packaging notes,Overview of code structure,Environment variables,Internals @anchor{internals/index packaging-notes}@anchor{19c} @section Packaging notes The configure-time option @pxref{192,,--enable-host-shared} is needed when building the jit in order to get position-independent code. This will slow down the regular compiler by a few percent. Hence when packaging gcc with libgccjit, please configure and build twice: @quotation @itemize * @item once without @pxref{192,,--enable-host-shared} for most languages, and @item once with @pxref{192,,--enable-host-shared} for the jit @end itemize @end quotation For example: @example # Configure and build with --enable-host-shared # for the jit: mkdir configuration-for-jit pushd configuration-for-jit $(SRCDIR)/configure \ --enable-host-shared \ --enable-languages=jit \ --prefix=$(DESTDIR) make popd # Configure and build *without* --enable-host-shared # for maximum speed: mkdir standard-configuration pushd standard-configuration $(SRCDIR)/configure \ --enable-languages=all \ --prefix=$(DESTDIR) make popd # Both of the above are configured to install to $(DESTDIR) # Install the configuration with --enable-host-shared first # *then* the one without, so that the faster build # of "cc1" et al overwrites the slower build. pushd configuration-for-jit make install popd pushd standard-configuration make install popd @end example @noindent @node Overview of code structure,Design notes,Packaging notes,Internals @anchor{internals/index overview-of-code-structure}@anchor{19d} @section Overview of code structure @itemize * @item @code{libgccjit.c} implements the API entrypoints. It performs error checking, then calls into classes of the gcc::jit::recording namespace within @code{jit-recording.c} and @code{jit-recording.h}. @item The gcc::jit::recording classes (within @code{jit-recording.c} and @code{jit-recording.h}) record the API calls that are made: @quotation @example /* Indentation indicates inheritance: */ class context; class memento; class string; class location; class type; class function_type; class compound_type; class struct_; class union_; class field; class fields; class function; class block; class rvalue; class lvalue; class local; class global; class param; class statement; @end example @noindent @end quotation @item When the context is compiled, the gcc::jit::playback classes (within @code{jit-playback.c} and @code{jit-playback.h}) replay the API calls within langhook:parse_file: @quotation @example /* Indentation indicates inheritance: */ class context; class wrapper; class type; class compound_type; class field; class function; class block; class rvalue; class lvalue; class param; class source_file; class source_line; class location; @end example @noindent @example Client Code . Generated . libgccjit.so . code . . . JIT API . JIT "Frontend". (libbackend.a) .................................................................................... │ . . . . ──────────────────────────> . . . . │ . . . . V . . . . ──> libgccjit.c . . . │ (error-checking). . . │ . . . ──> jit-recording.c . . (record API calls) . . <─────── . . . │ . . <─────────────────────────── . . │ . . . . │ . . . . V . . gcc_jit_context_compile . ──────────────────────────> . . . . │ start of recording::context::compile () . . │ . . . . │ start of playback::context::compile () . . │ (create tempdir) . . . │ . . . . │ ACQUIRE MUTEX . . . │ . . . . V───────────────────────> toplev::main (for now) . . . . │ . . . . (various code) . . . . │ . . . . V . . . <───────────────── langhook:parse_file . . . │ . . . . │ (jit_langhook_parse_file) . . . │ . ..........................................│..................VVVVVVVVVVVVV... . . . │ . No GC in here . . . │ jit-playback.c . . . │ (playback of API calls) . . . ───────────────> creation of functions, . . . . types, expression trees . . . <──────────────── etc . . . │(handle_locations: add locations to . . . │ linemap and associate them with trees) . . . │ . . . . │ . No GC in here ..........................................│..................AAAAAAAAAAAAA... . . . │ for each function . . . ──> postprocess . . . │ . . . . ────────────> cgraph_finalize_function . . . <──────────── . . . <── . . . . │ . . . . ──────────────────> (end of . . . . │ langhook_parse_file) . . . . │ . . . . (various code) . . . . │ . . . . ↓ . . . <───────────────── langhook:write_globals . . . │ . . . . │ (jit_langhook_write_globals) . . . │ . . . . │ . . . . ──────────────────> finalize_compilation_unit . . . . │ . . . . (the middle─end and backend) . . . . ↓ . . <───────────────────────────── end of toplev::main . . │ . . . . V───────────────────────> toplev::finalize . . . . │ (purge internal state) . . <──────────────────────── end of toplev::finalize . . │ . . . . V─> playback::context::postprocess: . . │ . . . . │ (assuming an in-memory compile): . . │ . . . . │ . Convert assembler to DSO ("fake.so") . . │ . . . . │ . Load DSO (dlopen "fake.so") . . │ . . . . │ . Bundle it up in a jit::result . . <── . . . . │ . . . . │ RELEASE MUTEX . . . │ . . . . │ end of playback::context::compile () . . │ . . . . │ playback::context dtor . . ──> . . . . │ Normally we cleanup the tempdir here: . . │ ("fake.so" is unlinked from the . . │ filesystem at this point) . . │ If the client code requested debuginfo, the . . │ cleanup happens later (in gcc_jit_result_release) . . │ to make it easier on the debugger (see PR jit/64206) . . <── . . . . │ . . . . │ end of recording::context::compile () <─────────────────────────── . . │ . . . . V . . gcc_jit_result_get_code . ──────────────────────────> . . . . │ dlsym () within loaded DSO <─────────────────────────── . . Get (void*). . . . │ . . . . │ Call it . . . . ───────────────> . . . . │ . . . . │ . . . <─────────────── . . . │ . . . . etc│ . . . . │ . . . . V . . gcc_jit_result_release . ──────────────────────────> . . . . │ dlclose () the loaded DSO . . │ (code becomes uncallable) . . │ . . . . │ If the client code requested debuginfo, then . . │ cleanup of the tempdir was delayed. . . │ If that was the case, clean it up now. <─────────────────────────── . . │ . . . . @end example @noindent @end quotation @end itemize Here is a high-level summary from @code{jit-common.h}: @quotation In order to allow jit objects to be usable outside of a compile whilst working with the existing structure of GCC's code the C API is implemented in terms of a gcc::jit::recording::context, which records the calls made to it. When a gcc_jit_context is compiled, the recording context creates a playback context. The playback context invokes the bulk of the GCC code, and within the "frontend" parsing hook, plays back the recorded API calls, creating GCC tree objects. So there are two parallel families of classes: those relating to recording, and those relating to playback: @itemize * @item Visibility: recording objects are exposed back to client code, whereas playback objects are internal to the library. @item Lifetime: recording objects have a lifetime equal to that of the recording context that created them, whereas playback objects only exist within the frontend hook. @item Memory allocation: recording objects are allocated by the recording context, and automatically freed by it when the context is released, whereas playback objects are allocated within the GC heap, and garbage-collected; they can own GC-references. @item Integration with rest of GCC: recording objects are unrelated to the rest of GCC, whereas playback objects are wrappers around "tree" instances. Hence you can't ask a recording rvalue or lvalue what its type is, whereas you can for a playback rvalue of lvalue (since it can work with the underlying GCC tree nodes). @item Instancing: There can be multiple recording contexts "alive" at once (albeit it only one compiling at once), whereas there can only be one playback context alive at one time (since it interacts with the GC). @end itemize Ultimately if GCC could support multiple GC heaps and contexts, and finer-grained initialization, then this recording vs playback distinction could be eliminated. During a playback, we associate objects from the recording with their counterparts during this playback. For simplicity, we store this within the recording objects, as @code{void *m_playback_obj}, casting it to the appropriate playback object subclass. For these casts to make sense, the two class hierarchies need to have the same structure. Note that the playback objects that @code{m_playback_obj} points to are GC-allocated, but the recording objects don't own references: these associations only exist within a part of the code where the GC doesn't collect, and are set back to NULL before the GC can run. @end quotation @anchor{internals/index example-of-log-file}@anchor{5c} Another way to understand the structure of the code is to enable logging, via @pxref{5b,,gcc_jit_context_set_logfile()}. Here is an example of a log generated via this call: @example JIT: libgccjit (GCC) version 5.0.0 20150123 (experimental) (x86_64-unknown-linux-gnu) JIT: compiled by GNU C version 4.8.3 20140911 (Red Hat 4.8.3-7), GMP version 5.1.2, MPFR version 3.1.2, MPC version 1.0.1 JIT: entering: gcc_jit_context_set_str_option JIT: GCC_JIT_STR_OPTION_PROGNAME: "./test-hello-world.c.exe" JIT: exiting: gcc_jit_context_set_str_option JIT: entering: gcc_jit_context_set_int_option JIT: GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL: 3 JIT: exiting: gcc_jit_context_set_int_option JIT: entering: gcc_jit_context_set_bool_option JIT: GCC_JIT_BOOL_OPTION_DEBUGINFO: true JIT: exiting: gcc_jit_context_set_bool_option JIT: entering: gcc_jit_context_set_bool_option JIT: GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE: false JIT: exiting: gcc_jit_context_set_bool_option JIT: entering: gcc_jit_context_set_bool_option JIT: GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE: false JIT: exiting: gcc_jit_context_set_bool_option JIT: entering: gcc_jit_context_set_bool_option JIT: GCC_JIT_BOOL_OPTION_SELFCHECK_GC: true JIT: exiting: gcc_jit_context_set_bool_option JIT: entering: gcc_jit_context_set_bool_option JIT: GCC_JIT_BOOL_OPTION_DUMP_SUMMARY: false JIT: exiting: gcc_jit_context_set_bool_option JIT: entering: gcc_jit_context_get_type JIT: exiting: gcc_jit_context_get_type JIT: entering: gcc_jit_context_get_type JIT: exiting: gcc_jit_context_get_type JIT: entering: gcc_jit_context_new_param JIT: exiting: gcc_jit_context_new_param JIT: entering: gcc_jit_context_new_function JIT: exiting: gcc_jit_context_new_function JIT: entering: gcc_jit_context_new_param JIT: exiting: gcc_jit_context_new_param JIT: entering: gcc_jit_context_get_type JIT: exiting: gcc_jit_context_get_type JIT: entering: gcc_jit_context_new_function JIT: exiting: gcc_jit_context_new_function JIT: entering: gcc_jit_context_new_string_literal JIT: exiting: gcc_jit_context_new_string_literal JIT: entering: gcc_jit_function_new_block JIT: exiting: gcc_jit_function_new_block JIT: entering: gcc_jit_block_add_comment JIT: exiting: gcc_jit_block_add_comment JIT: entering: gcc_jit_context_new_call JIT: exiting: gcc_jit_context_new_call JIT: entering: gcc_jit_block_add_eval JIT: exiting: gcc_jit_block_add_eval JIT: entering: gcc_jit_block_end_with_void_return JIT: exiting: gcc_jit_block_end_with_void_return JIT: entering: gcc_jit_context_dump_reproducer_to_file JIT: entering: void gcc::jit::recording::context::dump_reproducer_to_file(const char*) JIT: exiting: void gcc::jit::recording::context::dump_reproducer_to_file(const char*) JIT: exiting: gcc_jit_context_dump_reproducer_to_file JIT: entering: gcc_jit_context_compile JIT: in-memory compile of ctxt: 0x1283e20 JIT: entering: gcc::jit::result* gcc::jit::recording::context::compile() JIT: GCC_JIT_STR_OPTION_PROGNAME: "./test-hello-world.c.exe" JIT: GCC_JIT_INT_OPTION_OPTIMIZATION_LEVEL: 3 JIT: GCC_JIT_BOOL_OPTION_DEBUGINFO: true JIT: GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE: false JIT: GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE: false JIT: GCC_JIT_BOOL_OPTION_DUMP_GENERATED_CODE: false JIT: GCC_JIT_BOOL_OPTION_DUMP_SUMMARY: false JIT: GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING: false JIT: GCC_JIT_BOOL_OPTION_SELFCHECK_GC: true JIT: GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES: false JIT: entering: void gcc::jit::recording::context::validate() JIT: exiting: void gcc::jit::recording::context::validate() JIT: entering: gcc::jit::playback::context::context(gcc::jit::recording::context*) JIT: exiting: gcc::jit::playback::context::context(gcc::jit::recording::context*) JIT: entering: gcc::jit::playback::compile_to_memory::compile_to_memory(gcc::jit::recording::context*) JIT: exiting: gcc::jit::playback::compile_to_memory::compile_to_memory(gcc::jit::recording::context*) JIT: entering: void gcc::jit::playback::context::compile() JIT: entering: gcc::jit::tempdir::tempdir(gcc::jit::logger*, int) JIT: exiting: gcc::jit::tempdir::tempdir(gcc::jit::logger*, int) JIT: entering: bool gcc::jit::tempdir::create() JIT: m_path_template: /tmp/libgccjit-XXXXXX JIT: m_path_tempdir: /tmp/libgccjit-CKq1M9 JIT: exiting: bool gcc::jit::tempdir::create() JIT: entering: void gcc::jit::playback::context::acquire_mutex() JIT: exiting: void gcc::jit::playback::context::acquire_mutex() JIT: entering: void gcc::jit::playback::context::make_fake_args(vec*, const char*, vec*) JIT: reusing cached configure-time options JIT: configure_time_options[0]: -mtune=generic JIT: configure_time_options[1]: -march=x86-64 JIT: exiting: void gcc::jit::playback::context::make_fake_args(vec*, const char*, vec*) JIT: entering: toplev::main JIT: argv[0]: ./test-hello-world.c.exe JIT: argv[1]: /tmp/libgccjit-CKq1M9/fake.c JIT: argv[2]: -fPIC JIT: argv[3]: -O3 JIT: argv[4]: -g JIT: argv[5]: -quiet JIT: argv[6]: --param JIT: argv[7]: ggc-min-expand=0 JIT: argv[8]: --param JIT: argv[9]: ggc-min-heapsize=0 JIT: argv[10]: -mtune=generic JIT: argv[11]: -march=x86-64 JIT: entering: bool jit_langhook_init() JIT: exiting: bool jit_langhook_init() JIT: entering: void gcc::jit::playback::context::replay() JIT: entering: void gcc::jit::recording::context::replay_into(gcc::jit::replayer*) JIT: exiting: void gcc::jit::recording::context::replay_into(gcc::jit::replayer*) JIT: entering: void gcc::jit::recording::context::disassociate_from_playback() JIT: exiting: void gcc::jit::recording::context::disassociate_from_playback() JIT: entering: void gcc::jit::playback::context::handle_locations() JIT: exiting: void gcc::jit::playback::context::handle_locations() JIT: entering: void gcc::jit::playback::function::build_stmt_list() JIT: exiting: void gcc::jit::playback::function::build_stmt_list() JIT: entering: void gcc::jit::playback::function::build_stmt_list() JIT: exiting: void gcc::jit::playback::function::build_stmt_list() JIT: entering: void gcc::jit::playback::function::postprocess() JIT: exiting: void gcc::jit::playback::function::postprocess() JIT: entering: void gcc::jit::playback::function::postprocess() JIT: exiting: void gcc::jit::playback::function::postprocess() JIT: exiting: void gcc::jit::playback::context::replay() JIT: entering: void jit_langhook_write_globals() JIT: entering: void gcc::jit::playback::context::write_global_decls_1() JIT: exiting: void gcc::jit::playback::context::write_global_decls_1() JIT: entering: void gcc::jit::playback::context::write_global_decls_2() JIT: exiting: void gcc::jit::playback::context::write_global_decls_2() JIT: exiting: void jit_langhook_write_globals() JIT: exiting: toplev::main JIT: entering: void gcc::jit::playback::context::extract_any_requested_dumps(vec*) JIT: exiting: void gcc::jit::playback::context::extract_any_requested_dumps(vec*) JIT: entering: toplev::finalize JIT: exiting: toplev::finalize JIT: entering: virtual void gcc::jit::playback::compile_to_memory::postprocess(const char*) JIT: entering: void gcc::jit::playback::context::convert_to_dso(const char*) JIT: entering: void gcc::jit::playback::context::invoke_driver(const char*, const char*, const char*, timevar_id_t, bool, bool) JIT: argv[0]: x86_64-unknown-linux-gnu-gcc-5.0.0 JIT: argv[1]: -shared JIT: argv[2]: /tmp/libgccjit-CKq1M9/fake.s JIT: argv[3]: -o JIT: argv[4]: /tmp/libgccjit-CKq1M9/fake.so JIT: argv[5]: -fno-use-linker-plugin JIT: argv[6]: (null) JIT: exiting: void gcc::jit::playback::context::invoke_driver(const char*, const char*, const char*, timevar_id_t, bool, bool) JIT: exiting: void gcc::jit::playback::context::convert_to_dso(const char*) JIT: entering: gcc::jit::result* gcc::jit::playback::context::dlopen_built_dso() JIT: GCC_JIT_BOOL_OPTION_DEBUGINFO was set: handing over tempdir to jit::result JIT: entering: gcc::jit::result::result(gcc::jit::logger*, void*, gcc::jit::tempdir*) JIT: exiting: gcc::jit::result::result(gcc::jit::logger*, void*, gcc::jit::tempdir*) JIT: exiting: gcc::jit::result* gcc::jit::playback::context::dlopen_built_dso() JIT: exiting: virtual void gcc::jit::playback::compile_to_memory::postprocess(const char*) JIT: entering: void gcc::jit::playback::context::release_mutex() JIT: exiting: void gcc::jit::playback::context::release_mutex() JIT: exiting: void gcc::jit::playback::context::compile() JIT: entering: gcc::jit::playback::context::~context() JIT: exiting: gcc::jit::playback::context::~context() JIT: exiting: gcc::jit::result* gcc::jit::recording::context::compile() JIT: gcc_jit_context_compile: returning (gcc_jit_result *)0x12f75d0 JIT: exiting: gcc_jit_context_compile JIT: entering: gcc_jit_result_get_code JIT: locating fnname: hello_world JIT: entering: void* gcc::jit::result::get_code(const char*) JIT: exiting: void* gcc::jit::result::get_code(const char*) JIT: gcc_jit_result_get_code: returning (void *)0x7ff6b8cd87f0 JIT: exiting: gcc_jit_result_get_code JIT: entering: gcc_jit_context_release JIT: deleting ctxt: 0x1283e20 JIT: entering: gcc::jit::recording::context::~context() JIT: exiting: gcc::jit::recording::context::~context() JIT: exiting: gcc_jit_context_release JIT: entering: gcc_jit_result_release JIT: deleting result: 0x12f75d0 JIT: entering: virtual gcc::jit::result::~result() JIT: entering: gcc::jit::tempdir::~tempdir() JIT: unlinking .s file: /tmp/libgccjit-CKq1M9/fake.s JIT: unlinking .so file: /tmp/libgccjit-CKq1M9/fake.so JIT: removing tempdir: /tmp/libgccjit-CKq1M9 JIT: exiting: gcc::jit::tempdir::~tempdir() JIT: exiting: virtual gcc::jit::result::~result() JIT: exiting: gcc_jit_result_release JIT: gcc::jit::logger::~logger() @end example @noindent @node Design notes,,Overview of code structure,Internals @anchor{internals/index design-notes}@anchor{19e} @section Design notes It should not be possible for client code to cause an internal compiler error. If this @emph{does} happen, the root cause should be isolated (perhaps using @pxref{5d,,gcc_jit_context_dump_reproducer_to_file()}) and the cause should be rejected via additional checking. The checking ideally should be within the libgccjit API entrypoints in libgccjit.c, since this is as close as possible to the error; failing that, a good place is within @code{recording::context::validate ()} in jit-recording.c. @node Indices and tables,Index,Internals,Top @anchor{index indices-and-tables}@anchor{19f} @unnumbered Indices and tables @itemize * @item @emph{genindex} @item @emph{modindex} @item @emph{search} @end itemize @c Some notes: @c @c The Sphinx C domain appears to lack explicit support for enum values, @c so I've been using :c:macro: for them. @c @c See http://sphinx-doc.org/domains.html#the-c-domain @node Index,,Indices and tables,Top @unnumbered Index @printindex ge @c %**end of body @bye