-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
Copy pathdispatcher.py
1337 lines (1144 loc) · 50.6 KB
/
dispatcher.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# -*- coding: utf-8 -*-
import collections
import functools
import sys
import types as pytypes
import uuid
import weakref
from contextlib import ExitStack
from abc import abstractmethod
from numba import _dispatcher
from numba.core import (
utils, types, errors, typing, serialize, config, compiler, sigutils
)
from numba.core.compiler_lock import global_compiler_lock
from numba.core.typeconv.rules import default_type_manager
from numba.core.typing.templates import fold_arguments
from numba.core.typing.typeof import Purpose, typeof
from numba.core.bytecode import get_code_object
from numba.core.caching import NullCache, FunctionCache
from numba.core import entrypoints
import numba.core.event as ev
class OmittedArg(object):
"""
A placeholder for omitted arguments with a default value.
"""
def __init__(self, value):
self.value = value
def __repr__(self):
return "omitted arg(%r)" % (self.value,)
@property
def _numba_type_(self):
return types.Omitted(self.value)
class _FunctionCompiler(object):
def __init__(self, py_func, targetdescr, targetoptions, locals,
pipeline_class):
self.py_func = py_func
self.targetdescr = targetdescr
self.targetoptions = targetoptions
self.locals = locals
self.pysig = utils.pysignature(self.py_func)
self.pipeline_class = pipeline_class
# Remember key=(args, return_type) combinations that will fail
# compilation to avoid compilation attempt on them. The values are
# the exceptions.
self._failed_cache = {}
def fold_argument_types(self, args, kws):
"""
Given positional and named argument types, fold keyword arguments
and resolve defaults by inserting types.Omitted() instances.
A (pysig, argument types) tuple is returned.
"""
def normal_handler(index, param, value):
return value
def default_handler(index, param, default):
return types.Omitted(default)
def stararg_handler(index, param, values):
return types.StarArgTuple(values)
# For now, we take argument values from the @jit function
args = fold_arguments(self.pysig, args, kws,
normal_handler,
default_handler,
stararg_handler)
return self.pysig, args
def compile(self, args, return_type):
status, retval = self._compile_cached(args, return_type)
if status:
return retval
else:
raise retval
def _compile_cached(self, args, return_type):
key = tuple(args), return_type
try:
return False, self._failed_cache[key]
except KeyError:
pass
try:
retval = self._compile_core(args, return_type)
except errors.TypingError as e:
self._failed_cache[key] = e
return False, e
else:
return True, retval
def _compile_core(self, args, return_type):
flags = compiler.Flags()
self.targetdescr.options.parse_as_flags(flags, self.targetoptions)
flags = self._customize_flags(flags)
impl = self._get_implementation(args, {})
cres = compiler.compile_extra(self.targetdescr.typing_context,
self.targetdescr.target_context,
impl,
args=args, return_type=return_type,
flags=flags, locals=self.locals,
pipeline_class=self.pipeline_class)
# Check typing error if object mode is used
if cres.typing_error is not None and not flags.enable_pyobject:
raise cres.typing_error
return cres
def get_globals_for_reduction(self):
return serialize._get_function_globals_for_reduction(self.py_func)
def _get_implementation(self, args, kws):
return self.py_func
def _customize_flags(self, flags):
return flags
class _GeneratedFunctionCompiler(_FunctionCompiler):
def __init__(self, py_func, targetdescr, targetoptions, locals,
pipeline_class):
super(_GeneratedFunctionCompiler, self).__init__(
py_func, targetdescr, targetoptions, locals, pipeline_class)
self.impls = set()
def get_globals_for_reduction(self):
# This will recursively get the globals used by any nested
# implementation function.
return serialize._get_function_globals_for_reduction(self.py_func)
def _get_implementation(self, args, kws):
impl = self.py_func(*args, **kws)
# Check the generating function and implementation signatures are
# compatible, otherwise compiling would fail later.
pysig = utils.pysignature(self.py_func)
implsig = utils.pysignature(impl)
ok = len(pysig.parameters) == len(implsig.parameters)
if ok:
for pyparam, implparam in zip(pysig.parameters.values(),
implsig.parameters.values()):
# We allow the implementation to omit default values, but
# if it mentions them, they should have the same value...
if (pyparam.name != implparam.name or
pyparam.kind != implparam.kind or
(implparam.default is not implparam.empty and
implparam.default != pyparam.default)):
ok = False
if not ok:
raise TypeError("generated implementation %s should be compatible "
"with signature '%s', but has signature '%s'"
% (impl, pysig, implsig))
self.impls.add(impl)
return impl
_CompileStats = collections.namedtuple(
'_CompileStats', ('cache_path', 'cache_hits', 'cache_misses'))
class CompilingCounter(object):
"""
A simple counter that increment in __enter__ and decrement in __exit__.
"""
def __init__(self):
self.counter = 0
def __enter__(self):
assert self.counter >= 0
self.counter += 1
def __exit__(self, *args, **kwargs):
self.counter -= 1
assert self.counter >= 0
def __bool__(self):
return self.counter > 0
__nonzero__ = __bool__
class _DispatcherBase(_dispatcher.Dispatcher):
"""
Common base class for dispatcher Implementations.
"""
__numba__ = "py_func"
def __init__(self, arg_count, py_func, pysig, can_fallback,
exact_match_required):
self._tm = default_type_manager
# A mapping of signatures to compile results
self.overloads = collections.OrderedDict()
self.py_func = py_func
# other parts of Numba assume the old Python 2 name for code object
self.func_code = get_code_object(py_func)
# but newer python uses a different name
self.__code__ = self.func_code
# a place to keep an active reference to the types of the active call
self._types_active_call = set()
# Default argument values match the py_func
self.__defaults__ = py_func.__defaults__
argnames = tuple(pysig.parameters)
default_values = self.py_func.__defaults__ or ()
defargs = tuple(OmittedArg(val) for val in default_values)
try:
lastarg = list(pysig.parameters.values())[-1]
except IndexError:
has_stararg = False
else:
has_stararg = lastarg.kind == lastarg.VAR_POSITIONAL
_dispatcher.Dispatcher.__init__(self, self._tm.get_pointer(),
arg_count, self._fold_args,
argnames, defargs,
can_fallback,
has_stararg,
exact_match_required)
self.doc = py_func.__doc__
self._compiling_counter = CompilingCounter()
self._enable_sysmon = bool(config.ENABLE_SYS_MONITORING)
weakref.finalize(self, self._make_finalizer())
def _compilation_chain_init_hook(self):
"""
This will be called ahead of any part of compilation taking place (this
even includes being ahead of working out the types of the arguments).
This permits activities such as initialising extension entry points so
that the compiler knows about additional externally defined types etc
before it does anything.
"""
entrypoints.init_all()
def _reset_overloads(self):
self._clear()
self.overloads.clear()
def _make_finalizer(self):
"""
Return a finalizer function that will release references to
related compiled functions.
"""
overloads = self.overloads
targetctx = self.targetctx
# Early-bind utils.shutting_down() into the function's local namespace
# (see issue #689)
def finalizer(shutting_down=utils.shutting_down):
# The finalizer may crash at shutdown, skip it (resources
# will be cleared by the process exiting, anyway).
if shutting_down():
return
# This function must *not* hold any reference to self:
# we take care to bind the necessary objects in the closure.
for cres in overloads.values():
try:
targetctx.remove_user_function(cres.entry_point)
except KeyError:
pass
return finalizer
@property
def signatures(self):
"""
Returns a list of compiled function signatures.
"""
return list(self.overloads)
@property
def nopython_signatures(self):
return [cres.signature for cres in self.overloads.values()
if not cres.objectmode]
def disable_compile(self, val=True):
"""Disable the compilation of new signatures at call time.
"""
# If disabling compilation then there must be at least one signature
assert (not val) or len(self.signatures) > 0
self._can_compile = not val
def add_overload(self, cres):
args = tuple(cres.signature.args)
sig = [a._code for a in args]
self._insert(sig, cres.entry_point, cres.objectmode)
self.overloads[args] = cres
def fold_argument_types(self, args, kws):
return self._compiler.fold_argument_types(args, kws)
def get_call_template(self, args, kws):
"""
Get a typing.ConcreteTemplate for this dispatcher and the given
*args* and *kws* types. This allows to resolve the return type.
A (template, pysig, args, kws) tuple is returned.
"""
# XXX how about a dispatcher template class automating the
# following?
# Fold keyword arguments and resolve default values
pysig, args = self._compiler.fold_argument_types(args, kws)
kws = {}
# Ensure an overload is available
if self._can_compile:
self.compile(tuple(args))
# Create function type for typing
func_name = self.py_func.__name__
name = "CallTemplate({0})".format(func_name)
# The `key` isn't really used except for diagnosis here,
# so avoid keeping a reference to `cfunc`.
call_template = typing.make_concrete_template(
name, key=func_name, signatures=self.nopython_signatures)
return call_template, pysig, args, kws
def get_overload(self, sig):
"""
Return the compiled function for the given signature.
"""
args, return_type = sigutils.normalize_signature(sig)
return self.overloads[tuple(args)].entry_point
@property
def is_compiling(self):
"""
Whether a specialization is currently being compiled.
"""
return self._compiling_counter
def _compile_for_args(self, *args, **kws):
"""
For internal use. Compile a specialized version of the function
for the given *args* and *kws*, and return the resulting callable.
"""
assert not kws
# call any initialisation required for the compilation chain (e.g.
# extension point registration).
self._compilation_chain_init_hook()
def error_rewrite(e, issue_type):
"""
Rewrite and raise Exception `e` with help supplied based on the
specified issue_type.
"""
if config.SHOW_HELP:
help_msg = errors.error_extras[issue_type]
e.patch_message('\n'.join((str(e).rstrip(), help_msg)))
if config.FULL_TRACEBACKS:
raise e
else:
raise e.with_traceback(None)
argtypes = []
for a in args:
if isinstance(a, OmittedArg):
argtypes.append(types.Omitted(a.value))
else:
argtypes.append(self.typeof_pyval(a))
return_val = None
try:
return_val = self.compile(tuple(argtypes))
except errors.ForceLiteralArg as e:
# Received request for compiler re-entry with the list of arguments
# indicated by e.requested_args.
# First, check if any of these args are already Literal-ized
already_lit_pos = [i for i in e.requested_args
if isinstance(args[i], types.Literal)]
if already_lit_pos:
# Abort compilation if any argument is already a Literal.
# Letting this continue will cause infinite compilation loop.
m = ("Repeated literal typing request.\n"
"{}.\n"
"This is likely caused by an error in typing. "
"Please see nested and suppressed exceptions.")
info = ', '.join('Arg #{} is {}'.format(i, args[i])
for i in sorted(already_lit_pos))
raise errors.CompilerError(m.format(info))
# Convert requested arguments into a Literal.
args = [(types.literal
if i in e.requested_args
else lambda x: x)(args[i])
for i, v in enumerate(args)]
# Re-enter compilation with the Literal-ized arguments
return_val = self._compile_for_args(*args)
except errors.TypingError as e:
# Intercept typing error that may be due to an argument
# that failed inferencing as a Numba type
failed_args = []
for i, arg in enumerate(args):
val = arg.value if isinstance(arg, OmittedArg) else arg
try:
tp = typeof(val, Purpose.argument)
except ValueError as typeof_exc:
failed_args.append((i, str(typeof_exc)))
else:
if tp is None:
failed_args.append(
(i, f"cannot determine Numba type of value {val}"))
if failed_args:
# Patch error message to ease debugging
args_str = "\n".join(
f"- argument {i}: {err}" for i, err in failed_args
)
msg = (f"{str(e).rstrip()} \n\nThis error may have been caused "
f"by the following argument(s):\n{args_str}\n")
e.patch_message(msg)
error_rewrite(e, 'typing')
except errors.UnsupportedError as e:
# Something unsupported is present in the user code, add help info
error_rewrite(e, 'unsupported_error')
except (errors.NotDefinedError, errors.RedefinedError,
errors.VerificationError) as e:
# These errors are probably from an issue with either the code
# supplied being syntactically or otherwise invalid
error_rewrite(e, 'interpreter')
except errors.ConstantInferenceError as e:
# this is from trying to infer something as constant when it isn't
# or isn't supported as a constant
error_rewrite(e, 'constant_inference')
except Exception as e:
if config.SHOW_HELP:
if hasattr(e, 'patch_message'):
help_msg = errors.error_extras['reportable']
e.patch_message('\n'.join((str(e).rstrip(), help_msg)))
# ignore the FULL_TRACEBACKS config, this needs reporting!
raise e
finally:
self._types_active_call.clear()
return return_val
def inspect_llvm(self, signature=None):
"""Get the LLVM intermediate representation generated by compilation.
Parameters
----------
signature : tuple of numba types, optional
Specify a signature for which to obtain the LLVM IR. If None, the
IR is returned for all available signatures.
Returns
-------
llvm : dict[signature, str] or str
Either the LLVM IR string for the specified signature, or, if no
signature was given, a dictionary mapping signatures to LLVM IR
strings.
"""
if signature is not None:
lib = self.overloads[signature].library
return lib.get_llvm_str()
return dict((sig, self.inspect_llvm(sig)) for sig in self.signatures)
def inspect_asm(self, signature=None):
"""Get the generated assembly code.
Parameters
----------
signature : tuple of numba types, optional
Specify a signature for which to obtain the assembly code. If
None, the assembly code is returned for all available signatures.
Returns
-------
asm : dict[signature, str] or str
Either the assembly code for the specified signature, or, if no
signature was given, a dictionary mapping signatures to assembly
code.
"""
if signature is not None:
lib = self.overloads[signature].library
return lib.get_asm_str()
return dict((sig, self.inspect_asm(sig)) for sig in self.signatures)
def inspect_types(self, file=None, signature=None,
pretty=False, style='default', **kwargs):
"""Print/return Numba intermediate representation (IR)-annotated code.
Parameters
----------
file : file-like object, optional
File to which to print. Defaults to sys.stdout if None. Must be
None if ``pretty=True``.
signature : tuple of numba types, optional
Print/return the intermediate representation for only the given
signature. If None, the IR is printed for all available signatures.
pretty : bool, optional
If True, an Annotate object will be returned that can render the
IR with color highlighting in Jupyter and IPython. ``file`` must
be None if ``pretty`` is True. Additionally, the ``pygments``
library must be installed for ``pretty=True``.
style : str, optional
Choose a style for rendering. Ignored if ``pretty`` is ``False``.
This is directly consumed by ``pygments`` formatters. To see a
list of available styles, import ``pygments`` and run
``list(pygments.styles.get_all_styles())``.
Returns
-------
annotated : Annotate object, optional
Only returned if ``pretty=True``, otherwise this function is only
used for its printing side effect. If ``pretty=True``, an Annotate
object is returned that can render itself in Jupyter and IPython.
"""
overloads = self.overloads
if signature is not None:
overloads = {signature: self.overloads[signature]}
if not pretty:
if file is None:
file = sys.stdout
for ver, res in overloads.items():
print("%s %s" % (self.py_func.__name__, ver), file=file)
print('-' * 80, file=file)
print(res.type_annotation, file=file)
print('=' * 80, file=file)
else:
if file is not None:
raise ValueError("`file` must be None if `pretty=True`")
from numba.core.annotations.pretty_annotate import Annotate
return Annotate(self, signature=signature, style=style)
def inspect_cfg(self, signature=None, show_wrapper=None, **kwargs):
"""
For inspecting the CFG of the function.
By default the CFG of the user function is shown. The *show_wrapper*
option can be set to "python" or "cfunc" to show the python wrapper
function or the *cfunc* wrapper function, respectively.
Parameters accepted in kwargs
-----------------------------
filename : string, optional
the name of the output file, if given this will write the output to
filename
view : bool, optional
whether to immediately view the optional output file
highlight : bool, set, dict, optional
what, if anything, to highlight, options are:
{ incref : bool, # highlight NRT_incref calls
decref : bool, # highlight NRT_decref calls
returns : bool, # highlight exits which are normal returns
raises : bool, # highlight exits which are from raise
meminfo : bool, # highlight calls to NRT*meminfo
branches : bool, # highlight true/false branches
}
Default is True which sets all of the above to True. Supplying a set
of strings is also accepted, these are interpreted as key:True with
respect to the above dictionary. e.g. {'incref', 'decref'} would
switch on highlighting on increfs and decrefs.
interleave: bool, set, dict, optional
what, if anything, to interleave in the LLVM IR, options are:
{ python: bool # interleave python source code with the LLVM IR
lineinfo: bool # interleave line information markers with the LLVM
# IR
}
Default is True which sets all of the above to True. Supplying a set
of strings is also accepted, these are interpreted as key:True with
respect to the above dictionary. e.g. {'python',} would
switch on interleaving of python source code in the LLVM IR.
strip_ir : bool, optional
Default is False. If set to True all LLVM IR that is superfluous to
that requested in kwarg `highlight` will be removed.
show_key : bool, optional
Default is True. Create a "key" for the highlighting in the rendered
CFG.
fontsize : int, optional
Default is 8. Set the fontsize in the output to this value.
"""
if signature is not None:
cres = self.overloads[signature]
lib = cres.library
if show_wrapper == 'python':
fname = cres.fndesc.llvm_cpython_wrapper_name
elif show_wrapper == 'cfunc':
fname = cres.fndesc.llvm_cfunc_wrapper_name
else:
fname = cres.fndesc.mangled_name
return lib.get_function_cfg(fname, py_func=self.py_func, **kwargs)
return dict((sig, self.inspect_cfg(sig, show_wrapper=show_wrapper))
for sig in self.signatures)
def inspect_disasm_cfg(self, signature=None):
"""
For inspecting the CFG of the disassembly of the function.
Requires python package: r2pipe
Requires radare2 binary on $PATH.
Notebook rendering requires python package: graphviz
signature : tuple of Numba types, optional
Print/return the disassembly CFG for only the given signatures.
If None, the IR is printed for all available signatures.
"""
if signature is not None:
cres = self.overloads[signature]
lib = cres.library
return lib.get_disasm_cfg(cres.fndesc.mangled_name)
return dict((sig, self.inspect_disasm_cfg(sig))
for sig in self.signatures)
def get_annotation_info(self, signature=None):
"""
Gets the annotation information for the function specified by
signature. If no signature is supplied a dictionary of signature to
annotation information is returned.
"""
signatures = self.signatures if signature is None else [signature]
out = collections.OrderedDict()
for sig in signatures:
cres = self.overloads[sig]
ta = cres.type_annotation
key = (ta.func_id.filename + ':' + str(ta.func_id.firstlineno + 1),
ta.signature)
out[key] = ta.annotate_raw()[key]
return out
def _explain_ambiguous(self, *args, **kws):
"""
Callback for the C _Dispatcher object.
"""
assert not kws, "kwargs not handled"
args = tuple([self.typeof_pyval(a) for a in args])
# The order here must be deterministic for testing purposes, which
# is ensured by the OrderedDict.
sigs = self.nopython_signatures
# This will raise
self.typingctx.resolve_overload(self.py_func, sigs, args, kws,
allow_ambiguous=False)
def _explain_matching_error(self, *args, **kws):
"""
Callback for the C _Dispatcher object.
"""
assert not kws, "kwargs not handled"
args = [self.typeof_pyval(a) for a in args]
msg = ("No matching definition for argument type(s) %s"
% ', '.join(map(str, args)))
raise TypeError(msg)
def _search_new_conversions(self, *args, **kws):
"""
Callback for the C _Dispatcher object.
Search for approximately matching signatures for the given arguments,
and ensure the corresponding conversions are registered in the C++
type manager.
"""
assert not kws, "kwargs not handled"
args = [self.typeof_pyval(a) for a in args]
found = False
for sig in self.nopython_signatures:
conv = self.typingctx.install_possible_conversions(args, sig.args)
if conv:
found = True
return found
def __repr__(self):
return "%s(%s)" % (type(self).__name__, self.py_func)
def typeof_pyval(self, val):
"""
Resolve the Numba type of Python value *val*.
This is called from numba._dispatcher as a fallback if the native code
cannot decide the type.
"""
try:
tp = typeof(val, Purpose.argument)
except ValueError:
tp = types.pyobject
else:
if tp is None:
tp = types.pyobject
self._types_active_call.add(tp)
return tp
def _callback_add_timer(self, duration, cres, lock_name):
md = cres.metadata
# md can be None when code is loaded from cache
if md is not None:
timers = md.setdefault("timers", {})
if lock_name not in timers:
# Only write if the metadata does not exist
timers[lock_name] = duration
else:
msg = f"'{lock_name} metadata is already defined."
raise AssertionError(msg)
def _callback_add_compiler_timer(self, duration, cres):
return self._callback_add_timer(duration, cres,
lock_name="compiler_lock")
def _callback_add_llvm_timer(self, duration, cres):
return self._callback_add_timer(duration, cres,
lock_name="llvm_lock")
class _MemoMixin:
__uuid = None
# A {uuid -> instance} mapping, for deserialization
_memo = weakref.WeakValueDictionary()
# hold refs to last N functions deserialized, retaining them in _memo
# regardless of whether there is another reference
_recent = collections.deque(maxlen=config.FUNCTION_CACHE_SIZE)
@property
def _uuid(self):
"""
An instance-specific UUID, to avoid multiple deserializations of
a given instance.
Note: this is lazily-generated, for performance reasons.
"""
u = self.__uuid
if u is None:
u = str(uuid.uuid4())
self._set_uuid(u)
return u
def _set_uuid(self, u):
assert self.__uuid is None
self.__uuid = u
self._memo[u] = self
self._recent.append(self)
class Dispatcher(serialize.ReduceMixin, _MemoMixin, _DispatcherBase):
"""
Implementation of user-facing dispatcher objects (i.e. created using
the @jit decorator).
This is an abstract base class. Subclasses should define the targetdescr
class attribute.
"""
_fold_args = True
__numba__ = 'py_func'
def __init__(self, py_func, locals=None, targetoptions=None,
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.
pipeline_class: type numba.compiler.CompilerBase
The compiler pipeline type.
"""
if locals is None:
locals = {}
if targetoptions is None:
targetoptions = {}
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 = _FunctionCompiler
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)
def dump(self, tab=''):
print(f'{tab}DUMP {type(self).__name__}[{self.py_func.__name__}'
f', type code={self._type._code}]')
for cres in self.overloads.values():
cres.dump(tab=tab + ' ')
print(f'{tab}END DUMP {type(self).__name__}[{self.py_func.__name__}]')
@property
def _numba_type_(self):
return types.Dispatcher(self)
def enable_caching(self):
self._cache = FunctionCache(self.py_func)
def __get__(self, obj, objtype=None):
'''Allow a JIT function to be bound as a method to an object'''
if obj is None: # Unbound method
return self
else: # Bound method
return pytypes.MethodType(self, obj)
def _reduce_states(self):
"""
Reduce the instance for pickling. This will serialize
the original function as well the compilation options and
compiled signatures, but not the compiled code itself.
NOTE: part of ReduceMixin protocol
"""
if self._can_compile:
sigs = []
else:
sigs = [cr.signature for cr in self.overloads.values()]
return dict(
uuid=str(self._uuid),
py_func=self.py_func,
locals=self.locals,
targetoptions=self.targetoptions,
can_compile=self._can_compile,
sigs=sigs,
)
@classmethod
def _rebuild(cls, uuid, py_func, locals, targetoptions,
can_compile, sigs):
"""
Rebuild an Dispatcher instance after it was __reduce__'d.
NOTE: part of ReduceMixin protocol
"""
try:
return cls._memo[uuid]
except KeyError:
pass
self = cls(py_func, locals, targetoptions)
# Make sure this deserialization will be merged with subsequent ones
self._set_uuid(uuid)
for sig in sigs:
self.compile(sig)
self._can_compile = can_compile
return self
def compile(self, sig):
with ExitStack() as scope:
cres = None
def cb_compiler(dur):
if cres is not None:
self._callback_add_compiler_timer(dur, cres)
def cb_llvm(dur):
if cres is not None:
self._callback_add_llvm_timer(dur, cres)
scope.enter_context(ev.install_timer("numba:compiler_lock",
cb_compiler))
scope.enter_context(ev.install_timer("numba:llvm_lock", cb_llvm))
scope.enter_context(global_compiler_lock)
if not self._can_compile:
raise RuntimeError("compilation disabled")
# Use counter to track recursion compilation depth
with self._compiling_counter:
args, return_type = sigutils.normalize_signature(sig)
# Don't recompile if signature already exists
existing = self.overloads.get(tuple(args))
if existing is not None:
return existing.entry_point
# Try to load from disk cache
cres = self._cache.load_overload(sig, self.targetctx)
if cres is not None:
self._cache_hits[sig] += 1
# XXX fold this in add_overload()? (also see compiler.py)
if not cres.objectmode:
self.targetctx.insert_user_function(cres.entry_point,
cres.fndesc,
[cres.library])
self.add_overload(cres)
return cres.entry_point
self._cache_misses[sig] += 1
ev_details = dict(
dispatcher=self,
args=args,
return_type=return_type,
)
with ev.trigger_event("numba:compile", data=ev_details):
try:
cres = self._compiler.compile(args, return_type)
except errors.ForceLiteralArg as e:
def folded(args, kws):
return self._compiler.fold_argument_types(args,
kws)[1]
raise e.bind_fold_arguments(folded)
self.add_overload(cres)
self._cache.save_overload(sig, cres)
return cres.entry_point
def get_compile_result(self, sig):
"""Compile (if needed) and return the compilation result with the
given signature.
Returns ``CompileResult``.
Raises ``NumbaError`` if the signature is incompatible.
"""
atypes = tuple(sig.args)
if atypes not in self.overloads:
if self._can_compile:
# Compiling may raise any NumbaError
self.compile(atypes)
else:
msg = f"{sig} not available and compilation disabled"
raise errors.TypingError(msg)
return self.overloads[atypes]
def recompile(self):
"""
Recompile all signatures afresh.
"""
sigs = list(self.overloads)
old_can_compile = self._can_compile
# Ensure the old overloads are disposed of,
# including compiled functions.
self._make_finalizer()()
self._reset_overloads()
self._cache.flush()
self._can_compile = True
try:
for sig in sigs:
self.compile(sig)
finally:
self._can_compile = old_can_compile
@property
def stats(self):
return _CompileStats(
cache_path=self._cache.cache_path,
cache_hits=self._cache_hits,
cache_misses=self._cache_misses,
)
def parallel_diagnostics(self, signature=None, level=1):
"""
Print parallel diagnostic information for the given signature. If no
signature is present it is printed for all known signatures. level is
used to adjust the verbosity, level=1 (default) is minimal verbosity,
and 2, 3, and 4 provide increasing levels of verbosity.
"""
def dump(sig):
ol = self.overloads[sig]
pfdiag = ol.metadata.get('parfor_diagnostics', None)
if pfdiag is None:
msg = "No parfors diagnostic available, is 'parallel=True' set?"
raise ValueError(msg)
pfdiag.dump(level)
if signature is not None:
dump(signature)
else:
[dump(sig) for sig in self.signatures]
def get_metadata(self, signature=None):
"""
Obtain the compilation metadata for a given signature.
"""
if signature is not None:
return self.overloads[signature].metadata
else:
return dict(
(sig,self.overloads[sig].metadata) for sig in self.signatures
)
def get_function_type(self):
"""Return unique function type of dispatcher when possible, otherwise
return None.
A Dispatcher instance has unique function type when it
contains exactly one compilation result and its compilation
has been disabled (via its disable_compile method).
"""
if not self._can_compile and len(self.overloads) == 1:
cres = tuple(self.overloads.values())[0]