Example #1
0
    def generic_resolve(self, instance, attr):
        if attr in instance.struct:
            # It's a struct field => the type is well-known
            return instance.struct[attr]

        elif attr in instance.jitmethods:
            # It's a jitted method => typeinfer it
            meth = instance.jitmethods[attr]
            disp_type = types.Dispatcher(meth)

            class MethodTemplate(templates.AbstractTemplate):
                key = (self.key, attr)

                def generic(self, args, kws):
                    args = (instance, ) + tuple(args)
                    sig = disp_type.get_call_type(self.context, args, kws)
                    return sig.as_method()

            return types.BoundFunction(MethodTemplate, instance)

        elif attr in instance.jitprops:
            # It's a jitted property => typeinfer its getter
            impdct = instance.jitprops[attr]
            getter = impdct['get']
            disp_type = types.Dispatcher(getter)
            sig = disp_type.get_call_type(self.context, (instance, ), {})
            return sig.return_type
Example #2
0
def get_attr_impl(context, builder, typ, value, attr):
    """
    Generic getattr() for @jitclass instances.
    """
    if attr in typ.struct:
        # It's a struct field
        inst = context.make_helper(builder, typ, value=value)
        data_pointer = inst.data
        data = context.make_data_helper(builder,
                                        typ.get_data_type(),
                                        ref=data_pointer)
        return imputils.impl_ret_borrowed(context, builder, typ.struct[attr],
                                          getattr(data, _mangle_attr(attr)))
    elif attr in typ.jit_props:
        # It's a jitted property
        getter = typ.jit_props[attr]['get']
        sig = templates.signature(None, typ)
        dispatcher = types.Dispatcher(getter)
        sig = dispatcher.get_call_type(context.typing_context, [typ], {})
        call = context.get_function(dispatcher, sig)
        out = call(builder, [value])
        _add_linking_libs(context, call)
        return imputils.impl_ret_new_ref(context, builder, sig.return_type,
                                         out)

    raise NotImplementedError('attribute {0!r} not implemented'.format(attr))
Example #3
0
def set_attr_impl(context, builder, sig, args, attr):
    """
    Generic setattr() for @jitclass instances.
    """
    typ, valty = sig.args
    target, val = args

    if attr in typ.struct:
        # It's a struct member
        inst = context.make_helper(builder, typ, value=target)
        data_ptr = inst.data
        data = context.make_data_helper(builder, typ.get_data_type(), ref=data_ptr)

        # Get old value
        attr_type = typ.struct[attr]
        oldvalue = getattr(data, _mangle_attr(attr))

        # Store n
        setattr(data, _mangle_attr(attr), val)
        context.nrt.incref(builder, attr_type, val)

        # Delete old value
        context.nrt.decref(builder, attr_type, oldvalue)

    elif attr in typ.jitprops:
        # It's a jitted property
        setter = typ.jitprops[attr]["set"]
        disp_type = types.Dispatcher(setter)
        sig = disp_type.get_call_type(context.typing_context, (typ, valty), {})
        call = context.get_function(disp_type, sig)
        call(builder, (target, val))
        _add_linking_libs(context, call)
    else:
        raise NotImplementedError("attribute {0!r} not implemented".format(attr))
