numba-cuda 0.22.0__cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of numba-cuda might be problematic. Click here for more details.
- _numba_cuda_redirector.pth +4 -0
- _numba_cuda_redirector.py +89 -0
- numba_cuda/VERSION +1 -0
- numba_cuda/__init__.py +6 -0
- numba_cuda/_version.py +11 -0
- numba_cuda/numba/cuda/__init__.py +70 -0
- numba_cuda/numba/cuda/_internal/cuda_bf16.py +16394 -0
- numba_cuda/numba/cuda/_internal/cuda_fp16.py +8112 -0
- numba_cuda/numba/cuda/api.py +580 -0
- numba_cuda/numba/cuda/api_util.py +76 -0
- numba_cuda/numba/cuda/args.py +72 -0
- numba_cuda/numba/cuda/bf16.py +397 -0
- numba_cuda/numba/cuda/cache_hints.py +287 -0
- numba_cuda/numba/cuda/cext/__init__.py +2 -0
- numba_cuda/numba/cuda/cext/_devicearray.cpp +159 -0
- numba_cuda/numba/cuda/cext/_devicearray.cpython-312-aarch64-linux-gnu.so +0 -0
- numba_cuda/numba/cuda/cext/_devicearray.h +29 -0
- numba_cuda/numba/cuda/cext/_dispatcher.cpp +1098 -0
- numba_cuda/numba/cuda/cext/_dispatcher.cpython-312-aarch64-linux-gnu.so +0 -0
- numba_cuda/numba/cuda/cext/_hashtable.cpp +532 -0
- numba_cuda/numba/cuda/cext/_hashtable.h +135 -0
- numba_cuda/numba/cuda/cext/_helperlib.c +71 -0
- numba_cuda/numba/cuda/cext/_helperlib.cpython-312-aarch64-linux-gnu.so +0 -0
- numba_cuda/numba/cuda/cext/_helpermod.c +82 -0
- numba_cuda/numba/cuda/cext/_pymodule.h +38 -0
- numba_cuda/numba/cuda/cext/_typeconv.cpp +206 -0
- numba_cuda/numba/cuda/cext/_typeconv.cpython-312-aarch64-linux-gnu.so +0 -0
- numba_cuda/numba/cuda/cext/_typeof.cpp +1159 -0
- numba_cuda/numba/cuda/cext/_typeof.h +19 -0
- numba_cuda/numba/cuda/cext/capsulethunk.h +111 -0
- numba_cuda/numba/cuda/cext/mviewbuf.c +385 -0
- numba_cuda/numba/cuda/cext/mviewbuf.cpython-312-aarch64-linux-gnu.so +0 -0
- numba_cuda/numba/cuda/cext/typeconv.cpp +212 -0
- numba_cuda/numba/cuda/cext/typeconv.hpp +101 -0
- numba_cuda/numba/cuda/cg.py +67 -0
- numba_cuda/numba/cuda/cgutils.py +1294 -0
- numba_cuda/numba/cuda/cloudpickle/__init__.py +21 -0
- numba_cuda/numba/cuda/cloudpickle/cloudpickle.py +1598 -0
- numba_cuda/numba/cuda/cloudpickle/cloudpickle_fast.py +17 -0
- numba_cuda/numba/cuda/codegen.py +541 -0
- numba_cuda/numba/cuda/compiler.py +1396 -0
- numba_cuda/numba/cuda/core/analysis.py +758 -0
- numba_cuda/numba/cuda/core/annotations/__init__.py +0 -0
- numba_cuda/numba/cuda/core/annotations/pretty_annotate.py +288 -0
- numba_cuda/numba/cuda/core/annotations/type_annotations.py +305 -0
- numba_cuda/numba/cuda/core/base.py +1332 -0
- numba_cuda/numba/cuda/core/boxing.py +1411 -0
- numba_cuda/numba/cuda/core/bytecode.py +728 -0
- numba_cuda/numba/cuda/core/byteflow.py +2346 -0
- numba_cuda/numba/cuda/core/caching.py +744 -0
- numba_cuda/numba/cuda/core/callconv.py +392 -0
- numba_cuda/numba/cuda/core/codegen.py +171 -0
- numba_cuda/numba/cuda/core/compiler.py +199 -0
- numba_cuda/numba/cuda/core/compiler_lock.py +85 -0
- numba_cuda/numba/cuda/core/compiler_machinery.py +497 -0
- numba_cuda/numba/cuda/core/config.py +650 -0
- numba_cuda/numba/cuda/core/consts.py +124 -0
- numba_cuda/numba/cuda/core/controlflow.py +989 -0
- numba_cuda/numba/cuda/core/entrypoints.py +57 -0
- numba_cuda/numba/cuda/core/environment.py +66 -0
- numba_cuda/numba/cuda/core/errors.py +917 -0
- numba_cuda/numba/cuda/core/event.py +511 -0
- numba_cuda/numba/cuda/core/funcdesc.py +330 -0
- numba_cuda/numba/cuda/core/generators.py +387 -0
- numba_cuda/numba/cuda/core/imputils.py +509 -0
- numba_cuda/numba/cuda/core/inline_closurecall.py +1787 -0
- numba_cuda/numba/cuda/core/interpreter.py +3617 -0
- numba_cuda/numba/cuda/core/ir.py +1812 -0
- numba_cuda/numba/cuda/core/ir_utils.py +2638 -0
- numba_cuda/numba/cuda/core/optional.py +129 -0
- numba_cuda/numba/cuda/core/options.py +262 -0
- numba_cuda/numba/cuda/core/postproc.py +249 -0
- numba_cuda/numba/cuda/core/pythonapi.py +1859 -0
- numba_cuda/numba/cuda/core/registry.py +46 -0
- numba_cuda/numba/cuda/core/removerefctpass.py +123 -0
- numba_cuda/numba/cuda/core/rewrites/__init__.py +26 -0
- numba_cuda/numba/cuda/core/rewrites/ir_print.py +91 -0
- numba_cuda/numba/cuda/core/rewrites/registry.py +104 -0
- numba_cuda/numba/cuda/core/rewrites/static_binop.py +41 -0
- numba_cuda/numba/cuda/core/rewrites/static_getitem.py +189 -0
- numba_cuda/numba/cuda/core/rewrites/static_raise.py +100 -0
- numba_cuda/numba/cuda/core/sigutils.py +68 -0
- numba_cuda/numba/cuda/core/ssa.py +498 -0
- numba_cuda/numba/cuda/core/targetconfig.py +330 -0
- numba_cuda/numba/cuda/core/tracing.py +231 -0
- numba_cuda/numba/cuda/core/transforms.py +956 -0
- numba_cuda/numba/cuda/core/typed_passes.py +867 -0
- numba_cuda/numba/cuda/core/typeinfer.py +1950 -0
- numba_cuda/numba/cuda/core/unsafe/__init__.py +0 -0
- numba_cuda/numba/cuda/core/unsafe/bytes.py +67 -0
- numba_cuda/numba/cuda/core/unsafe/eh.py +67 -0
- numba_cuda/numba/cuda/core/unsafe/refcount.py +98 -0
- numba_cuda/numba/cuda/core/untyped_passes.py +1979 -0
- numba_cuda/numba/cuda/cpython/builtins.py +1153 -0
- numba_cuda/numba/cuda/cpython/charseq.py +1218 -0
- numba_cuda/numba/cuda/cpython/cmathimpl.py +560 -0
- numba_cuda/numba/cuda/cpython/enumimpl.py +103 -0
- numba_cuda/numba/cuda/cpython/iterators.py +167 -0
- numba_cuda/numba/cuda/cpython/listobj.py +1326 -0
- numba_cuda/numba/cuda/cpython/mathimpl.py +499 -0
- numba_cuda/numba/cuda/cpython/numbers.py +1475 -0
- numba_cuda/numba/cuda/cpython/rangeobj.py +289 -0
- numba_cuda/numba/cuda/cpython/slicing.py +322 -0
- numba_cuda/numba/cuda/cpython/tupleobj.py +456 -0
- numba_cuda/numba/cuda/cpython/unicode.py +2865 -0
- numba_cuda/numba/cuda/cpython/unicode_support.py +1597 -0
- numba_cuda/numba/cuda/cpython/unsafe/__init__.py +0 -0
- numba_cuda/numba/cuda/cpython/unsafe/numbers.py +64 -0
- numba_cuda/numba/cuda/cpython/unsafe/tuple.py +92 -0
- numba_cuda/numba/cuda/cuda_paths.py +691 -0
- numba_cuda/numba/cuda/cudadecl.py +543 -0
- numba_cuda/numba/cuda/cudadrv/__init__.py +14 -0
- numba_cuda/numba/cuda/cudadrv/devicearray.py +954 -0
- numba_cuda/numba/cuda/cudadrv/devices.py +249 -0
- numba_cuda/numba/cuda/cudadrv/driver.py +3238 -0
- numba_cuda/numba/cuda/cudadrv/drvapi.py +435 -0
- numba_cuda/numba/cuda/cudadrv/dummyarray.py +562 -0
- numba_cuda/numba/cuda/cudadrv/enums.py +613 -0
- numba_cuda/numba/cuda/cudadrv/error.py +48 -0
- numba_cuda/numba/cuda/cudadrv/libs.py +220 -0
- numba_cuda/numba/cuda/cudadrv/linkable_code.py +184 -0
- numba_cuda/numba/cuda/cudadrv/mappings.py +14 -0
- numba_cuda/numba/cuda/cudadrv/ndarray.py +26 -0
- numba_cuda/numba/cuda/cudadrv/nvrtc.py +193 -0
- numba_cuda/numba/cuda/cudadrv/nvvm.py +756 -0
- numba_cuda/numba/cuda/cudadrv/rtapi.py +13 -0
- numba_cuda/numba/cuda/cudadrv/runtime.py +34 -0
- numba_cuda/numba/cuda/cudaimpl.py +983 -0
- numba_cuda/numba/cuda/cudamath.py +149 -0
- numba_cuda/numba/cuda/datamodel/__init__.py +7 -0
- numba_cuda/numba/cuda/datamodel/cuda_manager.py +66 -0
- numba_cuda/numba/cuda/datamodel/cuda_models.py +1446 -0
- numba_cuda/numba/cuda/datamodel/cuda_packer.py +224 -0
- numba_cuda/numba/cuda/datamodel/cuda_registry.py +22 -0
- numba_cuda/numba/cuda/datamodel/cuda_testing.py +153 -0
- numba_cuda/numba/cuda/datamodel/manager.py +11 -0
- numba_cuda/numba/cuda/datamodel/models.py +9 -0
- numba_cuda/numba/cuda/datamodel/packer.py +9 -0
- numba_cuda/numba/cuda/datamodel/registry.py +11 -0
- numba_cuda/numba/cuda/datamodel/testing.py +11 -0
- numba_cuda/numba/cuda/debuginfo.py +997 -0
- numba_cuda/numba/cuda/decorators.py +294 -0
- numba_cuda/numba/cuda/descriptor.py +35 -0
- numba_cuda/numba/cuda/device_init.py +155 -0
- numba_cuda/numba/cuda/deviceufunc.py +1021 -0
- numba_cuda/numba/cuda/dispatcher.py +2463 -0
- numba_cuda/numba/cuda/errors.py +72 -0
- numba_cuda/numba/cuda/extending.py +697 -0
- numba_cuda/numba/cuda/flags.py +178 -0
- numba_cuda/numba/cuda/fp16.py +357 -0
- numba_cuda/numba/cuda/include/12/cuda_bf16.h +5118 -0
- numba_cuda/numba/cuda/include/12/cuda_bf16.hpp +3865 -0
- numba_cuda/numba/cuda/include/12/cuda_fp16.h +5363 -0
- numba_cuda/numba/cuda/include/12/cuda_fp16.hpp +3483 -0
- numba_cuda/numba/cuda/include/13/cuda_bf16.h +5118 -0
- numba_cuda/numba/cuda/include/13/cuda_bf16.hpp +3865 -0
- numba_cuda/numba/cuda/include/13/cuda_fp16.h +5363 -0
- numba_cuda/numba/cuda/include/13/cuda_fp16.hpp +3483 -0
- numba_cuda/numba/cuda/initialize.py +24 -0
- numba_cuda/numba/cuda/intrinsics.py +531 -0
- numba_cuda/numba/cuda/itanium_mangler.py +214 -0
- numba_cuda/numba/cuda/kernels/__init__.py +2 -0
- numba_cuda/numba/cuda/kernels/reduction.py +265 -0
- numba_cuda/numba/cuda/kernels/transpose.py +65 -0
- numba_cuda/numba/cuda/libdevice.py +3386 -0
- numba_cuda/numba/cuda/libdevicedecl.py +20 -0
- numba_cuda/numba/cuda/libdevicefuncs.py +1060 -0
- numba_cuda/numba/cuda/libdeviceimpl.py +88 -0
- numba_cuda/numba/cuda/locks.py +19 -0
- numba_cuda/numba/cuda/lowering.py +1980 -0
- numba_cuda/numba/cuda/mathimpl.py +374 -0
- numba_cuda/numba/cuda/memory_management/__init__.py +4 -0
- numba_cuda/numba/cuda/memory_management/memsys.cu +99 -0
- numba_cuda/numba/cuda/memory_management/memsys.cuh +22 -0
- numba_cuda/numba/cuda/memory_management/nrt.cu +212 -0
- numba_cuda/numba/cuda/memory_management/nrt.cuh +48 -0
- numba_cuda/numba/cuda/memory_management/nrt.py +390 -0
- numba_cuda/numba/cuda/memory_management/nrt_context.py +438 -0
- numba_cuda/numba/cuda/misc/appdirs.py +594 -0
- numba_cuda/numba/cuda/misc/cffiimpl.py +24 -0
- numba_cuda/numba/cuda/misc/coverage_support.py +43 -0
- numba_cuda/numba/cuda/misc/dump_style.py +41 -0
- numba_cuda/numba/cuda/misc/findlib.py +75 -0
- numba_cuda/numba/cuda/misc/firstlinefinder.py +96 -0
- numba_cuda/numba/cuda/misc/gdb_hook.py +240 -0
- numba_cuda/numba/cuda/misc/literal.py +28 -0
- numba_cuda/numba/cuda/misc/llvm_pass_timings.py +412 -0
- numba_cuda/numba/cuda/misc/special.py +94 -0
- numba_cuda/numba/cuda/models.py +56 -0
- numba_cuda/numba/cuda/np/arraymath.py +5130 -0
- numba_cuda/numba/cuda/np/arrayobj.py +7635 -0
- numba_cuda/numba/cuda/np/extensions.py +11 -0
- numba_cuda/numba/cuda/np/linalg.py +3087 -0
- numba_cuda/numba/cuda/np/math/__init__.py +0 -0
- numba_cuda/numba/cuda/np/math/cmathimpl.py +558 -0
- numba_cuda/numba/cuda/np/math/mathimpl.py +487 -0
- numba_cuda/numba/cuda/np/math/numbers.py +1461 -0
- numba_cuda/numba/cuda/np/npdatetime.py +969 -0
- numba_cuda/numba/cuda/np/npdatetime_helpers.py +217 -0
- numba_cuda/numba/cuda/np/npyfuncs.py +1808 -0
- numba_cuda/numba/cuda/np/npyimpl.py +1027 -0
- numba_cuda/numba/cuda/np/numpy_support.py +798 -0
- numba_cuda/numba/cuda/np/polynomial/__init__.py +4 -0
- numba_cuda/numba/cuda/np/polynomial/polynomial_core.py +242 -0
- numba_cuda/numba/cuda/np/polynomial/polynomial_functions.py +380 -0
- numba_cuda/numba/cuda/np/ufunc/__init__.py +4 -0
- numba_cuda/numba/cuda/np/ufunc/decorators.py +203 -0
- numba_cuda/numba/cuda/np/ufunc/sigparse.py +68 -0
- numba_cuda/numba/cuda/np/ufunc/ufuncbuilder.py +65 -0
- numba_cuda/numba/cuda/np/ufunc_db.py +1282 -0
- numba_cuda/numba/cuda/np/unsafe/__init__.py +0 -0
- numba_cuda/numba/cuda/np/unsafe/ndarray.py +84 -0
- numba_cuda/numba/cuda/nvvmutils.py +254 -0
- numba_cuda/numba/cuda/printimpl.py +126 -0
- numba_cuda/numba/cuda/random.py +308 -0
- numba_cuda/numba/cuda/reshape_funcs.cu +156 -0
- numba_cuda/numba/cuda/serialize.py +267 -0
- numba_cuda/numba/cuda/simulator/__init__.py +63 -0
- numba_cuda/numba/cuda/simulator/_internal/__init__.py +4 -0
- numba_cuda/numba/cuda/simulator/_internal/cuda_bf16.py +2 -0
- numba_cuda/numba/cuda/simulator/api.py +179 -0
- numba_cuda/numba/cuda/simulator/bf16.py +4 -0
- numba_cuda/numba/cuda/simulator/compiler.py +38 -0
- numba_cuda/numba/cuda/simulator/cudadrv/__init__.py +11 -0
- numba_cuda/numba/cuda/simulator/cudadrv/devicearray.py +462 -0
- numba_cuda/numba/cuda/simulator/cudadrv/devices.py +122 -0
- numba_cuda/numba/cuda/simulator/cudadrv/driver.py +66 -0
- numba_cuda/numba/cuda/simulator/cudadrv/drvapi.py +7 -0
- numba_cuda/numba/cuda/simulator/cudadrv/dummyarray.py +7 -0
- numba_cuda/numba/cuda/simulator/cudadrv/error.py +10 -0
- numba_cuda/numba/cuda/simulator/cudadrv/libs.py +10 -0
- numba_cuda/numba/cuda/simulator/cudadrv/linkable_code.py +61 -0
- numba_cuda/numba/cuda/simulator/cudadrv/nvrtc.py +11 -0
- numba_cuda/numba/cuda/simulator/cudadrv/nvvm.py +32 -0
- numba_cuda/numba/cuda/simulator/cudadrv/runtime.py +22 -0
- numba_cuda/numba/cuda/simulator/dispatcher.py +11 -0
- numba_cuda/numba/cuda/simulator/kernel.py +320 -0
- numba_cuda/numba/cuda/simulator/kernelapi.py +509 -0
- numba_cuda/numba/cuda/simulator/memory_management/__init__.py +4 -0
- numba_cuda/numba/cuda/simulator/memory_management/nrt.py +21 -0
- numba_cuda/numba/cuda/simulator/reduction.py +19 -0
- numba_cuda/numba/cuda/simulator/tests/support.py +4 -0
- numba_cuda/numba/cuda/simulator/vector_types.py +65 -0
- numba_cuda/numba/cuda/simulator_init.py +18 -0
- numba_cuda/numba/cuda/stubs.py +624 -0
- numba_cuda/numba/cuda/target.py +505 -0
- numba_cuda/numba/cuda/testing.py +347 -0
- numba_cuda/numba/cuda/tests/__init__.py +62 -0
- numba_cuda/numba/cuda/tests/benchmarks/__init__.py +0 -0
- numba_cuda/numba/cuda/tests/benchmarks/test_kernel_launch.py +119 -0
- numba_cuda/numba/cuda/tests/cloudpickle_main_class.py +9 -0
- numba_cuda/numba/cuda/tests/core/serialize_usecases.py +113 -0
- numba_cuda/numba/cuda/tests/core/test_itanium_mangler.py +83 -0
- numba_cuda/numba/cuda/tests/core/test_serialize.py +371 -0
- numba_cuda/numba/cuda/tests/cudadrv/__init__.py +9 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_array_attr.py +147 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_context_stack.py +161 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_cuda_array_slicing.py +397 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_cuda_auto_context.py +24 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_cuda_devicerecord.py +180 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_cuda_driver.py +313 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_cuda_memory.py +191 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_cuda_ndarray.py +621 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_deallocations.py +247 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_detect.py +100 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_emm_plugins.py +200 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_events.py +53 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_host_alloc.py +72 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_init.py +138 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_inline_ptx.py +43 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_is_fp16.py +15 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_linkable_code.py +58 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_linker.py +348 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_managed_alloc.py +128 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_module_callbacks.py +301 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_nvjitlink.py +174 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_nvrtc.py +28 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_nvvm_driver.py +185 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_pinned.py +39 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_profiler.py +23 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_reset_device.py +38 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_runtime.py +48 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_select_device.py +44 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_streams.py +127 -0
- numba_cuda/numba/cuda/tests/cudapy/__init__.py +9 -0
- numba_cuda/numba/cuda/tests/cudapy/cache_usecases.py +231 -0
- numba_cuda/numba/cuda/tests/cudapy/cache_with_cpu_usecases.py +50 -0
- numba_cuda/numba/cuda/tests/cudapy/cg_cache_usecases.py +36 -0
- numba_cuda/numba/cuda/tests/cudapy/complex_usecases.py +116 -0
- numba_cuda/numba/cuda/tests/cudapy/enum_usecases.py +59 -0
- numba_cuda/numba/cuda/tests/cudapy/extensions_usecases.py +62 -0
- numba_cuda/numba/cuda/tests/cudapy/jitlink.ptx +28 -0
- numba_cuda/numba/cuda/tests/cudapy/overload_usecases.py +33 -0
- numba_cuda/numba/cuda/tests/cudapy/recursion_usecases.py +104 -0
- numba_cuda/numba/cuda/tests/cudapy/test_alignment.py +47 -0
- numba_cuda/numba/cuda/tests/cudapy/test_analysis.py +1122 -0
- numba_cuda/numba/cuda/tests/cudapy/test_array.py +344 -0
- numba_cuda/numba/cuda/tests/cudapy/test_array_alignment.py +268 -0
- numba_cuda/numba/cuda/tests/cudapy/test_array_args.py +203 -0
- numba_cuda/numba/cuda/tests/cudapy/test_array_methods.py +63 -0
- numba_cuda/numba/cuda/tests/cudapy/test_array_reductions.py +360 -0
- numba_cuda/numba/cuda/tests/cudapy/test_atomics.py +1815 -0
- numba_cuda/numba/cuda/tests/cudapy/test_bfloat16.py +599 -0
- numba_cuda/numba/cuda/tests/cudapy/test_bfloat16_bindings.py +377 -0
- numba_cuda/numba/cuda/tests/cudapy/test_blackscholes.py +160 -0
- numba_cuda/numba/cuda/tests/cudapy/test_boolean.py +27 -0
- numba_cuda/numba/cuda/tests/cudapy/test_byteflow.py +98 -0
- numba_cuda/numba/cuda/tests/cudapy/test_cache_hints.py +210 -0
- numba_cuda/numba/cuda/tests/cudapy/test_caching.py +683 -0
- numba_cuda/numba/cuda/tests/cudapy/test_casting.py +265 -0
- numba_cuda/numba/cuda/tests/cudapy/test_cffi.py +42 -0
- numba_cuda/numba/cuda/tests/cudapy/test_compiler.py +718 -0
- numba_cuda/numba/cuda/tests/cudapy/test_complex.py +370 -0
- numba_cuda/numba/cuda/tests/cudapy/test_complex_kernel.py +23 -0
- numba_cuda/numba/cuda/tests/cudapy/test_const_string.py +142 -0
- numba_cuda/numba/cuda/tests/cudapy/test_constmem.py +178 -0
- numba_cuda/numba/cuda/tests/cudapy/test_cooperative_groups.py +193 -0
- numba_cuda/numba/cuda/tests/cudapy/test_copy_propagate.py +131 -0
- numba_cuda/numba/cuda/tests/cudapy/test_cuda_array_interface.py +438 -0
- numba_cuda/numba/cuda/tests/cudapy/test_cuda_jit_no_types.py +94 -0
- numba_cuda/numba/cuda/tests/cudapy/test_datetime.py +101 -0
- numba_cuda/numba/cuda/tests/cudapy/test_debug.py +105 -0
- numba_cuda/numba/cuda/tests/cudapy/test_debuginfo.py +978 -0
- numba_cuda/numba/cuda/tests/cudapy/test_debuginfo_types.py +476 -0
- numba_cuda/numba/cuda/tests/cudapy/test_device_func.py +500 -0
- numba_cuda/numba/cuda/tests/cudapy/test_dispatcher.py +820 -0
- numba_cuda/numba/cuda/tests/cudapy/test_enums.py +152 -0
- numba_cuda/numba/cuda/tests/cudapy/test_errors.py +111 -0
- numba_cuda/numba/cuda/tests/cudapy/test_exception.py +170 -0
- numba_cuda/numba/cuda/tests/cudapy/test_extending.py +1088 -0
- numba_cuda/numba/cuda/tests/cudapy/test_extending_types.py +71 -0
- numba_cuda/numba/cuda/tests/cudapy/test_fastmath.py +265 -0
- numba_cuda/numba/cuda/tests/cudapy/test_flow_control.py +1433 -0
- numba_cuda/numba/cuda/tests/cudapy/test_forall.py +57 -0
- numba_cuda/numba/cuda/tests/cudapy/test_freevar.py +34 -0
- numba_cuda/numba/cuda/tests/cudapy/test_frexp_ldexp.py +69 -0
- numba_cuda/numba/cuda/tests/cudapy/test_globals.py +62 -0
- numba_cuda/numba/cuda/tests/cudapy/test_gufunc.py +474 -0
- numba_cuda/numba/cuda/tests/cudapy/test_gufunc_scalar.py +167 -0
- numba_cuda/numba/cuda/tests/cudapy/test_gufunc_scheduling.py +92 -0
- numba_cuda/numba/cuda/tests/cudapy/test_idiv.py +39 -0
- numba_cuda/numba/cuda/tests/cudapy/test_inline.py +170 -0
- numba_cuda/numba/cuda/tests/cudapy/test_inspect.py +255 -0
- numba_cuda/numba/cuda/tests/cudapy/test_intrinsics.py +1219 -0
- numba_cuda/numba/cuda/tests/cudapy/test_ipc.py +263 -0
- numba_cuda/numba/cuda/tests/cudapy/test_ir.py +598 -0
- numba_cuda/numba/cuda/tests/cudapy/test_ir_utils.py +276 -0
- numba_cuda/numba/cuda/tests/cudapy/test_iterators.py +101 -0
- numba_cuda/numba/cuda/tests/cudapy/test_lang.py +68 -0
- numba_cuda/numba/cuda/tests/cudapy/test_laplace.py +123 -0
- numba_cuda/numba/cuda/tests/cudapy/test_libdevice.py +194 -0
- numba_cuda/numba/cuda/tests/cudapy/test_lineinfo.py +220 -0
- numba_cuda/numba/cuda/tests/cudapy/test_localmem.py +173 -0
- numba_cuda/numba/cuda/tests/cudapy/test_make_function_to_jit_function.py +364 -0
- numba_cuda/numba/cuda/tests/cudapy/test_mandel.py +47 -0
- numba_cuda/numba/cuda/tests/cudapy/test_math.py +842 -0
- numba_cuda/numba/cuda/tests/cudapy/test_matmul.py +76 -0
- numba_cuda/numba/cuda/tests/cudapy/test_minmax.py +78 -0
- numba_cuda/numba/cuda/tests/cudapy/test_montecarlo.py +25 -0
- numba_cuda/numba/cuda/tests/cudapy/test_multigpu.py +145 -0
- numba_cuda/numba/cuda/tests/cudapy/test_multiprocessing.py +39 -0
- numba_cuda/numba/cuda/tests/cudapy/test_multithreads.py +82 -0
- numba_cuda/numba/cuda/tests/cudapy/test_nondet.py +53 -0
- numba_cuda/numba/cuda/tests/cudapy/test_operator.py +504 -0
- numba_cuda/numba/cuda/tests/cudapy/test_optimization.py +93 -0
- numba_cuda/numba/cuda/tests/cudapy/test_overload.py +402 -0
- numba_cuda/numba/cuda/tests/cudapy/test_powi.py +128 -0
- numba_cuda/numba/cuda/tests/cudapy/test_print.py +193 -0
- numba_cuda/numba/cuda/tests/cudapy/test_py2_div_issue.py +37 -0
- numba_cuda/numba/cuda/tests/cudapy/test_random.py +117 -0
- numba_cuda/numba/cuda/tests/cudapy/test_record_dtype.py +614 -0
- numba_cuda/numba/cuda/tests/cudapy/test_recursion.py +130 -0
- numba_cuda/numba/cuda/tests/cudapy/test_reduction.py +94 -0
- numba_cuda/numba/cuda/tests/cudapy/test_retrieve_autoconverted_arrays.py +83 -0
- numba_cuda/numba/cuda/tests/cudapy/test_serialize.py +86 -0
- numba_cuda/numba/cuda/tests/cudapy/test_slicing.py +40 -0
- numba_cuda/numba/cuda/tests/cudapy/test_sm.py +457 -0
- numba_cuda/numba/cuda/tests/cudapy/test_sm_creation.py +233 -0
- numba_cuda/numba/cuda/tests/cudapy/test_ssa.py +454 -0
- numba_cuda/numba/cuda/tests/cudapy/test_stream_api.py +56 -0
- numba_cuda/numba/cuda/tests/cudapy/test_sync.py +277 -0
- numba_cuda/numba/cuda/tests/cudapy/test_tracing.py +200 -0
- numba_cuda/numba/cuda/tests/cudapy/test_transpose.py +90 -0
- numba_cuda/numba/cuda/tests/cudapy/test_typeconv.py +333 -0
- numba_cuda/numba/cuda/tests/cudapy/test_typeinfer.py +538 -0
- numba_cuda/numba/cuda/tests/cudapy/test_ufuncs.py +585 -0
- numba_cuda/numba/cuda/tests/cudapy/test_userexc.py +42 -0
- numba_cuda/numba/cuda/tests/cudapy/test_vector_type.py +485 -0
- numba_cuda/numba/cuda/tests/cudapy/test_vectorize.py +312 -0
- numba_cuda/numba/cuda/tests/cudapy/test_vectorize_complex.py +23 -0
- numba_cuda/numba/cuda/tests/cudapy/test_vectorize_decor.py +183 -0
- numba_cuda/numba/cuda/tests/cudapy/test_vectorize_device.py +40 -0
- numba_cuda/numba/cuda/tests/cudapy/test_vectorize_scalar_arg.py +40 -0
- numba_cuda/numba/cuda/tests/cudapy/test_warning.py +206 -0
- numba_cuda/numba/cuda/tests/cudapy/test_warp_ops.py +446 -0
- numba_cuda/numba/cuda/tests/cudasim/__init__.py +9 -0
- numba_cuda/numba/cuda/tests/cudasim/support.py +9 -0
- numba_cuda/numba/cuda/tests/cudasim/test_cudasim_issues.py +111 -0
- numba_cuda/numba/cuda/tests/data/__init__.py +2 -0
- numba_cuda/numba/cuda/tests/data/cta_barrier.cu +28 -0
- numba_cuda/numba/cuda/tests/data/cuda_include.cu +10 -0
- numba_cuda/numba/cuda/tests/data/error.cu +12 -0
- numba_cuda/numba/cuda/tests/data/include/add.cuh +8 -0
- numba_cuda/numba/cuda/tests/data/jitlink.cu +28 -0
- numba_cuda/numba/cuda/tests/data/jitlink.ptx +49 -0
- numba_cuda/numba/cuda/tests/data/warn.cu +12 -0
- numba_cuda/numba/cuda/tests/doc_examples/__init__.py +9 -0
- numba_cuda/numba/cuda/tests/doc_examples/ffi/__init__.py +2 -0
- numba_cuda/numba/cuda/tests/doc_examples/ffi/functions.cu +54 -0
- numba_cuda/numba/cuda/tests/doc_examples/ffi/include/mul.cuh +8 -0
- numba_cuda/numba/cuda/tests/doc_examples/ffi/saxpy.cu +14 -0
- numba_cuda/numba/cuda/tests/doc_examples/test_cg.py +86 -0
- numba_cuda/numba/cuda/tests/doc_examples/test_cpointer.py +68 -0
- numba_cuda/numba/cuda/tests/doc_examples/test_cpu_gpu_compat.py +81 -0
- numba_cuda/numba/cuda/tests/doc_examples/test_ffi.py +141 -0
- numba_cuda/numba/cuda/tests/doc_examples/test_laplace.py +160 -0
- numba_cuda/numba/cuda/tests/doc_examples/test_matmul.py +180 -0
- numba_cuda/numba/cuda/tests/doc_examples/test_montecarlo.py +119 -0
- numba_cuda/numba/cuda/tests/doc_examples/test_random.py +66 -0
- numba_cuda/numba/cuda/tests/doc_examples/test_reduction.py +80 -0
- numba_cuda/numba/cuda/tests/doc_examples/test_sessionize.py +206 -0
- numba_cuda/numba/cuda/tests/doc_examples/test_ufunc.py +53 -0
- numba_cuda/numba/cuda/tests/doc_examples/test_vecadd.py +76 -0
- numba_cuda/numba/cuda/tests/nocuda/__init__.py +9 -0
- numba_cuda/numba/cuda/tests/nocuda/test_dummyarray.py +452 -0
- numba_cuda/numba/cuda/tests/nocuda/test_function_resolution.py +48 -0
- numba_cuda/numba/cuda/tests/nocuda/test_import.py +63 -0
- numba_cuda/numba/cuda/tests/nocuda/test_library_lookup.py +252 -0
- numba_cuda/numba/cuda/tests/nocuda/test_nvvm.py +59 -0
- numba_cuda/numba/cuda/tests/nrt/__init__.py +9 -0
- numba_cuda/numba/cuda/tests/nrt/test_nrt.py +387 -0
- numba_cuda/numba/cuda/tests/nrt/test_nrt_refct.py +124 -0
- numba_cuda/numba/cuda/tests/support.py +900 -0
- numba_cuda/numba/cuda/typeconv/__init__.py +4 -0
- numba_cuda/numba/cuda/typeconv/castgraph.py +137 -0
- numba_cuda/numba/cuda/typeconv/rules.py +63 -0
- numba_cuda/numba/cuda/typeconv/typeconv.py +121 -0
- numba_cuda/numba/cuda/types/__init__.py +233 -0
- numba_cuda/numba/cuda/types/__init__.pyi +167 -0
- numba_cuda/numba/cuda/types/abstract.py +9 -0
- numba_cuda/numba/cuda/types/common.py +9 -0
- numba_cuda/numba/cuda/types/containers.py +9 -0
- numba_cuda/numba/cuda/types/cuda_abstract.py +533 -0
- numba_cuda/numba/cuda/types/cuda_common.py +110 -0
- numba_cuda/numba/cuda/types/cuda_containers.py +971 -0
- numba_cuda/numba/cuda/types/cuda_function_type.py +230 -0
- numba_cuda/numba/cuda/types/cuda_functions.py +798 -0
- numba_cuda/numba/cuda/types/cuda_iterators.py +120 -0
- numba_cuda/numba/cuda/types/cuda_misc.py +569 -0
- numba_cuda/numba/cuda/types/cuda_npytypes.py +690 -0
- numba_cuda/numba/cuda/types/cuda_scalars.py +280 -0
- numba_cuda/numba/cuda/types/ext_types.py +101 -0
- numba_cuda/numba/cuda/types/function_type.py +11 -0
- numba_cuda/numba/cuda/types/functions.py +9 -0
- numba_cuda/numba/cuda/types/iterators.py +9 -0
- numba_cuda/numba/cuda/types/misc.py +9 -0
- numba_cuda/numba/cuda/types/npytypes.py +9 -0
- numba_cuda/numba/cuda/types/scalars.py +9 -0
- numba_cuda/numba/cuda/typing/__init__.py +19 -0
- numba_cuda/numba/cuda/typing/arraydecl.py +939 -0
- numba_cuda/numba/cuda/typing/asnumbatype.py +130 -0
- numba_cuda/numba/cuda/typing/bufproto.py +70 -0
- numba_cuda/numba/cuda/typing/builtins.py +1209 -0
- numba_cuda/numba/cuda/typing/cffi_utils.py +219 -0
- numba_cuda/numba/cuda/typing/cmathdecl.py +47 -0
- numba_cuda/numba/cuda/typing/collections.py +138 -0
- numba_cuda/numba/cuda/typing/context.py +782 -0
- numba_cuda/numba/cuda/typing/ctypes_utils.py +125 -0
- numba_cuda/numba/cuda/typing/dictdecl.py +63 -0
- numba_cuda/numba/cuda/typing/enumdecl.py +74 -0
- numba_cuda/numba/cuda/typing/listdecl.py +147 -0
- numba_cuda/numba/cuda/typing/mathdecl.py +158 -0
- numba_cuda/numba/cuda/typing/npdatetime.py +322 -0
- numba_cuda/numba/cuda/typing/npydecl.py +749 -0
- numba_cuda/numba/cuda/typing/setdecl.py +115 -0
- numba_cuda/numba/cuda/typing/templates.py +1446 -0
- numba_cuda/numba/cuda/typing/typeof.py +301 -0
- numba_cuda/numba/cuda/ufuncs.py +746 -0
- numba_cuda/numba/cuda/utils.py +724 -0
- numba_cuda/numba/cuda/vector_types.py +214 -0
- numba_cuda/numba/cuda/vectorizers.py +260 -0
- numba_cuda-0.22.0.dist-info/METADATA +109 -0
- numba_cuda-0.22.0.dist-info/RECORD +487 -0
- numba_cuda-0.22.0.dist-info/WHEEL +6 -0
- numba_cuda-0.22.0.dist-info/licenses/LICENSE +26 -0
- numba_cuda-0.22.0.dist-info/licenses/LICENSE.numba +24 -0
- numba_cuda-0.22.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,989 @@
|
|
|
1
|
+
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
|
|
2
|
+
# SPDX-License-Identifier: BSD-2-Clause
|
|
3
|
+
|
|
4
|
+
import collections
|
|
5
|
+
import functools
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from numba.cuda.core.ir import Loc
|
|
9
|
+
from numba.cuda.core.errors import UnsupportedError
|
|
10
|
+
from numba.cuda.utils import PYVERSION
|
|
11
|
+
|
|
12
|
+
# List of bytecodes creating a new block in the control flow graph
|
|
13
|
+
# (in addition to explicit jump labels).
|
|
14
|
+
NEW_BLOCKERS = frozenset(
|
|
15
|
+
["SETUP_LOOP", "FOR_ITER", "SETUP_WITH", "BEFORE_WITH"]
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class CFBlock(object):
|
|
20
|
+
def __init__(self, offset):
|
|
21
|
+
self.offset = offset
|
|
22
|
+
self.body = []
|
|
23
|
+
# A map of jumps to outgoing blocks (successors):
|
|
24
|
+
# { offset of outgoing block -> number of stack pops }
|
|
25
|
+
self.outgoing_jumps = {}
|
|
26
|
+
# A map of jumps to incoming blocks (predecessors):
|
|
27
|
+
# { offset of incoming block -> number of stack pops }
|
|
28
|
+
self.incoming_jumps = {}
|
|
29
|
+
self.terminating = False
|
|
30
|
+
|
|
31
|
+
def __repr__(self):
|
|
32
|
+
args = (
|
|
33
|
+
self.offset,
|
|
34
|
+
sorted(self.outgoing_jumps),
|
|
35
|
+
sorted(self.incoming_jumps),
|
|
36
|
+
)
|
|
37
|
+
return "block(offset:%d, outgoing: %s, incoming: %s)" % args
|
|
38
|
+
|
|
39
|
+
def __iter__(self):
|
|
40
|
+
return iter(self.body)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Loop(
|
|
44
|
+
collections.namedtuple("Loop", ("entries", "exits", "header", "body"))
|
|
45
|
+
):
|
|
46
|
+
"""
|
|
47
|
+
A control flow loop, as detected by a CFGraph object.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
__slots__ = ()
|
|
51
|
+
|
|
52
|
+
# The loop header is enough to detect that two loops are really
|
|
53
|
+
# the same, assuming they belong to the same graph.
|
|
54
|
+
# (note: in practice, only one loop instance is created per graph
|
|
55
|
+
# loop, so identity would be fine)
|
|
56
|
+
|
|
57
|
+
def __eq__(self, other):
|
|
58
|
+
return isinstance(other, Loop) and other.header == self.header
|
|
59
|
+
|
|
60
|
+
def __hash__(self):
|
|
61
|
+
return hash(self.header)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class _DictOfContainers(collections.defaultdict):
|
|
65
|
+
"""A defaultdict with customized equality checks that ignore empty values.
|
|
66
|
+
|
|
67
|
+
Non-empty value is checked by: `bool(value_item) == True`.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def __eq__(self, other):
|
|
71
|
+
if isinstance(other, _DictOfContainers):
|
|
72
|
+
mine = self._non_empty_items()
|
|
73
|
+
theirs = other._non_empty_items()
|
|
74
|
+
return mine == theirs
|
|
75
|
+
|
|
76
|
+
return NotImplemented
|
|
77
|
+
|
|
78
|
+
def __ne__(self, other):
|
|
79
|
+
ret = self.__eq__(other)
|
|
80
|
+
if ret is NotImplemented:
|
|
81
|
+
return ret
|
|
82
|
+
else:
|
|
83
|
+
return not ret
|
|
84
|
+
|
|
85
|
+
def _non_empty_items(self):
|
|
86
|
+
return [(k, vs) for k, vs in sorted(self.items()) if vs]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class CFGraph(object):
|
|
90
|
+
"""
|
|
91
|
+
Generic (almost) implementation of a Control Flow Graph.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
def __init__(self):
|
|
95
|
+
self._nodes = set()
|
|
96
|
+
self._preds = _DictOfContainers(set)
|
|
97
|
+
self._succs = _DictOfContainers(set)
|
|
98
|
+
self._edge_data = {}
|
|
99
|
+
self._entry_point = None
|
|
100
|
+
|
|
101
|
+
def add_node(self, node):
|
|
102
|
+
"""
|
|
103
|
+
Add *node* to the graph. This is necessary before adding any
|
|
104
|
+
edges from/to the node. *node* can be any hashable object.
|
|
105
|
+
"""
|
|
106
|
+
self._nodes.add(node)
|
|
107
|
+
|
|
108
|
+
def add_edge(self, src, dest, data=None):
|
|
109
|
+
"""
|
|
110
|
+
Add an edge from node *src* to node *dest*, with optional
|
|
111
|
+
per-edge *data*.
|
|
112
|
+
If such an edge already exists, it is replaced (duplicate edges
|
|
113
|
+
are not possible).
|
|
114
|
+
"""
|
|
115
|
+
if src not in self._nodes:
|
|
116
|
+
raise ValueError(
|
|
117
|
+
"Cannot add edge as src node %s not in nodes %s"
|
|
118
|
+
% (src, self._nodes)
|
|
119
|
+
)
|
|
120
|
+
if dest not in self._nodes:
|
|
121
|
+
raise ValueError(
|
|
122
|
+
"Cannot add edge as dest node %s not in nodes %s"
|
|
123
|
+
% (dest, self._nodes)
|
|
124
|
+
)
|
|
125
|
+
self._add_edge(src, dest, data)
|
|
126
|
+
|
|
127
|
+
def successors(self, src):
|
|
128
|
+
"""
|
|
129
|
+
Yield (node, data) pairs representing the successors of node *src*.
|
|
130
|
+
(*data* will be None if no data was specified when adding the edge)
|
|
131
|
+
"""
|
|
132
|
+
for dest in self._succs[src]:
|
|
133
|
+
yield dest, self._edge_data[src, dest]
|
|
134
|
+
|
|
135
|
+
def predecessors(self, dest):
|
|
136
|
+
"""
|
|
137
|
+
Yield (node, data) pairs representing the predecessors of node *dest*.
|
|
138
|
+
(*data* will be None if no data was specified when adding the edge)
|
|
139
|
+
"""
|
|
140
|
+
for src in self._preds[dest]:
|
|
141
|
+
yield src, self._edge_data[src, dest]
|
|
142
|
+
|
|
143
|
+
def set_entry_point(self, node):
|
|
144
|
+
"""
|
|
145
|
+
Set the entry point of the graph to *node*.
|
|
146
|
+
"""
|
|
147
|
+
assert node in self._nodes
|
|
148
|
+
self._entry_point = node
|
|
149
|
+
|
|
150
|
+
def process(self):
|
|
151
|
+
"""
|
|
152
|
+
Compute essential properties of the control flow graph. The graph
|
|
153
|
+
must have been fully populated, and its entry point specified. Other
|
|
154
|
+
graph properties are computed on-demand.
|
|
155
|
+
"""
|
|
156
|
+
if self._entry_point is None:
|
|
157
|
+
raise RuntimeError("no entry point defined!")
|
|
158
|
+
self._eliminate_dead_blocks()
|
|
159
|
+
|
|
160
|
+
def dominators(self):
|
|
161
|
+
"""
|
|
162
|
+
Return a dictionary of {node -> set(nodes)} mapping each node to
|
|
163
|
+
the nodes dominating it.
|
|
164
|
+
|
|
165
|
+
A node D dominates a node N when any path leading to N must go through D
|
|
166
|
+
"""
|
|
167
|
+
return self._doms
|
|
168
|
+
|
|
169
|
+
def post_dominators(self):
|
|
170
|
+
"""
|
|
171
|
+
Return a dictionary of {node -> set(nodes)} mapping each node to
|
|
172
|
+
the nodes post-dominating it.
|
|
173
|
+
|
|
174
|
+
A node P post-dominates a node N when any path starting from N must go
|
|
175
|
+
through P.
|
|
176
|
+
"""
|
|
177
|
+
return self._post_doms
|
|
178
|
+
|
|
179
|
+
def immediate_dominators(self):
|
|
180
|
+
"""
|
|
181
|
+
Return a dictionary of {node -> node} mapping each node to its
|
|
182
|
+
immediate dominator (idom).
|
|
183
|
+
|
|
184
|
+
The idom(B) is the closest strict dominator of V
|
|
185
|
+
"""
|
|
186
|
+
return self._idom
|
|
187
|
+
|
|
188
|
+
def dominance_frontier(self):
|
|
189
|
+
"""
|
|
190
|
+
Return a dictionary of {node -> set(nodes)} mapping each node to
|
|
191
|
+
the nodes in its dominance frontier.
|
|
192
|
+
|
|
193
|
+
The dominance frontier _df(N) is the set of all nodes that are
|
|
194
|
+
immediate successors to blocks dominated by N but which aren't
|
|
195
|
+
strictly dominated by N
|
|
196
|
+
"""
|
|
197
|
+
return self._df
|
|
198
|
+
|
|
199
|
+
def dominator_tree(self):
|
|
200
|
+
"""
|
|
201
|
+
return a dictionary of {node -> set(nodes)} mapping each node to
|
|
202
|
+
the set of nodes it immediately dominates
|
|
203
|
+
|
|
204
|
+
The domtree(B) is the closest strict set of nodes that B dominates
|
|
205
|
+
"""
|
|
206
|
+
return self._domtree
|
|
207
|
+
|
|
208
|
+
@functools.cached_property
|
|
209
|
+
def _exit_points(self):
|
|
210
|
+
return self._find_exit_points()
|
|
211
|
+
|
|
212
|
+
@functools.cached_property
|
|
213
|
+
def _doms(self):
|
|
214
|
+
return self._find_dominators()
|
|
215
|
+
|
|
216
|
+
@functools.cached_property
|
|
217
|
+
def _back_edges(self):
|
|
218
|
+
return self._find_back_edges()
|
|
219
|
+
|
|
220
|
+
@functools.cached_property
|
|
221
|
+
def _topo_order(self):
|
|
222
|
+
return self._find_topo_order()
|
|
223
|
+
|
|
224
|
+
@functools.cached_property
|
|
225
|
+
def _descs(self):
|
|
226
|
+
return self._find_descendents()
|
|
227
|
+
|
|
228
|
+
@functools.cached_property
|
|
229
|
+
def _loops(self):
|
|
230
|
+
return self._find_loops()
|
|
231
|
+
|
|
232
|
+
@functools.cached_property
|
|
233
|
+
def _in_loops(self):
|
|
234
|
+
return self._find_in_loops()
|
|
235
|
+
|
|
236
|
+
@functools.cached_property
|
|
237
|
+
def _post_doms(self):
|
|
238
|
+
return self._find_post_dominators()
|
|
239
|
+
|
|
240
|
+
@functools.cached_property
|
|
241
|
+
def _idom(self):
|
|
242
|
+
return self._find_immediate_dominators()
|
|
243
|
+
|
|
244
|
+
@functools.cached_property
|
|
245
|
+
def _df(self):
|
|
246
|
+
return self._find_dominance_frontier()
|
|
247
|
+
|
|
248
|
+
@functools.cached_property
|
|
249
|
+
def _domtree(self):
|
|
250
|
+
return self._find_dominator_tree()
|
|
251
|
+
|
|
252
|
+
def descendents(self, node):
|
|
253
|
+
"""
|
|
254
|
+
Return the set of descendents of the given *node*, in topological
|
|
255
|
+
order (ignoring back edges).
|
|
256
|
+
"""
|
|
257
|
+
return self._descs[node]
|
|
258
|
+
|
|
259
|
+
def entry_point(self):
|
|
260
|
+
"""
|
|
261
|
+
Return the entry point node.
|
|
262
|
+
"""
|
|
263
|
+
assert self._entry_point is not None
|
|
264
|
+
return self._entry_point
|
|
265
|
+
|
|
266
|
+
def exit_points(self):
|
|
267
|
+
"""
|
|
268
|
+
Return the computed set of exit nodes (may be empty).
|
|
269
|
+
"""
|
|
270
|
+
return self._exit_points
|
|
271
|
+
|
|
272
|
+
def backbone(self):
|
|
273
|
+
"""
|
|
274
|
+
Return the set of nodes constituting the graph's backbone.
|
|
275
|
+
(i.e. the nodes that every path starting from the entry point
|
|
276
|
+
must go through). By construction, it is non-empty: it contains
|
|
277
|
+
at least the entry point.
|
|
278
|
+
"""
|
|
279
|
+
return self._post_doms[self._entry_point]
|
|
280
|
+
|
|
281
|
+
def loops(self):
|
|
282
|
+
"""
|
|
283
|
+
Return a dictionary of {node -> loop} mapping each loop header
|
|
284
|
+
to the loop (a Loop instance) starting with it.
|
|
285
|
+
"""
|
|
286
|
+
return self._loops
|
|
287
|
+
|
|
288
|
+
def in_loops(self, node):
|
|
289
|
+
"""
|
|
290
|
+
Return the list of Loop objects the *node* belongs to,
|
|
291
|
+
from innermost to outermost.
|
|
292
|
+
"""
|
|
293
|
+
return [self._loops[x] for x in self._in_loops.get(node, ())]
|
|
294
|
+
|
|
295
|
+
def dead_nodes(self):
|
|
296
|
+
"""
|
|
297
|
+
Return the set of dead nodes (eliminated from the graph).
|
|
298
|
+
"""
|
|
299
|
+
return self._dead_nodes
|
|
300
|
+
|
|
301
|
+
def nodes(self):
|
|
302
|
+
"""
|
|
303
|
+
Return the set of live nodes.
|
|
304
|
+
"""
|
|
305
|
+
return self._nodes
|
|
306
|
+
|
|
307
|
+
def topo_order(self):
|
|
308
|
+
"""
|
|
309
|
+
Return the sequence of nodes in topological order (ignoring back
|
|
310
|
+
edges).
|
|
311
|
+
"""
|
|
312
|
+
return self._topo_order
|
|
313
|
+
|
|
314
|
+
def topo_sort(self, nodes, reverse=False):
|
|
315
|
+
"""
|
|
316
|
+
Iterate over the *nodes* in topological order (ignoring back edges).
|
|
317
|
+
The sort isn't guaranteed to be stable.
|
|
318
|
+
"""
|
|
319
|
+
nodes = set(nodes)
|
|
320
|
+
it = self._topo_order
|
|
321
|
+
if reverse:
|
|
322
|
+
it = reversed(it)
|
|
323
|
+
for n in it:
|
|
324
|
+
if n in nodes:
|
|
325
|
+
yield n
|
|
326
|
+
|
|
327
|
+
def dump(self, file=None):
|
|
328
|
+
"""
|
|
329
|
+
Dump extensive debug information.
|
|
330
|
+
"""
|
|
331
|
+
import pprint
|
|
332
|
+
|
|
333
|
+
file = file or sys.stdout
|
|
334
|
+
if 1:
|
|
335
|
+
print("CFG adjacency lists:", file=file)
|
|
336
|
+
self._dump_adj_lists(file)
|
|
337
|
+
print("CFG dominators:", file=file)
|
|
338
|
+
pprint.pprint(self._doms, stream=file)
|
|
339
|
+
print("CFG post-dominators:", file=file)
|
|
340
|
+
pprint.pprint(self._post_doms, stream=file)
|
|
341
|
+
print("CFG back edges:", sorted(self._back_edges), file=file)
|
|
342
|
+
print("CFG loops:", file=file)
|
|
343
|
+
pprint.pprint(self._loops, stream=file)
|
|
344
|
+
print("CFG node-to-loops:", file=file)
|
|
345
|
+
pprint.pprint(self._in_loops, stream=file)
|
|
346
|
+
print("CFG backbone:", file=file)
|
|
347
|
+
pprint.pprint(self.backbone(), stream=file)
|
|
348
|
+
|
|
349
|
+
def render_dot(self, filename="numba_cfg.dot"):
|
|
350
|
+
"""Render the controlflow graph with GraphViz DOT via the
|
|
351
|
+
``graphviz`` python binding.
|
|
352
|
+
|
|
353
|
+
Returns
|
|
354
|
+
-------
|
|
355
|
+
g : graphviz.Digraph
|
|
356
|
+
Use `g.view()` to open the graph in the default PDF application.
|
|
357
|
+
"""
|
|
358
|
+
|
|
359
|
+
try:
|
|
360
|
+
import graphviz as gv
|
|
361
|
+
except ImportError:
|
|
362
|
+
raise ImportError(
|
|
363
|
+
"The feature requires `graphviz` but it is not available. "
|
|
364
|
+
"Please install with `pip install graphviz`"
|
|
365
|
+
)
|
|
366
|
+
g = gv.Digraph(filename=filename)
|
|
367
|
+
# Populate the nodes
|
|
368
|
+
for n in self._nodes:
|
|
369
|
+
g.node(str(n))
|
|
370
|
+
# Populate the edges
|
|
371
|
+
for n in self._nodes:
|
|
372
|
+
for edge in self._succs[n]:
|
|
373
|
+
g.edge(str(n), str(edge))
|
|
374
|
+
return g
|
|
375
|
+
|
|
376
|
+
# Internal APIs
|
|
377
|
+
|
|
378
|
+
def _add_edge(self, from_, to, data=None):
|
|
379
|
+
# This internal version allows adding edges to/from unregistered
|
|
380
|
+
# (ghost) nodes.
|
|
381
|
+
self._preds[to].add(from_)
|
|
382
|
+
self._succs[from_].add(to)
|
|
383
|
+
self._edge_data[from_, to] = data
|
|
384
|
+
|
|
385
|
+
def _remove_node_edges(self, node):
|
|
386
|
+
for succ in self._succs.pop(node, ()):
|
|
387
|
+
self._preds[succ].remove(node)
|
|
388
|
+
del self._edge_data[node, succ]
|
|
389
|
+
for pred in self._preds.pop(node, ()):
|
|
390
|
+
self._succs[pred].remove(node)
|
|
391
|
+
del self._edge_data[pred, node]
|
|
392
|
+
|
|
393
|
+
def _dfs(self, entries=None):
|
|
394
|
+
if entries is None:
|
|
395
|
+
entries = (self._entry_point,)
|
|
396
|
+
seen = set()
|
|
397
|
+
stack = list(entries)
|
|
398
|
+
while stack:
|
|
399
|
+
node = stack.pop()
|
|
400
|
+
if node not in seen:
|
|
401
|
+
yield node
|
|
402
|
+
seen.add(node)
|
|
403
|
+
for succ in self._succs[node]:
|
|
404
|
+
stack.append(succ)
|
|
405
|
+
|
|
406
|
+
def _eliminate_dead_blocks(self):
|
|
407
|
+
"""
|
|
408
|
+
Eliminate all blocks not reachable from the entry point, and
|
|
409
|
+
stash them into self._dead_nodes.
|
|
410
|
+
"""
|
|
411
|
+
live = set()
|
|
412
|
+
for node in self._dfs():
|
|
413
|
+
live.add(node)
|
|
414
|
+
self._dead_nodes = self._nodes - live
|
|
415
|
+
self._nodes = live
|
|
416
|
+
# Remove all edges leading from dead nodes
|
|
417
|
+
for dead in self._dead_nodes:
|
|
418
|
+
self._remove_node_edges(dead)
|
|
419
|
+
|
|
420
|
+
def _find_exit_points(self):
|
|
421
|
+
"""
|
|
422
|
+
Compute the graph's exit points.
|
|
423
|
+
"""
|
|
424
|
+
exit_points = set()
|
|
425
|
+
for n in self._nodes:
|
|
426
|
+
if not self._succs.get(n):
|
|
427
|
+
exit_points.add(n)
|
|
428
|
+
return exit_points
|
|
429
|
+
|
|
430
|
+
def _find_postorder(self):
|
|
431
|
+
succs = self._succs
|
|
432
|
+
back_edges = self._back_edges
|
|
433
|
+
post_order = []
|
|
434
|
+
seen = set()
|
|
435
|
+
|
|
436
|
+
post_order = []
|
|
437
|
+
|
|
438
|
+
# DFS
|
|
439
|
+
def dfs_rec(node):
|
|
440
|
+
if node not in seen:
|
|
441
|
+
seen.add(node)
|
|
442
|
+
stack.append((post_order.append, node))
|
|
443
|
+
for dest in succs[node]:
|
|
444
|
+
if (node, dest) not in back_edges:
|
|
445
|
+
stack.append((dfs_rec, dest))
|
|
446
|
+
|
|
447
|
+
stack = [(dfs_rec, self._entry_point)]
|
|
448
|
+
while stack:
|
|
449
|
+
cb, data = stack.pop()
|
|
450
|
+
cb(data)
|
|
451
|
+
|
|
452
|
+
return post_order
|
|
453
|
+
|
|
454
|
+
def _find_immediate_dominators(self):
|
|
455
|
+
# The algorithm implemented computes the immediate dominator
|
|
456
|
+
# for each node in the CFG which is equivalent to build a dominator tree
|
|
457
|
+
# Based on the implementation from NetworkX
|
|
458
|
+
# library - nx.immediate_dominators
|
|
459
|
+
# https://github.com/networkx/networkx/blob/858e7cb183541a78969fed0cbcd02346f5866c02/networkx/algorithms/dominance.py # noqa: E501
|
|
460
|
+
# References:
|
|
461
|
+
# Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy
|
|
462
|
+
# A Simple, Fast Dominance Algorithm
|
|
463
|
+
# https://www.cs.rice.edu/~keith/EMBED/dom.pdf
|
|
464
|
+
def intersect(u, v):
|
|
465
|
+
while u != v:
|
|
466
|
+
while idx[u] < idx[v]:
|
|
467
|
+
u = idom[u]
|
|
468
|
+
while idx[u] > idx[v]:
|
|
469
|
+
v = idom[v]
|
|
470
|
+
return u
|
|
471
|
+
|
|
472
|
+
entry = self._entry_point
|
|
473
|
+
preds_table = self._preds
|
|
474
|
+
|
|
475
|
+
order = self._find_postorder()
|
|
476
|
+
idx = {e: i for i, e in enumerate(order)} # index of each node
|
|
477
|
+
idom = {entry: entry}
|
|
478
|
+
order.pop()
|
|
479
|
+
order.reverse()
|
|
480
|
+
|
|
481
|
+
changed = True
|
|
482
|
+
while changed:
|
|
483
|
+
changed = False
|
|
484
|
+
for u in order:
|
|
485
|
+
new_idom = functools.reduce(
|
|
486
|
+
intersect, (v for v in preds_table[u] if v in idom)
|
|
487
|
+
)
|
|
488
|
+
if u not in idom or idom[u] != new_idom:
|
|
489
|
+
idom[u] = new_idom
|
|
490
|
+
changed = True
|
|
491
|
+
|
|
492
|
+
return idom
|
|
493
|
+
|
|
494
|
+
def _find_dominator_tree(self):
|
|
495
|
+
idom = self._idom
|
|
496
|
+
domtree = _DictOfContainers(set)
|
|
497
|
+
|
|
498
|
+
for u, v in idom.items():
|
|
499
|
+
# v dominates u
|
|
500
|
+
if u not in domtree:
|
|
501
|
+
domtree[u] = set()
|
|
502
|
+
if u != v:
|
|
503
|
+
domtree[v].add(u)
|
|
504
|
+
|
|
505
|
+
return domtree
|
|
506
|
+
|
|
507
|
+
def _find_dominance_frontier(self):
|
|
508
|
+
idom = self._idom
|
|
509
|
+
preds_table = self._preds
|
|
510
|
+
df = {u: set() for u in idom}
|
|
511
|
+
|
|
512
|
+
for u in idom:
|
|
513
|
+
if len(preds_table[u]) < 2:
|
|
514
|
+
continue
|
|
515
|
+
for v in preds_table[u]:
|
|
516
|
+
while v != idom[u]:
|
|
517
|
+
df[v].add(u)
|
|
518
|
+
v = idom[v]
|
|
519
|
+
|
|
520
|
+
return df
|
|
521
|
+
|
|
522
|
+
def _find_dominators_internal(self, post=False):
|
|
523
|
+
# See theoretical description in
|
|
524
|
+
# http://en.wikipedia.org/wiki/Dominator_%28graph_theory%29
|
|
525
|
+
# The algorithm implemented here uses a todo-list as described
|
|
526
|
+
# in http://pages.cs.wisc.edu/~fischer/cs701.f08/finding.loops.html
|
|
527
|
+
if post:
|
|
528
|
+
entries = set(self._exit_points)
|
|
529
|
+
preds_table = self._succs
|
|
530
|
+
succs_table = self._preds
|
|
531
|
+
else:
|
|
532
|
+
entries = set([self._entry_point])
|
|
533
|
+
preds_table = self._preds
|
|
534
|
+
succs_table = self._succs
|
|
535
|
+
|
|
536
|
+
if not entries:
|
|
537
|
+
raise RuntimeError(
|
|
538
|
+
"no entry points: dominator algorithm cannot be seeded"
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
doms = {}
|
|
542
|
+
for e in entries:
|
|
543
|
+
doms[e] = set([e])
|
|
544
|
+
|
|
545
|
+
todo = []
|
|
546
|
+
for n in self._nodes:
|
|
547
|
+
if n not in entries:
|
|
548
|
+
doms[n] = set(self._nodes)
|
|
549
|
+
todo.append(n)
|
|
550
|
+
|
|
551
|
+
while todo:
|
|
552
|
+
n = todo.pop()
|
|
553
|
+
if n in entries:
|
|
554
|
+
continue
|
|
555
|
+
new_doms = set([n])
|
|
556
|
+
preds = preds_table[n]
|
|
557
|
+
if preds:
|
|
558
|
+
new_doms |= functools.reduce(
|
|
559
|
+
set.intersection, [doms[p] for p in preds]
|
|
560
|
+
)
|
|
561
|
+
if new_doms != doms[n]:
|
|
562
|
+
assert len(new_doms) < len(doms[n])
|
|
563
|
+
doms[n] = new_doms
|
|
564
|
+
todo.extend(succs_table[n])
|
|
565
|
+
return doms
|
|
566
|
+
|
|
567
|
+
def _find_dominators(self):
|
|
568
|
+
return self._find_dominators_internal(post=False)
|
|
569
|
+
|
|
570
|
+
def _find_post_dominators(self):
|
|
571
|
+
# To handle infinite loops correctly, we need to add a dummy
|
|
572
|
+
# exit point, and link members of infinite loops to it.
|
|
573
|
+
dummy_exit = object()
|
|
574
|
+
self._exit_points.add(dummy_exit)
|
|
575
|
+
for loop in self._loops.values():
|
|
576
|
+
if not loop.exits:
|
|
577
|
+
for b in loop.body:
|
|
578
|
+
self._add_edge(b, dummy_exit)
|
|
579
|
+
pdoms = self._find_dominators_internal(post=True)
|
|
580
|
+
# Fix the _post_doms table to make no reference to the dummy exit
|
|
581
|
+
del pdoms[dummy_exit]
|
|
582
|
+
for doms in pdoms.values():
|
|
583
|
+
doms.discard(dummy_exit)
|
|
584
|
+
self._remove_node_edges(dummy_exit)
|
|
585
|
+
self._exit_points.remove(dummy_exit)
|
|
586
|
+
return pdoms
|
|
587
|
+
|
|
588
|
+
# Finding loops and back edges: see
|
|
589
|
+
# http://pages.cs.wisc.edu/~fischer/cs701.f08/finding.loops.html
|
|
590
|
+
|
|
591
|
+
def _find_back_edges(self, stats=None):
|
|
592
|
+
"""
|
|
593
|
+
Find back edges. An edge (src, dest) is a back edge if and
|
|
594
|
+
only if *dest* dominates *src*.
|
|
595
|
+
"""
|
|
596
|
+
# Prepare stats to capture execution information
|
|
597
|
+
if stats is not None:
|
|
598
|
+
if not isinstance(stats, dict):
|
|
599
|
+
raise TypeError(f"*stats* must be a dict; got {type(stats)}")
|
|
600
|
+
stats.setdefault("iteration_count", 0)
|
|
601
|
+
|
|
602
|
+
# Uses a simple DFS to find back-edges.
|
|
603
|
+
# The new algorithm is faster than the the previous dominator based
|
|
604
|
+
# algorithm.
|
|
605
|
+
back_edges = set()
|
|
606
|
+
# stack: keeps track of the traversal path
|
|
607
|
+
stack = []
|
|
608
|
+
# succs_state: keep track of unvisited successors of a node
|
|
609
|
+
succs_state = {}
|
|
610
|
+
entry_point = self.entry_point()
|
|
611
|
+
|
|
612
|
+
checked = set()
|
|
613
|
+
|
|
614
|
+
def push_state(node):
|
|
615
|
+
stack.append(node)
|
|
616
|
+
succs_state[node] = [dest for dest in self._succs[node]]
|
|
617
|
+
|
|
618
|
+
push_state(entry_point)
|
|
619
|
+
|
|
620
|
+
# Keep track for iteration count for debugging
|
|
621
|
+
iter_ct = 0
|
|
622
|
+
while stack:
|
|
623
|
+
iter_ct += 1
|
|
624
|
+
tos = stack[-1]
|
|
625
|
+
tos_succs = succs_state[tos]
|
|
626
|
+
# Are there successors not checked?
|
|
627
|
+
if tos_succs:
|
|
628
|
+
# Check the next successor
|
|
629
|
+
cur_node = tos_succs.pop()
|
|
630
|
+
# Is it in our traversal path?
|
|
631
|
+
if cur_node in stack:
|
|
632
|
+
# Yes, it's a backedge
|
|
633
|
+
back_edges.add((tos, cur_node))
|
|
634
|
+
elif cur_node not in checked:
|
|
635
|
+
# Push
|
|
636
|
+
push_state(cur_node)
|
|
637
|
+
else:
|
|
638
|
+
# Checked all successors. Pop
|
|
639
|
+
stack.pop()
|
|
640
|
+
checked.add(tos)
|
|
641
|
+
|
|
642
|
+
if stats is not None:
|
|
643
|
+
stats["iteration_count"] += iter_ct
|
|
644
|
+
return back_edges
|
|
645
|
+
|
|
646
|
+
def _find_topo_order(self):
|
|
647
|
+
succs = self._succs
|
|
648
|
+
back_edges = self._back_edges
|
|
649
|
+
post_order = []
|
|
650
|
+
seen = set()
|
|
651
|
+
|
|
652
|
+
def _dfs_rec(node):
|
|
653
|
+
if node not in seen:
|
|
654
|
+
seen.add(node)
|
|
655
|
+
for dest in succs[node]:
|
|
656
|
+
if (node, dest) not in back_edges:
|
|
657
|
+
_dfs_rec(dest)
|
|
658
|
+
post_order.append(node)
|
|
659
|
+
|
|
660
|
+
_dfs_rec(self._entry_point)
|
|
661
|
+
post_order.reverse()
|
|
662
|
+
return post_order
|
|
663
|
+
|
|
664
|
+
def _find_descendents(self):
|
|
665
|
+
descs = {}
|
|
666
|
+
for node in reversed(self._topo_order):
|
|
667
|
+
descs[node] = node_descs = set()
|
|
668
|
+
for succ in self._succs[node]:
|
|
669
|
+
if (node, succ) not in self._back_edges:
|
|
670
|
+
node_descs.add(succ)
|
|
671
|
+
node_descs.update(descs[succ])
|
|
672
|
+
return descs
|
|
673
|
+
|
|
674
|
+
def _find_loops(self):
|
|
675
|
+
"""
|
|
676
|
+
Find the loops defined by the graph's back edges.
|
|
677
|
+
"""
|
|
678
|
+
bodies = {}
|
|
679
|
+
for src, dest in self._back_edges:
|
|
680
|
+
# The destination of the back edge is the loop header
|
|
681
|
+
header = dest
|
|
682
|
+
# Build up the loop body from the back edge's source node,
|
|
683
|
+
# up to the source header.
|
|
684
|
+
body = set([header])
|
|
685
|
+
queue = [src]
|
|
686
|
+
while queue:
|
|
687
|
+
n = queue.pop()
|
|
688
|
+
if n not in body:
|
|
689
|
+
body.add(n)
|
|
690
|
+
queue.extend(self._preds[n])
|
|
691
|
+
# There can be several back edges to a given loop header;
|
|
692
|
+
# if so, merge the resulting body fragments.
|
|
693
|
+
if header in bodies:
|
|
694
|
+
bodies[header].update(body)
|
|
695
|
+
else:
|
|
696
|
+
bodies[header] = body
|
|
697
|
+
|
|
698
|
+
# Create a Loop object for each header.
|
|
699
|
+
loops = {}
|
|
700
|
+
for header, body in bodies.items():
|
|
701
|
+
entries = set()
|
|
702
|
+
exits = set()
|
|
703
|
+
for n in body:
|
|
704
|
+
entries.update(self._preds[n] - body)
|
|
705
|
+
exits.update(self._succs[n] - body)
|
|
706
|
+
loop = Loop(header=header, body=body, entries=entries, exits=exits)
|
|
707
|
+
loops[header] = loop
|
|
708
|
+
return loops
|
|
709
|
+
|
|
710
|
+
def _find_in_loops(self):
|
|
711
|
+
loops = self._loops
|
|
712
|
+
# Compute the loops to which each node belongs.
|
|
713
|
+
in_loops = dict((n, []) for n in self._nodes)
|
|
714
|
+
# Sort loops from longest to shortest
|
|
715
|
+
# This ensures that outer loops will come before inner loops
|
|
716
|
+
for loop in sorted(loops.values(), key=lambda loop: len(loop.body)):
|
|
717
|
+
for n in loop.body:
|
|
718
|
+
in_loops[n].append(loop.header)
|
|
719
|
+
return in_loops
|
|
720
|
+
|
|
721
|
+
def _dump_adj_lists(self, file):
|
|
722
|
+
adj_lists = dict(
|
|
723
|
+
(src, sorted(list(dests))) for src, dests in self._succs.items()
|
|
724
|
+
)
|
|
725
|
+
import pprint
|
|
726
|
+
|
|
727
|
+
pprint.pprint(adj_lists, stream=file)
|
|
728
|
+
|
|
729
|
+
def __eq__(self, other):
|
|
730
|
+
if not isinstance(other, CFGraph):
|
|
731
|
+
return NotImplemented
|
|
732
|
+
|
|
733
|
+
for x in ["_nodes", "_edge_data", "_entry_point", "_preds", "_succs"]:
|
|
734
|
+
this = getattr(self, x, None)
|
|
735
|
+
that = getattr(other, x, None)
|
|
736
|
+
if this != that:
|
|
737
|
+
return False
|
|
738
|
+
return True
|
|
739
|
+
|
|
740
|
+
def __ne__(self, other):
|
|
741
|
+
return not self.__eq__(other)
|
|
742
|
+
|
|
743
|
+
|
|
744
|
+
class ControlFlowAnalysis(object):
|
|
745
|
+
"""
|
|
746
|
+
Attributes
|
|
747
|
+
----------
|
|
748
|
+
- bytecode
|
|
749
|
+
|
|
750
|
+
- blocks
|
|
751
|
+
|
|
752
|
+
- blockseq
|
|
753
|
+
|
|
754
|
+
- doms: dict of set
|
|
755
|
+
Dominators
|
|
756
|
+
|
|
757
|
+
- backbone: set of block offsets
|
|
758
|
+
The set of block that is common to all possible code path.
|
|
759
|
+
|
|
760
|
+
"""
|
|
761
|
+
|
|
762
|
+
def __init__(self, bytecode):
|
|
763
|
+
self.bytecode = bytecode
|
|
764
|
+
self.blocks = {}
|
|
765
|
+
self.liveblocks = {}
|
|
766
|
+
self.blockseq = []
|
|
767
|
+
self.doms = None
|
|
768
|
+
self.backbone = None
|
|
769
|
+
# Internal temp states
|
|
770
|
+
self._force_new_block = True
|
|
771
|
+
self._curblock = None
|
|
772
|
+
self._blockstack = []
|
|
773
|
+
self._loops = []
|
|
774
|
+
self._withs = []
|
|
775
|
+
|
|
776
|
+
def iterblocks(self):
|
|
777
|
+
"""
|
|
778
|
+
Return all blocks in sequence of occurrence
|
|
779
|
+
"""
|
|
780
|
+
for i in self.blockseq:
|
|
781
|
+
yield self.blocks[i]
|
|
782
|
+
|
|
783
|
+
def iterliveblocks(self):
|
|
784
|
+
"""
|
|
785
|
+
Return all live blocks in sequence of occurrence
|
|
786
|
+
"""
|
|
787
|
+
for i in self.blockseq:
|
|
788
|
+
if i in self.liveblocks:
|
|
789
|
+
yield self.blocks[i]
|
|
790
|
+
|
|
791
|
+
def incoming_blocks(self, block):
|
|
792
|
+
"""
|
|
793
|
+
Yield (incoming block, number of stack pops) pairs for *block*.
|
|
794
|
+
"""
|
|
795
|
+
for i, pops in block.incoming_jumps.items():
|
|
796
|
+
if i in self.liveblocks:
|
|
797
|
+
yield self.blocks[i], pops
|
|
798
|
+
|
|
799
|
+
def dump(self, file=None):
|
|
800
|
+
self.graph.dump(file=None)
|
|
801
|
+
|
|
802
|
+
def run(self):
|
|
803
|
+
for inst in self._iter_inst():
|
|
804
|
+
fname = "op_%s" % inst.opname
|
|
805
|
+
fn = getattr(self, fname, None)
|
|
806
|
+
if fn is not None:
|
|
807
|
+
fn(inst)
|
|
808
|
+
elif inst.is_jump:
|
|
809
|
+
# this catches e.g. try... except
|
|
810
|
+
l = Loc(self.bytecode.func_id.filename, inst.lineno)
|
|
811
|
+
if inst.opname in {"SETUP_FINALLY"}:
|
|
812
|
+
msg = "'try' block not supported until python3.7 or later"
|
|
813
|
+
else:
|
|
814
|
+
msg = "Use of unsupported opcode (%s) found" % inst.opname
|
|
815
|
+
raise UnsupportedError(msg, loc=l)
|
|
816
|
+
else:
|
|
817
|
+
# Non-jump instructions are ignored
|
|
818
|
+
pass # intentionally
|
|
819
|
+
|
|
820
|
+
# Close all blocks
|
|
821
|
+
for cur, nxt in zip(self.blockseq, self.blockseq[1:]):
|
|
822
|
+
blk = self.blocks[cur]
|
|
823
|
+
if not blk.outgoing_jumps and not blk.terminating:
|
|
824
|
+
blk.outgoing_jumps[nxt] = 0
|
|
825
|
+
|
|
826
|
+
graph = CFGraph()
|
|
827
|
+
for b in self.blocks:
|
|
828
|
+
graph.add_node(b)
|
|
829
|
+
for b in self.blocks.values():
|
|
830
|
+
for out, pops in b.outgoing_jumps.items():
|
|
831
|
+
graph.add_edge(b.offset, out, pops)
|
|
832
|
+
graph.set_entry_point(min(self.blocks))
|
|
833
|
+
graph.process()
|
|
834
|
+
self.graph = graph
|
|
835
|
+
|
|
836
|
+
# Fill incoming
|
|
837
|
+
for b in self.blocks.values():
|
|
838
|
+
for out, pops in b.outgoing_jumps.items():
|
|
839
|
+
self.blocks[out].incoming_jumps[b.offset] = pops
|
|
840
|
+
|
|
841
|
+
# Find liveblocks
|
|
842
|
+
self.liveblocks = dict((i, self.blocks[i]) for i in self.graph.nodes())
|
|
843
|
+
|
|
844
|
+
for lastblk in reversed(self.blockseq):
|
|
845
|
+
if lastblk in self.liveblocks:
|
|
846
|
+
break
|
|
847
|
+
else:
|
|
848
|
+
raise AssertionError("No live block that exits!?")
|
|
849
|
+
|
|
850
|
+
# Find backbone
|
|
851
|
+
backbone = self.graph.backbone()
|
|
852
|
+
# Filter out in loop blocks (Assuming no other cyclic control blocks)
|
|
853
|
+
# This is to unavoid variable defined in loops to be considered as
|
|
854
|
+
# function scope.
|
|
855
|
+
inloopblocks = set()
|
|
856
|
+
|
|
857
|
+
for b in self.blocks.keys():
|
|
858
|
+
if self.graph.in_loops(b):
|
|
859
|
+
inloopblocks.add(b)
|
|
860
|
+
|
|
861
|
+
self.backbone = backbone - inloopblocks
|
|
862
|
+
|
|
863
|
+
def jump(self, target, pops=0):
|
|
864
|
+
"""
|
|
865
|
+
Register a jump (conditional or not) to *target* offset.
|
|
866
|
+
*pops* is the number of stack pops implied by the jump (default 0).
|
|
867
|
+
"""
|
|
868
|
+
self._curblock.outgoing_jumps[target] = pops
|
|
869
|
+
|
|
870
|
+
def _iter_inst(self):
|
|
871
|
+
for inst in self.bytecode:
|
|
872
|
+
if self._use_new_block(inst):
|
|
873
|
+
self._guard_with_as(inst)
|
|
874
|
+
self._start_new_block(inst)
|
|
875
|
+
self._curblock.body.append(inst.offset)
|
|
876
|
+
yield inst
|
|
877
|
+
|
|
878
|
+
def _use_new_block(self, inst):
|
|
879
|
+
if inst.offset in self.bytecode.labels:
|
|
880
|
+
res = True
|
|
881
|
+
elif inst.opname in NEW_BLOCKERS:
|
|
882
|
+
res = True
|
|
883
|
+
else:
|
|
884
|
+
res = self._force_new_block
|
|
885
|
+
|
|
886
|
+
self._force_new_block = False
|
|
887
|
+
return res
|
|
888
|
+
|
|
889
|
+
def _start_new_block(self, inst):
|
|
890
|
+
self._curblock = CFBlock(inst.offset)
|
|
891
|
+
self.blocks[inst.offset] = self._curblock
|
|
892
|
+
self.blockseq.append(inst.offset)
|
|
893
|
+
|
|
894
|
+
def _guard_with_as(self, current_inst):
|
|
895
|
+
"""Checks if the next instruction after a SETUP_WITH is something other
|
|
896
|
+
than a POP_TOP, if it is something else it'll be some sort of store
|
|
897
|
+
which is not supported (this corresponds to `with CTXMGR as VAR(S)`)."""
|
|
898
|
+
if current_inst.opname == "SETUP_WITH":
|
|
899
|
+
next_op = self.bytecode[current_inst.next].opname
|
|
900
|
+
if next_op != "POP_TOP":
|
|
901
|
+
msg = (
|
|
902
|
+
"The 'with (context manager) as "
|
|
903
|
+
"(variable):' construct is not "
|
|
904
|
+
"supported."
|
|
905
|
+
)
|
|
906
|
+
raise UnsupportedError(msg)
|
|
907
|
+
|
|
908
|
+
def op_SETUP_LOOP(self, inst):
|
|
909
|
+
end = inst.get_jump_target()
|
|
910
|
+
self._blockstack.append(end)
|
|
911
|
+
self._loops.append((inst.offset, end))
|
|
912
|
+
# TODO: Looplifting requires the loop entry be its own block.
|
|
913
|
+
# Forcing a new block here is the simplest solution for now.
|
|
914
|
+
# But, we should consider other less ad-hoc ways.
|
|
915
|
+
self.jump(inst.next)
|
|
916
|
+
self._force_new_block = True
|
|
917
|
+
|
|
918
|
+
def op_SETUP_WITH(self, inst):
|
|
919
|
+
end = inst.get_jump_target()
|
|
920
|
+
self._blockstack.append(end)
|
|
921
|
+
self._withs.append((inst.offset, end))
|
|
922
|
+
# TODO: WithLifting requires the loop entry be its own block.
|
|
923
|
+
# Forcing a new block here is the simplest solution for now.
|
|
924
|
+
# But, we should consider other less ad-hoc ways.
|
|
925
|
+
self.jump(inst.next)
|
|
926
|
+
self._force_new_block = True
|
|
927
|
+
|
|
928
|
+
def op_POP_BLOCK(self, inst):
|
|
929
|
+
self._blockstack.pop()
|
|
930
|
+
|
|
931
|
+
def op_FOR_ITER(self, inst):
|
|
932
|
+
self.jump(inst.get_jump_target())
|
|
933
|
+
self.jump(inst.next)
|
|
934
|
+
self._force_new_block = True
|
|
935
|
+
|
|
936
|
+
def _op_ABSOLUTE_JUMP_IF(self, inst):
|
|
937
|
+
self.jump(inst.get_jump_target())
|
|
938
|
+
self.jump(inst.next)
|
|
939
|
+
self._force_new_block = True
|
|
940
|
+
|
|
941
|
+
op_POP_JUMP_IF_FALSE = _op_ABSOLUTE_JUMP_IF
|
|
942
|
+
op_POP_JUMP_IF_TRUE = _op_ABSOLUTE_JUMP_IF
|
|
943
|
+
op_JUMP_IF_FALSE = _op_ABSOLUTE_JUMP_IF
|
|
944
|
+
op_JUMP_IF_TRUE = _op_ABSOLUTE_JUMP_IF
|
|
945
|
+
|
|
946
|
+
op_POP_JUMP_FORWARD_IF_FALSE = _op_ABSOLUTE_JUMP_IF
|
|
947
|
+
op_POP_JUMP_BACKWARD_IF_FALSE = _op_ABSOLUTE_JUMP_IF
|
|
948
|
+
op_POP_JUMP_FORWARD_IF_TRUE = _op_ABSOLUTE_JUMP_IF
|
|
949
|
+
op_POP_JUMP_BACKWARD_IF_TRUE = _op_ABSOLUTE_JUMP_IF
|
|
950
|
+
|
|
951
|
+
def _op_ABSOLUTE_JUMP_OR_POP(self, inst):
|
|
952
|
+
self.jump(inst.get_jump_target())
|
|
953
|
+
self.jump(inst.next, pops=1)
|
|
954
|
+
self._force_new_block = True
|
|
955
|
+
|
|
956
|
+
op_JUMP_IF_FALSE_OR_POP = _op_ABSOLUTE_JUMP_OR_POP
|
|
957
|
+
op_JUMP_IF_TRUE_OR_POP = _op_ABSOLUTE_JUMP_OR_POP
|
|
958
|
+
|
|
959
|
+
def op_JUMP_ABSOLUTE(self, inst):
|
|
960
|
+
self.jump(inst.get_jump_target())
|
|
961
|
+
self._force_new_block = True
|
|
962
|
+
|
|
963
|
+
def op_JUMP_FORWARD(self, inst):
|
|
964
|
+
self.jump(inst.get_jump_target())
|
|
965
|
+
self._force_new_block = True
|
|
966
|
+
|
|
967
|
+
op_JUMP_BACKWARD = op_JUMP_FORWARD
|
|
968
|
+
|
|
969
|
+
def op_RETURN_VALUE(self, inst):
|
|
970
|
+
self._curblock.terminating = True
|
|
971
|
+
self._force_new_block = True
|
|
972
|
+
|
|
973
|
+
if PYVERSION in ((3, 12), (3, 13)):
|
|
974
|
+
|
|
975
|
+
def op_RETURN_CONST(self, inst):
|
|
976
|
+
self._curblock.terminating = True
|
|
977
|
+
self._force_new_block = True
|
|
978
|
+
elif PYVERSION in ((3, 9), (3, 10), (3, 11)):
|
|
979
|
+
pass
|
|
980
|
+
else:
|
|
981
|
+
raise NotImplementedError(PYVERSION)
|
|
982
|
+
|
|
983
|
+
def op_RAISE_VARARGS(self, inst):
|
|
984
|
+
self._curblock.terminating = True
|
|
985
|
+
self._force_new_block = True
|
|
986
|
+
|
|
987
|
+
def op_BREAK_LOOP(self, inst):
|
|
988
|
+
self.jump(self._blockstack[-1])
|
|
989
|
+
self._force_new_block = True
|