.. Copyright (C) 2014-2023 Free Software Foundation, Inc.
   Originally contributed by David Malcolm <dmalcolm@redhat.com>

   This 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 of the License, or
   (at your option) any later version.

   This program 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 this program.  If not, see
   <https://www.gnu.org/licenses/>.

.. default-domain:: cpp

Creating and using functions
============================

Params
------
.. class:: gccjit::param

   A `gccjit::param` represents a parameter to a function.

.. function:: gccjit::param \
              gccjit::context::new_param (gccjit::type type,\
                                          const char *name, \
                                          gccjit::location loc)

   In preparation for creating a function, create a new parameter of the
   given type and name.

:class:`gccjit::param` is a subclass of :class:`gccjit::lvalue` (and thus
of :class:`gccjit::rvalue` and :class:`gccjit::object`).  It is a thin
wrapper around the C API's :c:expr:`gcc_jit_param *`.

Functions
---------

.. class:: gccjit::function

   A `gccjit::function` represents a function - either one that we're
   creating ourselves, or one that we're referencing.

.. function::  gccjit::function \
               gccjit::context::new_function (enum gcc_jit_function_kind,\
                                              gccjit::type return_type, \
                                              const char *name, \
                                              std::vector<param> &params, \
                                              int is_variadic, \
                                              gccjit::location loc)

   Create a gcc_jit_function with the given name and parameters.

   Parameters "is_variadic" and "loc" are optional.

   This is a wrapper around the C API's :c:func:`gcc_jit_context_new_function`.

.. function::  gccjit::function \
               gccjit::context::get_builtin_function (const char *name)

   This is a wrapper around the C API's
   :c:func:`gcc_jit_context_get_builtin_function`.

.. function::  gccjit::param \
               gccjit::function::get_param (int index) const

   Get the param of the given index (0-based).

.. function::  void \
               gccjit::function::dump_to_dot (const char *path)

   Emit the function in graphviz format to the given path.

.. function:: gccjit::lvalue \
              gccjit::function::new_local (gccjit::type type,\
                                           const char *name, \
                                           gccjit::location loc)

   Create a new local variable within the function, of the given type and
   name.

Blocks
------
.. class:: gccjit::block

   A `gccjit::block` represents a basic block within a function  i.e. a
   sequence of statements with a single entry point and a single exit
   point.

   :class:`gccjit::block` is a subclass of :class:`gccjit::object`.

   The first basic block that you create within a function will
   be the entrypoint.

   Each basic block that you create within a function must be
   terminated, either with a conditional, a jump, a return, or
   a switch.

   It's legal to have multiple basic blocks that return within
   one function.

.. function::  gccjit::block \
               gccjit::function::new_block (const char *name)

   Create a basic block of the given name.  The name may be NULL, but
   providing meaningful names is often helpful when debugging: it may
   show up in dumps of the internal representation, and in error
   messages.

Statements
----------

.. function:: void\
              gccjit::block::add_eval (gccjit::rvalue rvalue, \
                                       gccjit::location loc)

   Add evaluation of an rvalue, discarding the result
   (e.g. a function call that "returns" void).

   This is equivalent to this C code:

   .. code-block:: c

     (void)expression;

.. function:: void\
              gccjit::block::add_assignment (gccjit::lvalue lvalue, \
                                             gccjit::rvalue rvalue, \
                                             gccjit::location loc)

   Add evaluation of an rvalue, assigning the result to the given
   lvalue.

   This is roughly equivalent to this C code:

   .. code-block:: c

     lvalue = rvalue;

.. function:: void\
              gccjit::block::add_assignment_op (gccjit::lvalue lvalue, \
                                                enum gcc_jit_binary_op, \
                                                gccjit::rvalue rvalue, \
                                                gccjit::location loc)

   Add evaluation of an rvalue, using the result to modify an
   lvalue.

   This is analogous to "+=" and friends:

   .. code-block:: c

     lvalue += rvalue;
     lvalue *= rvalue;
     lvalue /= rvalue;

   etc.  For example:

   .. code-block:: c

     /* "i++" */
     loop_body.add_assignment_op (
       i,
       GCC_JIT_BINARY_OP_PLUS,
       ctxt.one (int_type));