Example #4
0
    def generic_resolve(self, instance, attr):
        if attr in instance.struct:
            # It's a struct field => the type is well-known
            return instance.struct[attr]

        elif attr in instance.jit_methods:
            # It's a jitted method => typeinfer it
            meth = instance.jit_methods[attr]
            disp_type = types.Dispatcher(meth)

            class MethodTemplate(templates.AbstractTemplate):
                key = (self.key, attr)

                def generic(self, args, kws):
                    args = (instance, ) + tuple(args)
                    sig = disp_type.get_call_type(self.context, args, kws)
                    return sig.as_method()

            return types.BoundFunction(MethodTemplate, instance)

        elif attr in instance.jit_static_methods:
            # It's a jitted method => typeinfer it
            meth = instance.jit_static_methods[attr]
            disp_type = types.Dispatcher(meth)

            class StaticMethodTemplate(templates.AbstractTemplate):
                key = (self.key, attr)

                def generic(self, args, kws):
                    # Don't add instance as the first argument for a static
                    # method.
                    sig = disp_type.get_call_type(self.context, args, kws)
                    # sig itself does not include ClassInstanceType as it's
                    # first argument, so instead of calling sig.as_method()
                    # we insert the recvr. This is equivalent to
                    # sig.replace(args=(instance,) + sig.args).as_method().
                    return sig.replace(recvr=instance)

            return types.BoundFunction(StaticMethodTemplate, instance)

        elif attr in instance.jit_props:
            # It's a jitted property => typeinfer its getter
            impdct = instance.jit_props[attr]
            getter = impdct['get']
            disp_type = types.Dispatcher(getter)
            sig = disp_type.get_call_type(self.context, (instance, ), {})
            return sig.return_type
Example #5
0
 def generic(self, args, kws):
     instance = args[0]
     if isinstance(instance, types.ClassInstanceType) and \
             _dunder_meth in instance.jit_methods:
         meth = instance.jit_methods[_dunder_meth]
         disp_type = types.Dispatcher(meth)
         sig = disp_type.get_call_type(self.context, args, kws)
         return sig
Example #6
0
 def imp(context, builder, sig, args):
     instance_type = sig.args[0]
     method = instance_type.jitmethods[attr]
     disp_type = types.Dispatcher(method)
     call = context.get_function(disp_type, sig)
     out = call(builder, args)
     _add_linking_libs(context, call)
     return imputils.impl_ret_new_ref(context, builder, sig.return_type, out)
Example #7
0
 def test_weaktype(self):
     d = Dummy()
     e = Dummy()
     a = types.Dispatcher(d)
     b = types.Dispatcher(d)
     c = types.Dispatcher(e)
     self.assertIs(a.dispatcher, d)
     self.assertIs(b.dispatcher, d)
     self.assertIs(c.dispatcher, e)
     # Equality of alive references
     self.assertTrue(a == b)
     self.assertFalse(a != b)
     self.assertTrue(a != c)
     self.assertFalse(a == c)
     z = types.int8
     self.assertFalse(a == z)
     self.assertFalse(b == z)
     self.assertFalse(c == z)
     self.assertTrue(a != z)
     self.assertTrue(b != z)
     self.assertTrue(c != z)
     # Hashing and mappings
     s = set([a, b, c])
     self.assertEqual(len(s), 2)
     self.assertIn(a, s)
     self.assertIn(b, s)
     self.assertIn(c, s)
     # Kill the references
     d = e = None
     gc.collect()
     with self.assertRaises(ReferenceError):
         a.dispatcher
     with self.assertRaises(ReferenceError):
         b.dispatcher
     with self.assertRaises(ReferenceError):
         c.dispatcher
     # Dead references are always unequal
     self.assertFalse(a == b)
     self.assertFalse(a == c)
     self.assertFalse(b == c)
     self.assertFalse(a == z)
     self.assertTrue(a != b)
     self.assertTrue(a != c)
     self.assertTrue(b != c)
     self.assertTrue(a != z)
Example #8
0
 def generic(self, args, kws):
     # Redirect resolution to __init__
     instance_type = self.key.instance_type
     ctor = instance_type.jitmethods['__init__']
     boundargs = (instance_type.get_reference_type(), ) + args
     disp_type = types.Dispatcher(ctor)
     sig = disp_type.get_call_type(self.context, boundargs, kws)
     # Actual constructor returns an instance value (not None)
     out = templates.signature(instance_type, *sig.args[1:])
     return out
