mirror of git://gcc.gnu.org/git/gcc.git
6538 lines
163 KiB
Plaintext
6538 lines
163 KiB
Plaintext
\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 20141110), November 10, 2014
|
|
|
|
David Malcolm
|
|
|
|
Copyright @copyright{} 2014, Free Software Foundation
|
|
@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 Free Software Foundation, Inc.
|
|
@c Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
|
@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 <http://www.gnu.org/licenses/>.
|
|
|
|
Contents:
|
|
|
|
@c Copyright (C) 2014 Free Software Foundation, Inc.
|
|
@c Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
|
@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 <http://www.gnu.org/licenses/>.
|
|
|
|
@menu
|
|
* Tutorial::
|
|
* Topic Reference::
|
|
* 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 2: Creating a trivial machine code function
|
|
|
|
* 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::
|
|
|
|
Topic Reference
|
|
|
|
* Compilation contexts::
|
|
* Objects::
|
|
* Types::
|
|
* Expressions::
|
|
* Creating and using functions::
|
|
* Source Locations::
|
|
* Compilation results::
|
|
|
|
Compilation contexts
|
|
|
|
* Lifetime-management::
|
|
* Thread-safety::
|
|
* Error-handling::
|
|
* 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::
|
|
|
|
Internals
|
|
|
|
* Working on the JIT library::
|
|
* Running the test suite::
|
|
* Environment variables::
|
|
* Overview of code structure::
|
|
|
|
@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 Free Software Foundation, Inc.
|
|
@c Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
|
@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 <http://www.gnu.org/licenses/>.
|
|
|
|
@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.
|
|
|
|
@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 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
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include <libgccjit.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
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 Free Software Foundation, Inc.
|
|
@c Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
|
@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 <http://www.gnu.org/licenses/>.
|
|
|
|
@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 <libgccjit.h>
|
|
@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 *}.
|
|
|
|
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::
|
|
* Full example::
|
|
|
|
@end menu
|
|
|
|
@node Options,Full example,,Tutorial part 2 Creating a trivial machine code function
|
|
@anchor{intro/tutorial02 options}@anchor{18}
|
|
@subsection Options
|
|
|
|
|
|
To get more information on what's going on, you can set debugging flags
|
|
on the context using @pxref{19,,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{1a,,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{1b,,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{1c,,gcc_jit_context_set_int_option()} with
|
|
@pxref{1d,,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{1e}
|
|
@subsection Full example
|
|
|
|
|
|
Here's what the above looks like as a complete program:
|
|
|
|
@quotation
|
|
|
|
@example
|
|
/* Usage example for libgccjit.so
|
|
Copyright (C) 2014 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
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include <libgccjit.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
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;
|
|
@}
|
|
|
|
/* 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", square (5));
|
|
|
|
error:
|
|
gcc_jit_context_release (ctxt);
|
|
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 Free Software Foundation, Inc.
|
|
@c Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
|
@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 <http://www.gnu.org/licenses/>.
|
|
|
|
@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{1f}@anchor{intro/tutorial03 doc}@anchor{20}
|
|
@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-squares,,,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 <libgccjit.h>
|
|
|
|
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{21}
|
|
@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{22,,gcc_jit_lvalue *}.
|
|
A @pxref{22,,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{22,,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{23,,gcc_jit_param *}, which is a subclass of
|
|
@pxref{22,,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{24,,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{22,,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{25}
|
|
@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{26,,gcc_jit_block *} instances within the
|
|
@pxref{27,,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{28,,gcc_jit_block_add_assignment()} to add
|
|
an assignment statement, and using @pxref{29,,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{2a,,gcc_jit_context_new_comparison()}:
|
|
|
|
@example
|
|
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{2b,,gcc_jit_block_end_with_conditional()}:
|
|
|
|
@example
|
|
gcc_jit_block_end_with_conditional (b_loop_cond, NULL, guard);
|
|
@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{2c,,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{2d,,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{2e,,gcc_jit_context_new_rvalue_from_int()} and
|
|
@pxref{2f,,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{30}
|
|
@subsection Visualizing the control flow graph
|
|
|
|
|
|
You can see the control flow graph of a function using
|
|
@pxref{31,,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-squares,,,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{32}
|
|
@subsection Full example
|
|
|
|
|
|
@quotation
|
|
|
|
@example
|
|
/* Usage example for libgccjit.so
|
|
Copyright (C) 2014 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
|
|
<http://www.gnu.org/licenses/>. */
|
|
|
|
#include <libgccjit.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
|
|
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 Free Software Foundation, Inc.
|
|
@c Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
|
@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 <http://www.gnu.org/licenses/>.
|
|
|
|
@node Tutorial part 4 Adding JIT-compilation to a toy interpreter,,Tutorial part 3 Loops and variables,Tutorial
|
|
@anchor{intro/tutorial04 tutorial-part-4-adding-jit-compilation-to-a-toy-interpreter}@anchor{33}@anchor{intro/tutorial04 doc}@anchor{34}
|
|
@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{35}
|
|
@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 (x<y).
|
|
|
|
@tab
|
|
|
|
@code{[..., x, y]}
|
|
|
|
@tab
|
|
|
|
@code{[..., (x<y)]}
|
|
|
|
@item
|
|
|
|
RECURSE
|
|
|
|
@tab
|
|
|
|
Recurse, passing the top
|
|
of the stack, and
|
|
popping the result.
|
|
|
|
@tab
|
|
|
|
@code{[..., x]}
|
|
|
|
@tab
|
|
|
|
@code{[..., fn(x)]}
|
|
|
|
@item
|
|
|
|
RETURN
|
|
|
|
@tab
|
|
|
|
Return the top of the
|
|
stack.
|
|
|
|
@tab
|
|
|
|
@code{[x]}
|
|
|
|
@tab
|
|
|
|
@code{[]}
|
|
|
|
@item
|
|
|
|
PUSH_CONST @cite{arg}
|
|
|
|
@tab
|
|
|
|
Push an int const.
|
|
|
|
@tab
|
|
|
|
@code{[...]}
|
|
|
|
@tab
|
|
|
|
@code{[..., arg]}
|
|
|
|
@item
|
|
|
|
JUMP_ABS_IF_TRUE @cite{arg}
|
|
|
|
@tab
|
|
|
|
Pop; if top of stack was
|
|
nonzero, jump to
|
|
@code{arg}.
|
|
|
|
@tab
|
|
|
|
@code{[..., x]}
|
|
|
|
@tab
|
|
|
|
@code{[...]}
|
|
|
|
@end multitable
|
|
|
|
|
|
Programs can be interpreted, disassembled, and compiled to machine code.
|
|
|
|
The interpreter reads @code{.toy} scripts. Here's what a simple recursive
|
|
factorial program looks like, the script @code{factorial.toy}.
|
|
The parser ignores lines beginning with a @cite{#}.
|
|
|
|
@quotation
|
|
|
|
@example
|
|
# Simple recursive factorial implementation, roughly equivalent to:
|
|
#
|
|
# int factorial (int arg)
|
|
# @{
|
|
# if (arg < 2)
|
|
# return arg
|
|
# return arg * factorial (arg - 1)
|
|
# @}
|
|
|
|
# Initial state:
|
|
# stack: [arg]
|
|
|
|
# 0:
|
|
DUP
|
|
# stack: [arg, arg]
|
|
|
|
# 1:
|
|
PUSH_CONST 2
|
|
# stack: [arg, arg, 2]
|
|
|
|
# 2:
|
|
BINARY_COMPARE_LT
|
|
# stack: [arg, (arg < 2)]
|
|
|
|
# 3:
|
|
JUMP_ABS_IF_TRUE 9
|
|
# stack: [arg]
|
|
|
|
# 4:
|
|
DUP
|
|
# stack: [arg, arg]
|
|
|
|
# 5:
|
|
PUSH_CONST 1
|
|
# stack: [arg, arg, 1]
|
|
|
|
# 6:
|
|
BINARY_SUBTRACT
|
|
# stack: [arg, (arg - 1)
|
|
|
|
# 7:
|
|
RECURSE
|
|
# stack: [arg, factorial(arg - 1)]
|
|
|
|
# 8:
|
|
BINARY_MULT
|
|
# stack: [arg * factorial(arg - 1)]
|
|
|
|
# 9:
|
|
RETURN
|
|
|
|
@end example
|
|
|
|
@noindent
|
|
@end quotation
|
|
|
|
The interpreter is a simple infinite loop with a big @code{switch} statement
|
|
based on what the next opcode is:
|
|
|
|
@quotation
|
|
|
|
@example
|
|
|
|
static int
|
|
toyvm_function_interpret (toyvm_function *fn, int arg, FILE *trace)
|
|
@{
|
|
toyvm_frame frame;
|
|
#define PUSH(ARG) (toyvm_frame_push (&frame, (ARG)))
|
|
#define POP(ARG) (toyvm_frame_pop (&frame))
|
|
|
|
frame.frm_function = fn;
|
|
frame.frm_pc = 0;
|
|
frame.frm_cur_depth = 0;
|
|
|
|
PUSH (arg);
|
|
|
|
while (1)
|
|
@{
|
|
toyvm_op *op;
|
|
int x, y;
|
|
assert (frame.frm_pc < fn->fn_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{36}
|
|
@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_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
|
|
|
|
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{37}
|
|
@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{38,,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{39}
|
|
@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{3a,,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{3b}
|
|
@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{31,,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{factorial,,,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{3c}
|
|
@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 *result = gcc_jit_context_compile (state.ctxt);
|
|
gcc_jit_context_release (state.ctxt);
|
|
|
|
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 = toyvm_function_compile (fn);
|
|
printf ("compiler result: %d\n",
|
|
code (atoi (argv[2])));
|
|
|
|
|
|
@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{3d}
|
|
@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{3e,,gcc_jit_context_new_location()} and using the
|
|
results.
|
|
|
|
@item
|
|
Enable the generation of debugging information, by setting
|
|
@pxref{3f,,GCC_JIT_BOOL_OPTION_DEBUGINFO} on the
|
|
@pxref{8,,gcc_jit_context} via
|
|
@pxref{19,,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{40}
|
|
@subsection Examining the generated code
|
|
|
|
|
|
How good is the optimized code?
|
|
|
|
We can turn up optimizations, by calling
|
|
@pxref{1c,,gcc_jit_context_set_int_option()} with
|
|
@pxref{1d,,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)
|
|
@{
|
|
<unnamed type> 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{41}
|
|
@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{42}
|
|
@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;
|
|
<unnamed type> _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{3f,,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;
|
|
<unnamed type> _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 <bb 4> (instr9);
|
|
else
|
|
goto <bb 3> (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 <stack_depth_24(2), stack_depth_53(3)>
|
|
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{26,,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{43}
|
|
@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;
|
|
<unnamed type> _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 <bb 4> (instr9);
|
|
else
|
|
goto <bb 3> (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;
|
|
<unnamed type> _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 <bb 4> (instr9);
|
|
else
|
|
goto <bb 3> (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 <stack$0_39(2), stack$0_1(3)>
|
|
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;
|
|
<unnamed type> _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 <bb 4> (instr9);
|
|
else
|
|
goto <bb 3> (instr4);
|
|
|
|
instr4:
|
|
/* DUP */:
|
|
_38 = arg_5(D) + -1;
|
|
_44 = factorial (_38);
|
|
_51 = arg_5(D) * _44;
|
|
stack$0_1 = _51;
|
|
|
|
# stack$0_52 = PHI <arg_5(D)(2), _51(3)>
|
|
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;
|
|
<unnamed type> _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 <arg_8(D)(0)>
|
|
# mult_acc_13 = PHI <1(0)>
|
|
initial:
|
|
|
|
<bb 5>:
|
|
# arg_4 = PHI <arg_9(2), _7(3)>
|
|
# mult_acc_1 = PHI <mult_acc_13(2), mult_acc_11(3)>
|
|
_5 = arg_4 <= 1;
|
|
_6 = (signed int) _5;
|
|
if (_6 != 0)
|
|
goto <bb 4> (instr9);
|
|
else
|
|
goto <bb 3> (instr4);
|
|
|
|
instr4:
|
|
/* DUP */:
|
|
_7 = arg_4 + -1;
|
|
mult_acc_11 = mult_acc_1 * arg_4;
|
|
goto <bb 5>;
|
|
|
|
# stack$0_12 = PHI <arg_4(5)>
|
|
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{44}
|
|
@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;
|
|
<unnamed type> _20;
|
|
signed int _21;
|
|
signed int _38;
|
|
signed int mul_tmp_44;
|
|
signed int mult_acc_51;
|
|
|
|
# arg_5 = PHI <arg_39(D)(0), _38(3)>
|
|
# mult_acc_1 = PHI <1(0), mult_acc_51(3)>
|
|
initial:
|
|
_20 = arg_5 <= 1;
|
|
_21 = (signed int) _20;
|
|
if (_21 != 0)
|
|
goto <bb 4> (instr9);
|
|
else
|
|
goto <bb 3> (instr4);
|
|
|
|
instr4:
|
|
/* DUP */:
|
|
_38 = arg_5 + -1;
|
|
mult_acc_51 = mult_acc_1 * arg_5;
|
|
goto <bb 2> (initial);
|
|
|
|
# stack$0_52 = PHI <arg_5(2)>
|
|
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 Free Software Foundation, Inc.
|
|
@c Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
|
@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 <http://www.gnu.org/licenses/>.
|
|
|
|
@node Topic Reference,Internals,Tutorial,Top
|
|
@anchor{topics/index doc}@anchor{45}@anchor{topics/index topic-reference}@anchor{46}
|
|
@chapter Topic Reference
|
|
|
|
|
|
@c Copyright (C) 2014 Free Software Foundation, Inc.
|
|
@c Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
|
@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 <http://www.gnu.org/licenses/>.
|
|
|
|
@menu
|
|
* Compilation contexts::
|
|
* Objects::
|
|
* Types::
|
|
* Expressions::
|
|
* Creating and using functions::
|
|
* Source Locations::
|
|
* Compilation results::
|
|
|
|
Compilation contexts
|
|
|
|
* Lifetime-management::
|
|
* Thread-safety::
|
|
* Error-handling::
|
|
* 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::
|
|
|
|
@end menu
|
|
|
|
|
|
@node Compilation contexts,Objects,,Topic Reference
|
|
@anchor{topics/contexts compilation-contexts}@anchor{47}@anchor{topics/contexts doc}@anchor{48}
|
|
@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::
|
|
* Debugging::
|
|
* Options: Options<2>.
|
|
|
|
@end menu
|
|
|
|
@node Lifetime-management,Thread-safety,,Compilation contexts
|
|
@anchor{topics/contexts lifetime-management}@anchor{49}
|
|
@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{e,,gcc_jit_object *} 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{4a}
|
|
@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,Lifetime-management,Compilation contexts
|
|
@anchor{topics/contexts thread-safety}@anchor{4b}
|
|
@subsection Thread-safety
|
|
|
|
|
|
Instances of @pxref{e,,gcc_jit_object *} 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{4a,,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,Debugging,Thread-safety,Compilation contexts
|
|
@anchor{topics/contexts error-handling}@anchor{4c}
|
|
@subsection Error-handling
|
|
|
|
|
|
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{4d,,gcc_jit_context_get_first_error()}.
|
|
|
|
@geindex gcc_jit_context_get_first_error (C function)
|
|
@anchor{topics/contexts gcc_jit_context_get_first_error}@anchor{4d}
|
|
@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
|
|
|
|
@node Debugging,Options<2>,Error-handling,Compilation contexts
|
|
@anchor{topics/contexts debugging}@anchor{4e}
|
|
@subsection Debugging
|
|
|
|
|
|
@geindex gcc_jit_context_dump_to_file (C function)
|
|
@anchor{topics/contexts gcc_jit_context_dump_to_file}@anchor{4f}
|
|
@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{38,,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{3f,,GCC_JIT_BOOL_OPTION_DEBUGINFO} to allow stepping through the
|
|
code in a debugger.
|
|
@end deffn
|
|
|
|
@node Options<2>,,Debugging,Compilation contexts
|
|
@anchor{topics/contexts options}@anchor{50}
|
|
@subsection Options
|
|
|
|
|
|
@menu
|
|
* String Options::
|
|
* Boolean options::
|
|
* Integer options::
|
|
|
|
@end menu
|
|
|
|
@node String Options,Boolean options,,Options<2>
|
|
@anchor{topics/contexts string-options}@anchor{51}
|
|
@subsubsection String Options
|
|
|
|
|
|
@geindex gcc_jit_context_set_str_option (C function)
|
|
@anchor{topics/contexts gcc_jit_context_set_str_option}@anchor{52}
|
|
@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{53}
|
|
@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{54}
|
|
@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{55}
|
|
@subsubsection Boolean options
|
|
|
|
|
|
@geindex gcc_jit_context_set_bool_option (C function)
|
|
@anchor{topics/contexts gcc_jit_context_set_bool_option}@anchor{19}
|
|
@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{56}
|
|
@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{3f}
|
|
@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{38,,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{57}
|
|
@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
|
|
<statement_list 0x7f4875a62cc0
|
|
type <void_type 0x7f4875a64bd0 VOID
|
|
align 8 symtab 0 alias set -1 canonical type 0x7f4875a64bd0
|
|
pointer_to_this <pointer_type 0x7f4875a64c78>>
|
|
side-effects head 0x7f4875a761e0 tail 0x7f4875a761f8 stmts 0x7f4875a62d20 0x7f4875a62d00
|
|
|
|
stmt <label_expr 0x7f4875a62d20 type <void_type 0x7f4875a64bd0>
|
|
side-effects
|
|
arg 0 <label_decl 0x7f4875a79080 entry type <void_type 0x7f4875a64bd0>
|
|
VOID file (null) line 0 col 0
|
|
align 1 context <function_decl 0x7f4875a77500 square>>>
|
|
stmt <return_expr 0x7f4875a62d00
|
|
type <integer_type 0x7f4875a645e8 public SI
|
|
size <integer_cst 0x7f4875a623a0 constant 32>
|
|
unit size <integer_cst 0x7f4875a623c0 constant 4>
|
|
align 32 symtab 0 alias set -1 canonical type 0x7f4875a645e8 precision 32 min <integer_cst 0x7f4875a62340 -2147483648> max <integer_cst 0x7f4875a62360 2147483647>
|
|
pointer_to_this <pointer_type 0x7f4875a6b348>>
|
|
side-effects
|
|
arg 0 <modify_expr 0x7f4875a72a78 type <integer_type 0x7f4875a645e8>
|
|
side-effects arg 0 <result_decl 0x7f4875a7a000 D.54>
|
|
arg 1 <mult_expr 0x7f4875a72a50 type <integer_type 0x7f4875a645e8>
|
|
arg 0 <parm_decl 0x7f4875a79000 i> arg 1 <parm_decl 0x7f4875a79000 i>>>>>
|
|
@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{1a}
|
|
@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{1b}
|
|
@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{58}
|
|
@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{59}
|
|
@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{5a,,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{5b}
|
|
@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{5a}
|
|
@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{5c}
|
|
@subsubsection Integer options
|
|
|
|
|
|
@geindex gcc_jit_context_set_int_option (C function)
|
|
@anchor{topics/contexts gcc_jit_context_set_int_option}@anchor{1c}
|
|
@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{5d}
|
|
@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{1d}
|
|
@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 Free Software Foundation, Inc.
|
|
@c Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
|
@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 <http://www.gnu.org/licenses/>.
|
|
|
|
@node Objects,Types,Compilation contexts,Topic Reference
|
|
@anchor{topics/objects objects}@anchor{5e}@anchor{topics/objects doc}@anchor{5f}
|
|
@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{60}
|
|
@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 Free Software Foundation, Inc.
|
|
@c Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
|
@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 <http://www.gnu.org/licenses/>.
|
|
|
|
@node Types,Expressions,Objects,Topic Reference
|
|
@anchor{topics/types doc}@anchor{61}@anchor{topics/types types}@anchor{62}
|
|
@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{63,,gcc_jit_type_get_pointer()} and @pxref{64,,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{65}
|
|
@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 {xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx} {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 *)}
|
|
|
|
@end multitable
|
|
|
|
@end deffn
|
|
|
|
@geindex gcc_jit_context_get_int_type (C function)
|
|
@anchor{topics/types gcc_jit_context_get_int_type}@anchor{66}
|
|
@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{67}
|
|
@subsection Pointers, @cite{const}, and @cite{volatile}
|
|
|
|
|
|
@geindex gcc_jit_type_get_pointer (C function)
|
|
@anchor{topics/types gcc_jit_type_get_pointer}@anchor{63}
|
|
@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{64}
|
|
@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{68}
|
|
@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{69}
|
|
@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{6a}
|
|
@subsection Structures and unions
|
|
|
|
|
|
@geindex gcc_jit_struct (C type)
|
|
@anchor{topics/types gcc_jit_struct}@anchor{6b}
|
|
@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{6c}
|
|
@deffn {C Type} gcc_jit_field
|
|
@end deffn
|
|
|
|
A field within a @pxref{6b,,gcc_jit_struct}.
|
|
|
|
You can model C @cite{struct} types by creating @pxref{6b,,gcc_jit_struct *} and
|
|
@pxref{6c,,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{6d}
|
|
@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{6e}
|
|
@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{6f}
|
|
@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{70}
|
|
@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{71,,gcc_jit_struct_set_fields()}.
|
|
@end deffn
|
|
|
|
@geindex gcc_jit_struct_as_type (C function)
|
|
@anchor{topics/types gcc_jit_struct_as_type}@anchor{72}
|
|
@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{71}
|
|
@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 Free Software Foundation, Inc.
|
|
@c Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
|
@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 <http://www.gnu.org/licenses/>.
|
|
|
|
@node Expressions,Creating and using functions,Types,Topic Reference
|
|
@anchor{topics/expressions expressions}@anchor{73}@anchor{topics/expressions doc}@anchor{74}
|
|
@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{75}
|
|
@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{76}
|
|
@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{77}
|
|
@subsubsection Simple expressions
|
|
|
|
|
|
@geindex gcc_jit_context_new_rvalue_from_int (C function)
|
|
@anchor{topics/expressions gcc_jit_context_new_rvalue_from_int}@anchor{2e}
|
|
@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 value.
|
|
@end deffn
|
|
|
|
@geindex gcc_jit_context_zero (C function)
|
|
@anchor{topics/expressions gcc_jit_context_zero}@anchor{29}
|
|
@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{2d}
|
|
@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{2f}
|
|
@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 value.
|
|
@end deffn
|
|
|
|
@geindex gcc_jit_context_new_rvalue_from_ptr (C function)
|
|
@anchor{topics/expressions gcc_jit_context_new_rvalue_from_ptr}@anchor{78}
|
|
@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{79}
|
|
@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{7a}
|
|
@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{7b}
|
|
@subsubsection Unary Operations
|
|
|
|
|
|
@geindex gcc_jit_context_new_unary_op (C function)
|
|
@anchor{topics/expressions gcc_jit_context_new_unary_op}@anchor{7c}
|
|
@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{7d}
|
|
@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{7e,,GCC_JIT_UNARY_OP_MINUS}
|
|
|
|
@tab
|
|
|
|
@cite{-(EXPR)}
|
|
|
|
@item
|
|
|
|
@pxref{7f,,GCC_JIT_UNARY_OP_BITWISE_NEGATE}
|
|
|
|
@tab
|
|
|
|
@cite{~(EXPR)}
|
|
|
|
@item
|
|
|
|
@pxref{80,,GCC_JIT_UNARY_OP_LOGICAL_NEGATE}
|
|
|
|
@tab
|
|
|
|
@cite{!(EXPR)}
|
|
|
|
@end multitable
|
|
|
|
|
|
@geindex GCC_JIT_UNARY_OP_MINUS (C macro)
|
|
@anchor{topics/expressions GCC_JIT_UNARY_OP_MINUS}@anchor{7e}
|
|
@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{7f}
|
|
@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{80}
|
|
@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
|
|
|
|
@node Binary Operations,Comparisons,Unary Operations,Rvalues
|
|
@anchor{topics/expressions binary-operations}@anchor{81}
|
|
@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{82}
|
|
@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{83,,GCC_JIT_BINARY_OP_PLUS}
|
|
|
|
@tab
|
|
|
|
@cite{x + y}
|
|
|
|
@item
|
|
|
|
@code{GCC_JIT_BINARY_OP_MINUS}
|
|
|
|
@tab
|
|
|
|
@cite{x - y}
|
|
|
|
@item
|
|
|
|
@pxref{84,,GCC_JIT_BINARY_OP_MULT}
|
|
|
|
@tab
|
|
|
|
@cite{x * y}
|
|
|
|
@item
|
|
|
|
@pxref{85,,GCC_JIT_BINARY_OP_DIVIDE}
|
|
|
|
@tab
|
|
|
|
@cite{x / y}
|
|
|
|
@item
|
|
|
|
@pxref{86,,GCC_JIT_BINARY_OP_MODULO}
|
|
|
|
@tab
|
|
|
|
@cite{x % y}
|
|
|
|
@item
|
|
|
|
@pxref{87,,GCC_JIT_BINARY_OP_BITWISE_AND}
|
|
|
|
@tab
|
|
|
|
@cite{x & y}
|
|
|
|
@item
|
|
|
|
@pxref{88,,GCC_JIT_BINARY_OP_BITWISE_XOR}
|
|
|
|
@tab
|
|
|
|
@cite{x ^ y}
|
|
|
|
@item
|
|
|
|
@pxref{89,,GCC_JIT_BINARY_OP_BITWISE_OR}
|
|
|
|
@tab
|
|
|
|
@cite{x | y}
|
|
|
|
@item
|
|
|
|
@pxref{8a,,GCC_JIT_BINARY_OP_LOGICAL_AND}
|
|
|
|
@tab
|
|
|
|
@cite{x && y}
|
|
|
|
@item
|
|
|
|
@pxref{8b,,GCC_JIT_BINARY_OP_LOGICAL_OR}
|
|
|
|
@tab
|
|
|
|
@cite{x || y}
|
|
|
|
@item
|
|
|
|
@pxref{8c,,GCC_JIT_BINARY_OP_LSHIFT}
|
|
|
|
@tab
|
|
|
|
@cite{x << y}
|
|
|
|
@item
|
|
|
|
@pxref{8d,,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{83}
|
|
@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{8e,,gcc_jit_context_new_array_access()}.
|
|
@end deffn
|
|
|
|
|
|
@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{84}
|
|
@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{85}
|
|
@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{86}
|
|
@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{87}
|
|
@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{88}
|
|
@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{89}
|
|
@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{8a}
|
|
@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{8b}
|
|
@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{8c}
|
|
@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{8d}
|
|
@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{8f}
|
|
@subsubsection Comparisons
|
|
|
|
|
|
@geindex gcc_jit_context_new_comparison (C function)
|
|
@anchor{topics/expressions gcc_jit_context_new_comparison}@anchor{2a}
|
|
@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{90}
|
|
@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{91}
|
|
@subsubsection Function calls
|
|
|
|
|
|
@geindex gcc_jit_context_new_call (C function)
|
|
@anchor{topics/expressions gcc_jit_context_new_call}@anchor{92}
|
|
@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{92,,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{93,,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{94}
|
|
@subsubsection Type-coercion
|
|
|
|
|
|
@geindex gcc_jit_context_new_cast (C function)
|
|
@anchor{topics/expressions gcc_jit_context_new_cast}@anchor{95}
|
|
@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{96}
|
|
@subsection Lvalues
|
|
|
|
|
|
@geindex gcc_jit_lvalue (C type)
|
|
@anchor{topics/expressions gcc_jit_lvalue}@anchor{22}
|
|
@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{97}
|
|
@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{98}
|
|
@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{99}
|
|
@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{9a}
|
|
@subsubsection Global variables
|
|
|
|
|
|
@geindex gcc_jit_context_new_global (C function)
|
|
@anchor{topics/expressions gcc_jit_context_new_global}@anchor{9b}
|
|
@deffn {C Function} gcc_jit_lvalue * gcc_jit_context_new_global (gcc_jit_context@w{ }*ctxt, gcc_jit_location@w{ }*loc, gcc_jit_type@w{ }*type, const char@w{ }*name)
|
|
|
|
Add a new global variable of the given type and name to the context.
|
|
@end deffn
|
|
|
|
@node Working with pointers structs and unions,,Lvalues,Expressions
|
|
@anchor{topics/expressions working-with-pointers-structs-and-unions}@anchor{9c}
|
|
@subsection Working with pointers, structs and unions
|
|
|
|
|
|
@geindex gcc_jit_rvalue_dereference (C function)
|
|
@anchor{topics/expressions gcc_jit_rvalue_dereference}@anchor{9d}
|
|
@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{9e}
|
|
@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{9f}
|
|
@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{a0}
|
|
@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{8e}
|
|
@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 Free Software Foundation, Inc.
|
|
@c Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
|
@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 <http://www.gnu.org/licenses/>.
|
|
|
|
@node Creating and using functions,Source Locations,Expressions,Topic Reference
|
|
@anchor{topics/functions doc}@anchor{a1}@anchor{topics/functions creating-and-using-functions}@anchor{a2}
|
|
@section Creating and using functions
|
|
|
|
|
|
@menu
|
|
* Params::
|
|
* Functions::
|
|
* Blocks::
|
|
* Statements::
|
|
|
|
@end menu
|
|
|
|
@node Params,Functions,,Creating and using functions
|
|
@anchor{topics/functions params}@anchor{a3}
|
|
@subsection Params
|
|
|
|
|
|
@geindex gcc_jit_param (C type)
|
|
@anchor{topics/functions gcc_jit_param}@anchor{23}
|
|
@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{a4}
|
|
@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{a5}
|
|
@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{a6}
|
|
@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{a7}
|
|
@subsection Functions
|
|
|
|
|
|
@geindex gcc_jit_function (C type)
|
|
@anchor{topics/functions gcc_jit_function}@anchor{27}
|
|
@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{a8}
|
|
@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{a9}
|
|
@deffn {C Macro} GCC_JIT_FUNCTION_EXPORTED
|
|
|
|
Function is defined by the client code and visible
|
|
by name outside of the JIT.
|
|
@end deffn
|
|
|
|
@geindex GCC_JIT_FUNCTION_INTERNAL (C macro)
|
|
@anchor{topics/functions GCC_JIT_FUNCTION_INTERNAL}@anchor{aa}
|
|
@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{ab}
|
|
@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{ac}
|
|
@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{ad}
|
|
@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{ae}
|
|
@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{af}
|
|
@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{31}
|
|
@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{24}
|
|
@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{b0}
|
|
@subsection Blocks
|
|
|
|
|
|
@geindex gcc_jit_block (C type)
|
|
@anchor{topics/functions gcc_jit_block}@anchor{26}
|
|
@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{b1}
|
|
@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{b2}
|
|
@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{b3}
|
|
@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{b4}
|
|
@subsection Statements
|
|
|
|
|
|
@geindex gcc_jit_block_add_eval (C function)
|
|
@anchor{topics/functions gcc_jit_block_add_eval}@anchor{93}
|
|
@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{28}
|
|
@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{2c}
|
|
@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{3a}
|
|
@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{57,,GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE}
|
|
and @pxref{1a,,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{2b}
|
|
@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{b5}
|
|
@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{b6}
|
|
@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{b7}
|
|
@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 Free Software Foundation, Inc.
|
|
@c Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
|
@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 <http://www.gnu.org/licenses/>.
|
|
|
|
@node Source Locations,Compilation results,Creating and using functions,Topic Reference
|
|
@anchor{topics/locations source-locations}@anchor{b8}@anchor{topics/locations doc}@anchor{b9}
|
|
@section Source Locations
|
|
|
|
|
|
@geindex gcc_jit_location (C type)
|
|
@anchor{topics/locations gcc_jit_location}@anchor{38}
|
|
@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{3e,,gcc_jit_context_new_location()}.
|
|
|
|
You need to enable @pxref{3f,,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{3e}
|
|
@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{ba}
|
|
@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{4f,,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 Free Software Foundation, Inc.
|
|
@c Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
|
@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 <http://www.gnu.org/licenses/>.
|
|
|
|
@node Compilation results,,Source Locations,Topic Reference
|
|
@anchor{topics/results compilation-results}@anchor{bb}@anchor{topics/results doc}@anchor{bc}
|
|
@section Compilation results
|
|
|
|
|
|
@geindex gcc_jit_result (C type)
|
|
@anchor{topics/results gcc_jit_result}@anchor{16}
|
|
@deffn {C Type} gcc_jit_result
|
|
|
|
A @cite{gcc_jit_result} encapsulates the result of compiling a context.
|
|
@end deffn
|
|
|
|
@geindex gcc_jit_context_compile (C function)
|
|
@anchor{topics/results 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 *}.
|
|
@end deffn
|
|
|
|
@geindex gcc_jit_result_get_code (C function)
|
|
@anchor{topics/results 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.
|
|
This will need to be cast to a function pointer of the
|
|
correct type before it can be called.
|
|
@end deffn
|
|
|
|
@geindex gcc_jit_result_release (C function)
|
|
@anchor{topics/results gcc_jit_result_release}@anchor{bd}
|
|
@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.
|
|
@end deffn
|
|
|
|
@c Copyright (C) 2014 Free Software Foundation, Inc.
|
|
@c Originally contributed by David Malcolm <dmalcolm@redhat.com>
|
|
@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 <http://www.gnu.org/licenses/>.
|
|
|
|
@node Internals,Indices and tables,Topic Reference,Top
|
|
@anchor{internals/index internals}@anchor{be}@anchor{internals/index doc}@anchor{bf}
|
|
@chapter Internals
|
|
|
|
|
|
@menu
|
|
* Working on the JIT library::
|
|
* Running the test suite::
|
|
* Environment variables::
|
|
* Overview of code structure::
|
|
|
|
@end menu
|
|
|
|
@node Working on the JIT library,Running the test suite,,Internals
|
|
@anchor{internals/index working-on-the-jit-library}@anchor{c0}
|
|
@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 \
|
|
--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{c1}
|
|
@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
|
|
@anchor{internals/index cmdoption--enable-languages}@anchor{c2}
|
|
@deffn {Option} --enable-languages=jit
|
|
|
|
This specifies which frontends to build. The JIT library looks like
|
|
a frontend to the rest of the code.
|
|
@end deffn
|
|
|
|
@geindex command line option; --disable-bootstrap
|
|
@anchor{internals/index cmdoption--disable-bootstrap}@anchor{c3}
|
|
@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{c4}
|
|
@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{c5}
|
|
@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.exe
|
|
@end example
|
|
|
|
@noindent
|
|
|
|
@node Environment variables,Overview of code structure,Running the test suite,Internals
|
|
@anchor{internals/index environment-variables}@anchor{c6}
|
|
@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{c7}
|
|
@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{c8}
|
|
@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{c9}
|
|
@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 Overview of code structure,,Environment variables,Internals
|
|
@anchor{internals/index overview-of-code-structure}@anchor{ca}
|
|
@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 builtins_manager; // declared within jit-builtins.h
|
|
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 .
|
|
──────────────────────────> . .
|
|
. . │ . .
|
|
. . │ 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
|
|
. . │ RELEASE MUTEX .
|
|
. . │ . .
|
|
. . │ Convert assembler to DSO
|
|
. . │ . .
|
|
. . │ Load DSO .
|
|
<─────────────────────────── . .
|
|
│ . . . .
|
|
Get (void*). . . .
|
|
│ . . . .
|
|
│ Call it . . . .
|
|
───────────────> . . .
|
|
. │ . . .
|
|
. │ . . .
|
|
<─────────────── . . .
|
|
│ . . . .
|
|
│ . . . .
|
|
etc
|
|
|
|
@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
|
|
|
|
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.
|
|
|
|
@node Indices and tables,Index,Internals,Top
|
|
@anchor{index indices-and-tables}@anchor{cb}
|
|
@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
|