1import sys
2import platform
3
4import llvmlite.binding as ll
5import llvmlite.llvmpy.core as lc
6from llvmlite import ir
7
8from numba import _dynfunc
9from numba.core.callwrapper import PyCallWrapper
10from numba.core.base import BaseContext, PYOBJECT
11from numba.core import utils, types, config, cgutils, callconv, codegen, externals, fastmathpass, intrinsics
12from numba.core.utils import cached_property
13from numba.core.options import TargetOptions
14from numba.core.runtime import rtsys
15from numba.core.compiler_lock import global_compiler_lock
16import numba.core.entrypoints
17from numba.core.cpu_options import (ParallelOptions, FastMathOptions,
18                                    InlineOptions)
19from numba.cpython import setobj, listobj
20
21# Keep those structures in sync with _dynfunc.c.
22
23class ClosureBody(cgutils.Structure):
24    _fields = [('env', types.pyobject)]
25
26
27class EnvBody(cgutils.Structure):
28    _fields = [
29        ('globals', types.pyobject),
30        ('consts', types.pyobject),
31    ]
32
33
34class CPUContext(BaseContext):
35    """
36    Changes BaseContext calling convention
37    """
38    allow_dynamic_globals = True
39
40    # Overrides
41    def create_module(self, name):
42        return self._internal_codegen._create_empty_module(name)
43
44    @global_compiler_lock
45    def init(self):
46        self.is32bit = (utils.MACHINE_BITS == 32)
47        self._internal_codegen = codegen.JITCPUCodegen("numba.exec")
48
49        # Add ARM ABI functions from libgcc_s
50        if platform.machine() == 'armv7l':
51            ll.load_library_permanently('libgcc_s.so.1')
52
53        # Map external C functions.
54        externals.c_math_functions.install(self)
55
56        # Initialize NRT runtime
57        rtsys.initialize(self)
58
59        # Initialize additional implementations
60        import numba.cpython.unicode
61        import numba.typed.dictimpl
62        import numba.experimental.function_type
63
64    def load_additional_registries(self):
65        # Add target specific implementations
66        from numba.np import npyimpl
67        from numba.cpython import cmathimpl, mathimpl, printimpl, randomimpl
68        from numba.misc import cffiimpl
69        self.install_registry(cmathimpl.registry)
70        self.install_registry(cffiimpl.registry)
71        self.install_registry(mathimpl.registry)
72        self.install_registry(npyimpl.registry)
73        self.install_registry(printimpl.registry)
74        self.install_registry(randomimpl.registry)
75
76        # load 3rd party extensions
77        numba.core.entrypoints.init_all()
78
79    @property
80    def target_data(self):
81        return self._internal_codegen.target_data
82
83    def with_aot_codegen(self, name, **aot_options):
84        aot_codegen = codegen.AOTCPUCodegen(name, **aot_options)
85        return self.subtarget(_internal_codegen=aot_codegen,
86                              aot_mode=True)
87
88    def codegen(self):
89        return self._internal_codegen
90
91    @cached_property
92    def call_conv(self):
93        return callconv.CPUCallConv(self)
94
95    def get_env_body(self, builder, envptr):
96        """
97        From the given *envptr* (a pointer to a _dynfunc.Environment object),
98        get a EnvBody allowing structured access to environment fields.
99        """
100        body_ptr = cgutils.pointer_add(
101            builder, envptr, _dynfunc._impl_info['offsetof_env_body'])
102        return EnvBody(self, builder, ref=body_ptr, cast_ref=True)
103
104    def get_env_manager(self, builder):
105        envgv = self.declare_env_global(builder.module,
106                                        self.get_env_name(self.fndesc))
107        envarg = builder.load(envgv)
108        pyapi = self.get_python_api(builder)
109        pyapi.emit_environment_sentry(
110            envarg, debug_msg=self.fndesc.env_name,
111        )
112        env_body = self.get_env_body(builder, envarg)
113        return pyapi.get_env_manager(self.environment, env_body, envarg)
114
115    def get_generator_state(self, builder, genptr, return_type):
116        """
117        From the given *genptr* (a pointer to a _dynfunc.Generator object),
118        get a pointer to its state area.
119        """
120        return cgutils.pointer_add(
121            builder, genptr, _dynfunc._impl_info['offsetof_generator_state'],
122            return_type=return_type)
123
124    def build_list(self, builder, list_type, items):
125        """
126        Build a list from the Numba *list_type* and its initial *items*.
127        """
128        return listobj.build_list(self, builder, list_type, items)
129
130    def build_set(self, builder, set_type, items):
131        """
132        Build a set from the Numba *set_type* and its initial *items*.
133        """
134        return setobj.build_set(self, builder, set_type, items)
135
136    def build_map(self, builder, dict_type, item_types, items):
137        from numba.typed import dictobject
138
139        return dictobject.build_map(self, builder, dict_type, item_types, items)
140
141
142    def post_lowering(self, mod, library):
143        if self.fastmath:
144            fastmathpass.rewrite_module(mod, self.fastmath)
145
146        if self.is32bit:
147            # 32-bit machine needs to replace all 64-bit div/rem to avoid
148            # calls to compiler-rt
149            intrinsics.fix_divmod(mod)
150
151        library.add_linking_library(rtsys.library)
152
153    def create_cpython_wrapper(self, library, fndesc, env, call_helper,
154                               release_gil=False):
155        wrapper_module = self.create_module("wrapper")
156        fnty = self.call_conv.get_function_type(fndesc.restype, fndesc.argtypes)
157        wrapper_callee = wrapper_module.add_function(fnty, fndesc.llvm_func_name)
158        builder = PyCallWrapper(self, wrapper_module, wrapper_callee,
159                                fndesc, env, call_helper=call_helper,
160                                release_gil=release_gil)
161        builder.build()
162        library.add_ir_module(wrapper_module)
163
164    def create_cfunc_wrapper(self, library, fndesc, env, call_helper):
165
166        wrapper_module = self.create_module("cfunc_wrapper")
167        fnty = self.call_conv.get_function_type(fndesc.restype, fndesc.argtypes)
168        wrapper_callee = wrapper_module.add_function(fnty, fndesc.llvm_func_name)
169
170        ll_argtypes = [self.get_value_type(ty) for ty in fndesc.argtypes]
171        ll_return_type = self.get_value_type(fndesc.restype)
172
173        wrapty = ir.FunctionType(ll_return_type, ll_argtypes)
174        wrapfn = wrapper_module.add_function(wrapty, fndesc.llvm_cfunc_wrapper_name)
175        builder = ir.IRBuilder(wrapfn.append_basic_block('entry'))
176
177        status, out = self.call_conv.call_function(
178            builder, wrapper_callee, fndesc.restype, fndesc.argtypes, wrapfn.args)
179
180        with builder.if_then(status.is_error, likely=False):
181            # If (and only if) an error occurred, acquire the GIL
182            # and use the interpreter to write out the exception.
183            pyapi = self.get_python_api(builder)
184            gil_state = pyapi.gil_ensure()
185            self.call_conv.raise_error(builder, pyapi, status)
186            cstr = self.insert_const_string(builder.module, repr(self))
187            strobj = pyapi.string_from_string(cstr)
188            pyapi.err_write_unraisable(strobj)
189            pyapi.decref(strobj)
190            pyapi.gil_release(gil_state)
191
192        builder.ret(out)
193        library.add_ir_module(wrapper_module)
194
195    def get_executable(self, library, fndesc, env):
196        """
197        Returns
198        -------
199        (cfunc, fnptr)
200
201        - cfunc
202            callable function (Can be None)
203        - fnptr
204            callable function address
205        - env
206            an execution environment (from _dynfunc)
207        """
208        # Code generation
209        baseptr = library.get_pointer_to_function(fndesc.llvm_func_name)
210        fnptr = library.get_pointer_to_function(fndesc.llvm_cpython_wrapper_name)
211
212        # Note: we avoid reusing the original docstring to avoid encoding
213        # issues on Python 2, see issue #1908
214        doc = "compiled wrapper for %r" % (fndesc.qualname,)
215        cfunc = _dynfunc.make_function(fndesc.lookup_module(),
216                                       fndesc.qualname.split('.')[-1],
217                                       doc, fnptr, env,
218                                       # objects to keepalive with the function
219                                       (library,)
220                                       )
221        library.codegen.set_env(self.get_env_name(fndesc), env)
222        return cfunc
223
224    def calc_array_sizeof(self, ndim):
225        '''
226        Calculate the size of an array struct on the CPU target
227        '''
228        aryty = types.Array(types.int32, ndim, 'A')
229        return self.get_abi_sizeof(self.get_value_type(aryty))
230
231
232# ----------------------------------------------------------------------------
233# TargetOptions
234
235class CPUTargetOptions(TargetOptions):
236    OPTIONS = {
237        "nopython": bool,
238        "nogil": bool,
239        "forceobj": bool,
240        "looplift": bool,
241        "boundscheck": bool,
242        "debug": bool,
243        "_nrt": bool,
244        "no_rewrites": bool,
245        "no_cpython_wrapper": bool,
246        "no_cfunc_wrapper": bool,
247        "fastmath": FastMathOptions,
248        "error_model": str,
249        "parallel": ParallelOptions,
250        "inline": InlineOptions,
251    }
252
253
254# ----------------------------------------------------------------------------
255# Internal
256
257def remove_refct_calls(func):
258    """
259    Remove redundant incref/decref within on a per block basis
260    """
261    for bb in func.basic_blocks:
262        remove_null_refct_call(bb)
263        remove_refct_pairs(bb)
264
265
266def remove_null_refct_call(bb):
267    """
268    Remove refct api calls to NULL pointer
269    """
270    pass
271    ## Skipped for now
272    # for inst in bb.instructions:
273    #     if isinstance(inst, lc.CallOrInvokeInstruction):
274    #         fname = inst.called_function.name
275    #         if fname == "Py_IncRef" or fname == "Py_DecRef":
276    #             arg = inst.args[0]
277    #             print(type(arg))
278    #             if isinstance(arg, lc.ConstantPointerNull):
279    #                 inst.erase_from_parent()
280
281
282def remove_refct_pairs(bb):
283    """
284    Remove incref decref pairs on the same variable
285    """
286
287    didsomething = True
288
289    while didsomething:
290        didsomething = False
291
292        increfs = {}
293        decrefs = {}
294
295        # Mark
296        for inst in bb.instructions:
297            if isinstance(inst, lc.CallOrInvokeInstruction):
298                fname = inst.called_function.name
299                if fname == "Py_IncRef":
300                    arg = inst.operands[0]
301                    increfs[arg] = inst
302                elif fname == "Py_DecRef":
303                    arg = inst.operands[0]
304                    decrefs[arg] = inst
305
306        # Sweep
307        for val in increfs.keys():
308            if val in decrefs:
309                increfs[val].erase_from_parent()
310                decrefs[val].erase_from_parent()
311                didsomething = True
312