Example #9
0
    def generic(self, args, kws):
        """
        Type the overloaded function by compiling the appropriate
        implementation for the given args.
        """
        disp, new_args = self._get_impl(args, kws)
        if disp is None:
            return
        # Compile and type it for the given types
        disp_type = types.Dispatcher(disp)
        # Store the compiled overload for use in the lowering phase if there's
        # no inlining required (else functions are being compiled which will
        # never be used as they are inlined)
        if not self._inline.is_never_inline:
            # need to run the compiler front end up to type inference to compute
            # a signature
            from numba.core import typed_passes, compiler

            ir = compiler.run_frontend(disp_type.dispatcher.py_func)
            resolve = disp_type.dispatcher.get_call_template
            template, pysig, folded_args, kws = resolve(new_args, kws)

            typemap, return_type, calltypes = typed_passes.type_inference_stage(
                self.context, ir, folded_args, None)
            sig = Signature(return_type, folded_args, None)
            # this stores a load of info for the cost model function if supplied
            # it by default is None
            self._inline_overloads[sig.args] = {"folded_args": folded_args}
            # this stores the compiled overloads, if there's no compiled
            # overload available i.e. function is always inlined, the key still
            # needs to exist for type resolution

            # NOTE: If lowering is failing on a `_EmptyImplementationEntry`,
            #       the inliner has failed to inline this entry corretly.
            impl_init = _EmptyImplementationEntry("always inlined")
            self._compiled_overloads[sig.args] = impl_init
            if not self._inline.is_always_inline:
                # this branch is here because a user has supplied a function to
                # determine whether to inline or not. As a result both compiled
                # function and inliner info needed, delaying the computation of
                # this leads to an internal state mess at present. TODO: Fix!
                sig = disp_type.get_call_type(self.context, new_args, kws)
                self._compiled_overloads[sig.args] = disp_type.get_overload(
                    sig)
                # store the inliner information, it's used later in the cost
                # model function call
                iinfo = _inline_info(ir, typemap, calltypes, sig)
                self._inline_overloads[sig.args] = {
                    "folded_args": folded_args,
                    "iinfo": iinfo,
                }
        else:
            sig = disp_type.get_call_type(self.context, new_args, kws)
            self._compiled_overloads[sig.args] = disp_type.get_overload(sig)
        return sig
Example #10
0
    def __init__(self,
                 py_func,
                 locals={},
                 targetoptions={},
                 impl_kind='direct',
                 pipeline_class=compiler.Compiler):
        """
        Parameters
        ----------
        py_func: function object to be compiled
        locals: dict, optional
            Mapping of local variable names to Numba types.  Used to override
            the types deduced by the type inference engine.
        targetoptions: dict, optional
            Target-specific config options.
        impl_kind: str
            Select the compiler mode for `@jit` and `@generated_jit`
        pipeline_class: type numba.compiler.CompilerBase
            The compiler pipeline type.
        """
        self.typingctx = self.targetdescr.typing_context
        self.targetctx = self.targetdescr.target_context

        pysig = utils.pysignature(py_func)
        arg_count = len(pysig.parameters)
        can_fallback = not targetoptions.get('nopython', False)

        _DispatcherBase.__init__(self,
                                 arg_count,
                                 py_func,
                                 pysig,
                                 can_fallback,
                                 exact_match_required=False)

        functools.update_wrapper(self, py_func)

        self.targetoptions = targetoptions
        self.locals = locals
        self._cache = NullCache()
        compiler_class = self._impl_kinds[impl_kind]
        self._impl_kind = impl_kind
        self._compiler = compiler_class(py_func, self.targetdescr,
                                        targetoptions, locals, pipeline_class)
        self._cache_hits = collections.Counter()
        self._cache_misses = collections.Counter()

        self._type = types.Dispatcher(self)
        self.typingctx.insert_global(self, self._type)

        # Remember target restriction
        self._required_target_backend = targetoptions.get('target_backend')
Example #11
0
    def generic(self, args, kws):
        # Redirect resolution to __init__
        instance_type = self.key.instance_type
        ctor = instance_type.jitmethods["__init__"]
        boundargs = (instance_type.get_reference_type(),) + args
        disp_type = types.Dispatcher(ctor)
        sig = disp_type.get_call_type(self.context, boundargs, kws)

        if not isinstance(sig.return_type, types.NoneType):
            raise TypeError(f"__init__() should return None, not '{sig.return_type}'")

        # Actual constructor returns an instance value (not None)
        out = templates.signature(instance_type, *sig.args[1:])
        return out
