"""
rbclib is a C runtime library for rbc.

Sometimes the IR code generated by rbc needs to call some external helper
function written in C; rbclib makes it possible to write such functions.

This is the file layout:

  - _rbclib.c contains the actual C source code

  - _rbclib_build.py is the CFFI build script which is called by setup.py

  - _rbclib.abi3.so (_rbclib.pyd on Windows) is the native extension produced
    by _rbclib_build.py and that can be imported by 'import rbc.rbclib._rbclib'

rbclib functions can be called in two ways:

  1. From pure Python, through CFFI: for this you need to call
     e.g. lib._rbclib_add_ints(). This is useful e.g. inside tests

  2. From an @rjit function, through rbc.  For this you need to call
     e.g. rbclib.add_ints. The codegen logic is in rbclib/intrinsic.py.  In
     this case, the generated LLVM will contain a direct call to the
     underlying C function (_rbclib_add_ints), which means that such a
     function must be loaded in memory and LLVM must be aware of it. This is
     done by load_inside_llvm(), which is called from
     libfuncs.RBCLib.__init__().

NOTE: rbclib is based on CFFI, but this is not strictly necessary: in theory,
it is enough to dlopen() a C library which contains the desired
functions. However, using CFFI has many advantages:

  1. It is very easy to integrate the build process with setup.py

  2. You can call the functions also from Python (useful for testing)

  3. Loading the shared library in memory is as simple as importing the
     _rbclib module, instead of having to load the library explicitly
     e.g. through ctypes
"""

import llvmlite.binding
try:
    from . import _rbclib
except ImportError as e:
    # improve the ImportError error message
    msg = (f"{e}\nThis probably indicates "
           "that rbc has not been built/installed correctly, possibly "
           "because cffi was not available at compilation time")
    e.msg = msg
    e.args = (msg,)
    raise

from ._rbclib import lib, ffi  # noqa: F401
from .intrinsic import add_ints  # noqa: F401
from . import tracing_allocator  # noqa: F401, side effects

# this contains all the C names which we want to expose to the RBC
# compiler. See libfuncs.RBCLib.
FUNCTION_NAMES = [
    '_rbclib_add_ints',
    'rbclib_allocate_varlen_buffer',
    'rbclib_free_buffer',
    'rbclib_tracing_allocate_varlen_buffer',
    'rbclib_tracing_free_buffer',
]


def load_inside_llvm():
    llvmlite.binding.load_library_permanently(_rbclib.__file__)