.. function:: void\
              gccjit::block::add_comment (const char *text, \
	                                  gccjit::location loc)

   Add a no-op textual comment to the internal representation of the
   code.  It will be optimized away, but will be visible in the dumps
   seen via :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_INITIAL_TREE`
   and :c:macro:`GCC_JIT_BOOL_OPTION_DUMP_INITIAL_GIMPLE`,
   and thus may be of use when debugging how your project's internal
   representation gets converted to the libgccjit IR.

   Parameter "loc" is optional.

.. function:: void\
              gccjit::block::end_with_conditional (gccjit::rvalue boolval,\
                                                   gccjit::block on_true,\
                                                   gccjit::block on_false, \
                                                   gccjit::location loc)

   Terminate a block by adding evaluation of an rvalue, branching on the
   result to the appropriate successor block.

   This is roughly equivalent to this C code:

   .. code-block:: c

     if (boolval)
       goto on_true;
     else
       goto on_false;

   block, boolval, on_true, and on_false must be non-NULL.

.. function:: void\
              gccjit::block::end_with_jump (gccjit::block target, \
                                            gccjit::location loc)

   Terminate a block by adding a jump to the given target block.

   This is roughly equivalent to this C code:

   .. code-block:: c

      goto target;

.. function:: void\
              gccjit::block::end_with_return (gccjit::rvalue rvalue, \
                                              gccjit::location loc)

   Terminate a block.

   Both params are optional.

   An rvalue must be provided for a function returning non-void, and
   must not be provided by a function "returning" `void`.

   If an rvalue is provided, the block is terminated by evaluating the
   rvalue and returning the value.

   This is roughly equivalent to this C code:

   .. code-block:: c

      return expression;

   If an rvalue is not provided, the block is terminated by adding a
   valueless return, for use within a function with "void" return type.

   This is equivalent to this C code:

   .. code-block:: c

      return;

.. class:: gccjit::case_

   A `gccjit::case_` represents a case within a switch statement, and
   is created within a particular :class:`gccjit::context` using
   :func:`gccjit::context::new_case`.  It is a subclass of
   :class:`gccjit::object`.

   Each case expresses a multivalued range of integer values.  You
   can express single-valued cases by passing in the same value for
   both `min_value` and `max_value`.

.. function:: gccjit::case_ *\
               gccjit::context::new_case (gccjit::rvalue min_value,\
                                          gccjit::rvalue max_value,\
                                          gccjit::block dest_block)

    Create a new gccjit::case for use in a switch statement.
    `min_value` and `max_value` must be constants of an integer type,
    which must match that of the expression of the switch statement.

    `dest_block` must be within the same function as the switch
    statement.

.. function:: void\
              gccjit::block::end_with_switch (gccjit::rvalue expr,\
                                              gccjit::block default_block,\
                                              std::vector <gccjit::case_> cases,\
                                              gccjit::location loc)

   Terminate a block by adding evalation of an rvalue, then performing
   a multiway branch.

   This is roughly equivalent to this C code:

   .. code-block:: c

     switch (expr)
       {
       default:
         goto default_block;

       case C0.min_value ... C0.max_value:
         goto C0.dest_block;

       case C1.min_value ... C1.max_value:
         goto C1.dest_block;

       ...etc...

       case C[N - 1].min_value ... C[N - 1].max_value:
         goto C[N - 1].dest_block;
     }

   ``expr`` must be of the same integer type as all of the ``min_value``
   and ``max_value`` within the cases.

   The ranges of the cases must not overlap (or have duplicate
   values).

   The API entrypoints relating to switch statements and cases:

      * :func:`gccjit::block::end_with_switch`

      * :func:`gccjit::context::new_case`

   were added in :ref:`LIBGCCJIT_ABI_3`; you can test for their presence
   using

   .. code-block:: c

      #ifdef LIBGCCJIT_HAVE_SWITCH_STATEMENTS

   Here's an example of creating a switch statement:

     .. literalinclude:: ../../../../testsuite/jit.dg/test-switch.cc
       :start-after: /* Quote from here in docs/cp/topics/functions.rst.  */
       :end-before: /* Quote up to here in docs/cp/topics/functions.rst.  */
       :language: c++