Example #12
0
            def imp(context, builder, sig, args):
                instance_type = sig.args[0]

                if attr in instance_type.jit_methods:
                    method = instance_type.jit_methods[attr]
                elif attr in instance_type.jit_static_methods:
                    method = instance_type.jit_static_methods[attr]
                    # imp gets called as a method, where the first argument is
                    # self.  We drop this for a static method.
                    sig = sig.replace(args=sig.args[1:])
                    args = args[1:]

                disp_type = types.Dispatcher(method)
                call = context.get_function(disp_type, sig)
                out = call(builder, args)
                _add_linking_libs(context, call)
                return imputils.impl_ret_new_ref(context, builder,
                                                 sig.return_type, out)
Example #13
0
def ctor_impl(context, builder, sig, args):
    """
    Generic constructor (__new__) for jitclasses.
    """
    # Allocate the instance
    inst_typ = sig.return_type
    alloc_type = context.get_data_type(inst_typ.get_data_type())
    alloc_size = context.get_abi_sizeof(alloc_type)

    meminfo = context.nrt.meminfo_alloc_dtor(
        builder,
        context.get_constant(types.uintp, alloc_size),
        imp_dtor(context, builder.module, inst_typ),
    )
    data_pointer = context.nrt.meminfo_data(builder, meminfo)
    data_pointer = builder.bitcast(data_pointer,
                                   alloc_type.as_pointer())

    # Nullify all data
    builder.store(cgutils.get_null_value(alloc_type),
                  data_pointer)

    inst_struct = context.make_helper(builder, inst_typ)
    inst_struct.meminfo = meminfo
    inst_struct.data = data_pointer

    # Call the jitted __init__
    # TODO: extract the following into a common util
    init_sig = (sig.return_type,) + sig.args

    init = inst_typ.jit_methods['__init__']
    disp_type = types.Dispatcher(init)
    call = context.get_function(disp_type, types.void(*init_sig))
    _add_linking_libs(context, call)
    realargs = [inst_struct._getvalue()] + list(args)
    call(builder, realargs)

    # Prepare return value
    ret = inst_struct._getvalue()

    return imputils.impl_ret_new_ref(context, builder, inst_typ, ret)
Example #14
0
    def _build_impl(self, cache_key, args, kws):
        """Build and cache the implementation.

        Given the positional (`args`) and keyword arguments (`kws`), obtains
        the `overload` implementation and wrap it in a Dispatcher object.
        The expected argument types are returned for use by type-inference.
        The expected argument types are only different from the given argument
        types if there is an imprecise type in the given argument types.

        Parameters
        ----------
        cache_key : hashable
            The key used for caching the implementation.
        args : Tuple[Type]
            Types of positional argument.
        kws : Dict[Type]
            Types of keyword argument.

        Returns
        -------
        disp, args :
            On success, returns `(Dispatcher, Tuple[Type])`.
            On failure, returns `(None, None)`.

        """
        from numba import jit

        # Get the overload implementation for the given types
        ovf_result = self._overload_func(*args, **kws)
        if ovf_result is None:
            # No implementation => fail typing
            self._impl_cache[cache_key] = None, None
            return None, None
        elif isinstance(ovf_result, tuple):
            # The implementation returned a signature that the type-inferencer
            # should be using.
            sig, pyfunc = ovf_result
            args = sig.args
            kws = {}
            cache_key = None  # don't cache
        else:
            # Regular case
            pyfunc = ovf_result

        # Check type of pyfunc
        if not isinstance(pyfunc, FunctionType):
            msg = ("Implementator function returned by `@overload` "
                   "has an unexpected type.  Got {}")
            raise AssertionError(msg.format(pyfunc))

        # check that the typing and impl sigs match up
        if self._strict:
            self._validate_sigs(self._overload_func, pyfunc)
        # Make dispatcher
        jitdecor = jit(nopython=True, **self._jit_options)
        disp = jitdecor(pyfunc)
        # Make sure that the implementation can be fully compiled
        disp_type = types.Dispatcher(disp)
        disp_type.get_call_type(self.context, args, kws)
        if cache_key is not None:
            self._impl_cache[cache_key] = disp, args
        return disp, args
