# Creating a trivial machine code function

Consider this C function:

>     int square(int i)
>     {
>       return i * i;
>     }

How can we construct this from within Python using libgccjit?

First we need to import the Python bindings to libgccjit:

    >>> import gccjit

All state associated with compilation is associated with a `gccjit.Context`:

    >>> ctxt = gccjit.Context()

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 int type, so let’s obtain this from the context, as a `gccjit.Type`:

    >>> int_type = ctxt.get_type(gccjit.TypeKind.INT)

The various objects in the API have reasonable __str__ methods:

    >>> print(int_type)
    int

Let’s create the function. To do so, we first need to construct its single parameter, specifying its type and giving it a name:

    >>> param_i = ctxt.new_param(int_type, b'i')
    >>> print(param_i)
    i

Now we can create the function:

    >>> fn = ctxt.new_function(gccjit.FunctionKind.EXPORTED,
    ...                        int_type, # return type
    ...                        b"square", # name
    ...                        [param_i]) # params
    >>> print(fn)
    square

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:

    >>> block = fn.new_block(b'entry')
    >>> print(block)
    entry

Our basic block is relatively simple: it immediately terminates by returning the value of an expression. We can build the expression:

    >>> expr = ctxt.new_binary_op(gccjit.BinaryOp.MULT,
    ...                           int_type,
    ...                           param_i, param_i)
    >>> print(expr)
    i * i

This 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:

    >>> block.end_with_return(expr)

OK, we’ve populated the context. We can now compile it:

    >>> jit_result = ctxt.compile()

and get a `gccjit.Result`.

We can now look up a specific machine code routine within the result, in this case, the function we created above:

    >>> void_ptr = jit_result.get_code(b"square")

We can now use ctypes.CFUNCTYPE to turn it into something we can call from Python:

    >>> import ctypes
    >>> int_int_func_type = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int)
    >>> callable = int_int_func_type(void_ptr)

It should now be possible to run the code:

    >>> callable(5)
    25

## Options

To get more information on what’s going on, you can set debugging flags on the context using `gccjit.Context.set_bool_option()`.

Setting `gccjit.BoolOption.DUMP_INITIAL_GIMPLE` will dump a C-like representation to stderr when you compile (GCC’s “GIMPLE” representation):

    >>> ctxt.set_bool_option(gccjit.BoolOption.DUMP_INITIAL_GIMPLE, True)
    >>> jit_result = ctxt.compile()
    square (signed int i)
    {
      signed int D.260;

      entry:
      D.260 = i * i;
      return D.260;
    }

We can see the generated machine code in assembler form (on stderr) by setting `gccjit.BoolOption.DUMP_GENERATED_CODE` on the context before compiling:

    >>> ctxt.set_bool_option(gccjit.BoolOption.DUMP_GENERATED_CODE, True)
    >>> jit_result = ctxt.compile()
            .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

By default, no optimizations are performed, the equivalent of GCC’s -O0 option. We can turn things up to e.g. -O3 by calling `gccjit.Context.set_int_option()` with `gccjit.IntOption.OPTIMIZATION_LEVEL`:

    >>> ctxt.set_int_option(gccjit.IntOption.OPTIMIZATION_LEVEL, 3)
    >>> jit_result = ctxt.compile()
            .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

Naturally this has only a small effect on such a trivial function.

## Full example

Here’s what the above looks like as a complete program:

>     import ctypes

>     import gccjit

>     def create_fn():
>         # Create a compilation context:
>         ctxt = gccjit.Context()

>         # Turn these on to get various kinds of debugging:
>         if 0:
>             ctxt.set_bool_option(gccjit.BoolOption.DUMP_INITIAL_TREE, True)
>             ctxt.set_bool_option(gccjit.BoolOption.DUMP_INITIAL_GIMPLE, True)
>             ctxt.set_bool_option(gccjit.BoolOption.DUMP_GENERATED_CODE, True)

>         # Adjust this to control optimization level of the generated code:
>         if 0:
>             ctxt.set_int_option(gccjit.IntOption.OPTIMIZATION_LEVEL, 3)

>         int_type = ctxt.get_type(gccjit.TypeKind.INT)

>         # Create parameter "i":
>         param_i = ctxt.new_param(int_type, b'i')
>         # Create the function:
>         fn = ctxt.new_function(gccjit.FunctionKind.EXPORTED,
>                                int_type,
>                                b"square",
>                                [param_i])

>         # Create a basic block within the function:
>         block = fn.new_block(b'entry')

>         # This basic block is relatively simple:
>         block.end_with_return(
>             ctxt.new_binary_op(gccjit.BinaryOp.MULT,
>                                int_type,
>                                param_i, param_i))

>         # Having populated the context, compile it.
>         jit_result = ctxt.compile()

>         # This is what you get back from ctxt.compile():
>         assert isinstance(jit_result, gccjit.Result)

>         return jit_result

>     def test_calling_fn(i):
>         jit_result = create_fn()

>         # Look up a specific machine code routine within the gccjit.Result,
>         # in this case, the function we created above:
>         void_ptr = jit_result.get_code(b"square")

>         # Now use ctypes.CFUNCTYPE to turn it into something we can call
>         # from Python:
>         int_int_func_type = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int)
>         code = int_int_func_type(void_ptr)

>         # Now try running the code:
>         return code(i)

>     if __name__ == '__main__':
>         print(test_calling_fn(5))