Example #15
0
    def generic(self, args, kws):
        """
        Type the overloaded function by compiling the appropriate
        implementation for the given args.
        """
        disp, new_args = self._get_impl(args, kws)
        if disp is None:
            return
        # Compile and type it for the given types
        disp_type = types.Dispatcher(disp)
        # Store the compiled overload for use in the lowering phase if there's
        # no inlining required (else functions are being compiled which will
        # never be used as they are inlined)
        if not self._inline.is_never_inline:
            # need to run the compiler front end up to type inference to compute
            # a signature
            from numba.core import typed_passes, compiler
            from numba.core.inline_closurecall import InlineWorker
            fcomp = disp._compiler
            flags = compiler.Flags()

            # Updating these causes problems?!
            #fcomp.targetdescr.options.parse_as_flags(flags,
            #                                         fcomp.targetoptions)
            #flags = fcomp._customize_flags(flags)

            # spoof a compiler pipline like the one that will be in use
            tyctx = fcomp.targetdescr.typing_context
            tgctx = fcomp.targetdescr.target_context
            compiler_inst = fcomp.pipeline_class(
                tyctx,
                tgctx,
                None,
                None,
                None,
                flags,
                None,
            )
            inline_worker = InlineWorker(
                tyctx,
                tgctx,
                fcomp.locals,
                compiler_inst,
                flags,
                None,
            )

            # If the inlinee contains something to trigger literal arg dispatch
            # then the pipeline call will unconditionally fail due to a raised
            # ForceLiteralArg exception. Therefore `resolve` is run first, as
            # type resolution must occur at some point, this will hit any
            # `literally` calls and because it's going via the dispatcher will
            # handle them correctly i.e. ForceLiteralArg propagates. This having
            # the desired effect of ensuring the pipeline call is only made in
            # situations that will succeed. For context see #5887.
            resolve = disp_type.dispatcher.get_call_template
            template, pysig, folded_args, kws = resolve(new_args, kws)
            ir = inline_worker.run_untyped_passes(disp_type.dispatcher.py_func)

            (typemap, return_type, calltypes,
             _) = typed_passes.type_inference_stage(self.context, ir,
                                                    folded_args, None)
            sig = Signature(return_type, folded_args, None)
            # this stores a load of info for the cost model function if supplied
            # it by default is None
            self._inline_overloads[sig.args] = {'folded_args': folded_args}
            # this stores the compiled overloads, if there's no compiled
            # overload available i.e. function is always inlined, the key still
            # needs to exist for type resolution

            # NOTE: If lowering is failing on a `_EmptyImplementationEntry`,
            #       the inliner has failed to inline this entry corretly.
            impl_init = _EmptyImplementationEntry('always inlined')
            self._compiled_overloads[sig.args] = impl_init
            if not self._inline.is_always_inline:
                # this branch is here because a user has supplied a function to
                # determine whether to inline or not. As a result both compiled
                # function and inliner info needed, delaying the computation of
                # this leads to an internal state mess at present. TODO: Fix!
                sig = disp_type.get_call_type(self.context, new_args, kws)
                self._compiled_overloads[sig.args] = disp_type.get_overload(
                    sig)
                # store the inliner information, it's used later in the cost
                # model function call
            iinfo = _inline_info(ir, typemap, calltypes, sig)
            self._inline_overloads[sig.args] = {
                'folded_args': folded_args,
                'iinfo': iinfo
            }
        else:
            sig = disp_type.get_call_type(self.context, new_args, kws)
            self._compiled_overloads[sig.args] = disp_type.get_overload(sig)
        return sig
Example #16
0
 def _numba_type_(self):
     return types.Dispatcher(self)