numba-cuda 0.18.1__py3-none-any.whl → 0.19.1__py3-none-any.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.

Files changed (301) hide show
  1. _numba_cuda_redirector.pth +3 -0
  2. _numba_cuda_redirector.py +3 -0
  3. numba_cuda/VERSION +1 -1
  4. numba_cuda/__init__.py +2 -1
  5. numba_cuda/_version.py +2 -13
  6. numba_cuda/numba/cuda/__init__.py +4 -1
  7. numba_cuda/numba/cuda/_internal/cuda_bf16.py +5 -2
  8. numba_cuda/numba/cuda/_internal/cuda_fp16.py +4 -1
  9. numba_cuda/numba/cuda/api.py +5 -7
  10. numba_cuda/numba/cuda/api_util.py +3 -0
  11. numba_cuda/numba/cuda/args.py +3 -0
  12. numba_cuda/numba/cuda/bf16.py +3 -0
  13. numba_cuda/numba/cuda/cg.py +3 -0
  14. numba_cuda/numba/cuda/cgutils.py +3 -0
  15. numba_cuda/numba/cuda/codegen.py +3 -0
  16. numba_cuda/numba/cuda/compiler.py +10 -4
  17. numba_cuda/numba/cuda/core/caching.py +3 -0
  18. numba_cuda/numba/cuda/core/callconv.py +3 -0
  19. numba_cuda/numba/cuda/core/codegen.py +3 -0
  20. numba_cuda/numba/cuda/core/compiler.py +3 -0
  21. numba_cuda/numba/cuda/core/interpreter.py +3595 -0
  22. numba_cuda/numba/cuda/core/ir_utils.py +2644 -0
  23. numba_cuda/numba/cuda/core/sigutils.py +58 -0
  24. numba_cuda/numba/cuda/core/typed_passes.py +3 -0
  25. numba_cuda/numba/cuda/cuda_paths.py +12 -17
  26. numba_cuda/numba/cuda/cudadecl.py +4 -1
  27. numba_cuda/numba/cuda/cudadrv/__init__.py +3 -0
  28. numba_cuda/numba/cuda/cudadrv/devicearray.py +3 -0
  29. numba_cuda/numba/cuda/cudadrv/devices.py +3 -0
  30. numba_cuda/numba/cuda/cudadrv/driver.py +7 -19
  31. numba_cuda/numba/cuda/cudadrv/drvapi.py +3 -0
  32. numba_cuda/numba/cuda/cudadrv/dummyarray.py +3 -0
  33. numba_cuda/numba/cuda/cudadrv/enums.py +3 -0
  34. numba_cuda/numba/cuda/cudadrv/error.py +4 -0
  35. numba_cuda/numba/cuda/cudadrv/libs.py +4 -2
  36. numba_cuda/numba/cuda/cudadrv/linkable_code.py +3 -0
  37. numba_cuda/numba/cuda/cudadrv/mappings.py +3 -0
  38. numba_cuda/numba/cuda/cudadrv/ndarray.py +3 -0
  39. numba_cuda/numba/cuda/cudadrv/nvrtc.py +47 -44
  40. numba_cuda/numba/cuda/cudadrv/nvvm.py +6 -18
  41. numba_cuda/numba/cuda/cudadrv/rtapi.py +3 -0
  42. numba_cuda/numba/cuda/cudadrv/runtime.py +15 -1
  43. numba_cuda/numba/cuda/cudaimpl.py +3 -0
  44. numba_cuda/numba/cuda/cudamath.py +4 -1
  45. numba_cuda/numba/cuda/debuginfo.py +3 -0
  46. numba_cuda/numba/cuda/decorators.py +7 -3
  47. numba_cuda/numba/cuda/descriptor.py +3 -0
  48. numba_cuda/numba/cuda/device_init.py +3 -0
  49. numba_cuda/numba/cuda/deviceufunc.py +5 -1
  50. numba_cuda/numba/cuda/dispatcher.py +6 -2
  51. numba_cuda/numba/cuda/errors.py +10 -0
  52. numba_cuda/numba/cuda/extending.py +4 -1
  53. numba_cuda/numba/cuda/flags.py +2 -0
  54. numba_cuda/numba/cuda/fp16.py +3 -0
  55. numba_cuda/numba/cuda/initialize.py +4 -0
  56. numba_cuda/numba/cuda/intrinsic_wrapper.py +3 -0
  57. numba_cuda/numba/cuda/intrinsics.py +3 -0
  58. numba_cuda/numba/cuda/itanium_mangler.py +214 -0
  59. numba_cuda/numba/cuda/kernels/__init__.py +2 -0
  60. numba_cuda/numba/cuda/kernels/reduction.py +3 -0
  61. numba_cuda/numba/cuda/kernels/transpose.py +3 -0
  62. numba_cuda/numba/cuda/libdevice.py +4 -0
  63. numba_cuda/numba/cuda/libdevicedecl.py +4 -1
  64. numba_cuda/numba/cuda/libdevicefuncs.py +4 -1
  65. numba_cuda/numba/cuda/libdeviceimpl.py +3 -0
  66. numba_cuda/numba/cuda/locks.py +3 -0
  67. numba_cuda/numba/cuda/lowering.py +53 -16
  68. numba_cuda/numba/cuda/mathimpl.py +3 -0
  69. numba_cuda/numba/cuda/memory_management/__init__.py +3 -0
  70. numba_cuda/numba/cuda/memory_management/memsys.cu +5 -0
  71. numba_cuda/numba/cuda/memory_management/memsys.cuh +5 -0
  72. numba_cuda/numba/cuda/memory_management/nrt.cu +5 -0
  73. numba_cuda/numba/cuda/memory_management/nrt.cuh +5 -0
  74. numba_cuda/numba/cuda/memory_management/nrt.py +5 -1
  75. numba_cuda/numba/cuda/models.py +3 -0
  76. numba_cuda/numba/cuda/nvvmutils.py +3 -0
  77. numba_cuda/numba/cuda/printimpl.py +3 -0
  78. numba_cuda/numba/cuda/random.py +3 -0
  79. numba_cuda/numba/cuda/reshape_funcs.cu +5 -0
  80. numba_cuda/numba/cuda/serialize.py +3 -0
  81. numba_cuda/numba/cuda/simulator/__init__.py +3 -0
  82. numba_cuda/numba/cuda/simulator/_internal/__init__.py +3 -0
  83. numba_cuda/numba/cuda/simulator/_internal/cuda_bf16.py +2 -0
  84. numba_cuda/numba/cuda/simulator/api.py +4 -1
  85. numba_cuda/numba/cuda/simulator/bf16.py +3 -0
  86. numba_cuda/numba/cuda/simulator/compiler.py +3 -0
  87. numba_cuda/numba/cuda/simulator/cudadrv/__init__.py +3 -0
  88. numba_cuda/numba/cuda/simulator/cudadrv/devicearray.py +3 -0
  89. numba_cuda/numba/cuda/simulator/cudadrv/devices.py +3 -0
  90. numba_cuda/numba/cuda/simulator/cudadrv/driver.py +3 -7
  91. numba_cuda/numba/cuda/simulator/cudadrv/drvapi.py +3 -0
  92. numba_cuda/numba/cuda/simulator/cudadrv/dummyarray.py +3 -0
  93. numba_cuda/numba/cuda/simulator/cudadrv/error.py +4 -0
  94. numba_cuda/numba/cuda/simulator/cudadrv/libs.py +4 -0
  95. numba_cuda/numba/cuda/simulator/cudadrv/linkable_code.py +4 -0
  96. numba_cuda/numba/cuda/simulator/cudadrv/nvrtc.py +3 -0
  97. numba_cuda/numba/cuda/simulator/cudadrv/nvvm.py +3 -0
  98. numba_cuda/numba/cuda/simulator/cudadrv/runtime.py +3 -0
  99. numba_cuda/numba/cuda/simulator/dispatcher.py +4 -0
  100. numba_cuda/numba/cuda/simulator/kernel.py +3 -0
  101. numba_cuda/numba/cuda/simulator/kernelapi.py +3 -0
  102. numba_cuda/numba/cuda/simulator/memory_management/__init__.py +3 -0
  103. numba_cuda/numba/cuda/simulator/memory_management/nrt.py +3 -0
  104. numba_cuda/numba/cuda/simulator/reduction.py +3 -0
  105. numba_cuda/numba/cuda/simulator/vector_types.py +3 -0
  106. numba_cuda/numba/cuda/simulator_init.py +3 -0
  107. numba_cuda/numba/cuda/stubs.py +3 -0
  108. numba_cuda/numba/cuda/target.py +4 -2
  109. numba_cuda/numba/cuda/testing.py +7 -6
  110. numba_cuda/numba/cuda/tests/__init__.py +3 -0
  111. numba_cuda/numba/cuda/tests/complex_usecases.py +3 -0
  112. numba_cuda/numba/cuda/tests/core/serialize_usecases.py +3 -0
  113. numba_cuda/numba/cuda/tests/core/test_itanium_mangler.py +83 -0
  114. numba_cuda/numba/cuda/tests/core/test_serialize.py +3 -0
  115. numba_cuda/numba/cuda/tests/cudadrv/__init__.py +3 -0
  116. numba_cuda/numba/cuda/tests/cudadrv/test_array_attr.py +3 -0
  117. numba_cuda/numba/cuda/tests/cudadrv/test_context_stack.py +3 -0
  118. numba_cuda/numba/cuda/tests/cudadrv/test_cuda_array_slicing.py +3 -0
  119. numba_cuda/numba/cuda/tests/cudadrv/test_cuda_auto_context.py +3 -0
  120. numba_cuda/numba/cuda/tests/cudadrv/test_cuda_devicerecord.py +3 -0
  121. numba_cuda/numba/cuda/tests/cudadrv/test_cuda_driver.py +3 -0
  122. numba_cuda/numba/cuda/tests/cudadrv/test_cuda_libraries.py +3 -0
  123. numba_cuda/numba/cuda/tests/cudadrv/test_cuda_memory.py +3 -0
  124. numba_cuda/numba/cuda/tests/cudadrv/test_cuda_ndarray.py +4 -1
  125. numba_cuda/numba/cuda/tests/cudadrv/test_deallocations.py +4 -1
  126. numba_cuda/numba/cuda/tests/cudadrv/test_detect.py +4 -1
  127. numba_cuda/numba/cuda/tests/cudadrv/test_emm_plugins.py +4 -1
  128. numba_cuda/numba/cuda/tests/cudadrv/test_events.py +3 -0
  129. numba_cuda/numba/cuda/tests/cudadrv/test_host_alloc.py +3 -0
  130. numba_cuda/numba/cuda/tests/cudadrv/test_init.py +3 -0
  131. numba_cuda/numba/cuda/tests/cudadrv/test_inline_ptx.py +3 -0
  132. numba_cuda/numba/cuda/tests/cudadrv/test_is_fp16.py +3 -0
  133. numba_cuda/numba/cuda/tests/cudadrv/test_linker.py +4 -1
  134. numba_cuda/numba/cuda/tests/cudadrv/test_managed_alloc.py +4 -1
  135. numba_cuda/numba/cuda/tests/cudadrv/test_module_callbacks.py +3 -0
  136. numba_cuda/numba/cuda/tests/cudadrv/test_mvc.py +4 -1
  137. numba_cuda/numba/cuda/tests/cudadrv/test_nvjitlink.py +3 -0
  138. numba_cuda/numba/cuda/tests/cudadrv/test_nvrtc.py +7 -6
  139. numba_cuda/numba/cuda/tests/cudadrv/test_nvvm_driver.py +3 -4
  140. numba_cuda/numba/cuda/tests/cudadrv/test_pinned.py +3 -0
  141. numba_cuda/numba/cuda/tests/cudadrv/test_profiler.py +3 -0
  142. numba_cuda/numba/cuda/tests/cudadrv/test_ptds.py +4 -1
  143. numba_cuda/numba/cuda/tests/cudadrv/test_reset_device.py +3 -0
  144. numba_cuda/numba/cuda/tests/cudadrv/test_runtime.py +3 -0
  145. numba_cuda/numba/cuda/tests/cudadrv/test_select_device.py +3 -0
  146. numba_cuda/numba/cuda/tests/cudadrv/test_streams.py +3 -0
  147. numba_cuda/numba/cuda/tests/cudapy/__init__.py +3 -0
  148. numba_cuda/numba/cuda/tests/cudapy/cache_usecases.py +3 -0
  149. numba_cuda/numba/cuda/tests/cudapy/cache_with_cpu_usecases.py +3 -0
  150. numba_cuda/numba/cuda/tests/cudapy/cg_cache_usecases.py +3 -0
  151. numba_cuda/numba/cuda/tests/cudapy/extensions_usecases.py +3 -0
  152. numba_cuda/numba/cuda/tests/cudapy/recursion_usecases.py +3 -0
  153. numba_cuda/numba/cuda/tests/cudapy/test_alignment.py +3 -0
  154. numba_cuda/numba/cuda/tests/cudapy/test_array.py +3 -0
  155. numba_cuda/numba/cuda/tests/cudapy/test_array_alignment.py +3 -0
  156. numba_cuda/numba/cuda/tests/cudapy/test_array_args.py +3 -0
  157. numba_cuda/numba/cuda/tests/cudapy/test_array_methods.py +3 -0
  158. numba_cuda/numba/cuda/tests/cudapy/test_atomics.py +3 -0
  159. numba_cuda/numba/cuda/tests/cudapy/test_bfloat16.py +4 -3
  160. numba_cuda/numba/cuda/tests/cudapy/test_bfloat16_bindings.py +4 -3
  161. numba_cuda/numba/cuda/tests/cudapy/test_blackscholes.py +3 -0
  162. numba_cuda/numba/cuda/tests/cudapy/test_boolean.py +3 -0
  163. numba_cuda/numba/cuda/tests/cudapy/test_caching.py +149 -3
  164. numba_cuda/numba/cuda/tests/cudapy/test_casting.py +3 -0
  165. numba_cuda/numba/cuda/tests/cudapy/test_cffi.py +4 -1
  166. numba_cuda/numba/cuda/tests/cudapy/test_compiler.py +3 -4
  167. numba_cuda/numba/cuda/tests/cudapy/test_complex.py +3 -0
  168. numba_cuda/numba/cuda/tests/cudapy/test_complex_kernel.py +3 -0
  169. numba_cuda/numba/cuda/tests/cudapy/test_const_string.py +3 -0
  170. numba_cuda/numba/cuda/tests/cudapy/test_constmem.py +3 -0
  171. numba_cuda/numba/cuda/tests/cudapy/test_cooperative_groups.py +3 -0
  172. numba_cuda/numba/cuda/tests/cudapy/test_cuda_array_interface.py +4 -1
  173. numba_cuda/numba/cuda/tests/cudapy/test_cuda_jit_no_types.py +4 -1
  174. numba_cuda/numba/cuda/tests/cudapy/test_datetime.py +3 -0
  175. numba_cuda/numba/cuda/tests/cudapy/test_debug.py +4 -1
  176. numba_cuda/numba/cuda/tests/cudapy/test_debuginfo.py +23 -284
  177. numba_cuda/numba/cuda/tests/cudapy/test_debuginfo_types.py +476 -0
  178. numba_cuda/numba/cuda/tests/cudapy/test_device_func.py +4 -1
  179. numba_cuda/numba/cuda/tests/cudapy/test_dispatcher.py +3 -0
  180. numba_cuda/numba/cuda/tests/cudapy/test_enums.py +3 -0
  181. numba_cuda/numba/cuda/tests/cudapy/test_errors.py +4 -1
  182. numba_cuda/numba/cuda/tests/cudapy/test_exception.py +3 -0
  183. numba_cuda/numba/cuda/tests/cudapy/test_extending.py +4 -6
  184. numba_cuda/numba/cuda/tests/cudapy/test_fastmath.py +3 -0
  185. numba_cuda/numba/cuda/tests/cudapy/test_forall.py +3 -0
  186. numba_cuda/numba/cuda/tests/cudapy/test_freevar.py +3 -0
  187. numba_cuda/numba/cuda/tests/cudapy/test_frexp_ldexp.py +3 -0
  188. numba_cuda/numba/cuda/tests/cudapy/test_globals.py +3 -0
  189. numba_cuda/numba/cuda/tests/cudapy/test_gufunc.py +4 -1
  190. numba_cuda/numba/cuda/tests/cudapy/test_gufunc_scalar.py +3 -0
  191. numba_cuda/numba/cuda/tests/cudapy/test_gufunc_scheduling.py +3 -0
  192. numba_cuda/numba/cuda/tests/cudapy/test_idiv.py +3 -0
  193. numba_cuda/numba/cuda/tests/cudapy/test_inline.py +3 -0
  194. numba_cuda/numba/cuda/tests/cudapy/test_inspect.py +3 -0
  195. numba_cuda/numba/cuda/tests/cudapy/test_intrinsics.py +3 -0
  196. numba_cuda/numba/cuda/tests/cudapy/test_ipc.py +4 -1
  197. numba_cuda/numba/cuda/tests/cudapy/test_ir_utils.py +298 -0
  198. numba_cuda/numba/cuda/tests/cudapy/test_iterators.py +3 -0
  199. numba_cuda/numba/cuda/tests/cudapy/test_lang.py +3 -0
  200. numba_cuda/numba/cuda/tests/cudapy/test_laplace.py +3 -0
  201. numba_cuda/numba/cuda/tests/cudapy/test_libdevice.py +3 -0
  202. numba_cuda/numba/cuda/tests/cudapy/test_lineinfo.py +4 -1
  203. numba_cuda/numba/cuda/tests/cudapy/test_localmem.py +3 -0
  204. numba_cuda/numba/cuda/tests/cudapy/test_mandel.py +3 -0
  205. numba_cuda/numba/cuda/tests/cudapy/test_math.py +3 -0
  206. numba_cuda/numba/cuda/tests/cudapy/test_matmul.py +3 -0
  207. numba_cuda/numba/cuda/tests/cudapy/test_minmax.py +3 -0
  208. numba_cuda/numba/cuda/tests/cudapy/test_montecarlo.py +3 -0
  209. numba_cuda/numba/cuda/tests/cudapy/test_multigpu.py +3 -0
  210. numba_cuda/numba/cuda/tests/cudapy/test_multiprocessing.py +3 -0
  211. numba_cuda/numba/cuda/tests/cudapy/test_multithreads.py +3 -0
  212. numba_cuda/numba/cuda/tests/cudapy/test_nondet.py +3 -0
  213. numba_cuda/numba/cuda/tests/cudapy/test_operator.py +4 -1
  214. numba_cuda/numba/cuda/tests/cudapy/test_optimization.py +3 -0
  215. numba_cuda/numba/cuda/tests/cudapy/test_overload.py +3 -0
  216. numba_cuda/numba/cuda/tests/cudapy/test_powi.py +3 -0
  217. numba_cuda/numba/cuda/tests/cudapy/test_print.py +3 -0
  218. numba_cuda/numba/cuda/tests/cudapy/test_py2_div_issue.py +3 -0
  219. numba_cuda/numba/cuda/tests/cudapy/test_random.py +3 -0
  220. numba_cuda/numba/cuda/tests/cudapy/test_record_dtype.py +3 -0
  221. numba_cuda/numba/cuda/tests/cudapy/test_recursion.py +3 -0
  222. numba_cuda/numba/cuda/tests/cudapy/test_reduction.py +3 -0
  223. numba_cuda/numba/cuda/tests/cudapy/test_retrieve_autoconverted_arrays.py +3 -0
  224. numba_cuda/numba/cuda/tests/cudapy/test_serialize.py +3 -0
  225. numba_cuda/numba/cuda/tests/cudapy/test_slicing.py +3 -0
  226. numba_cuda/numba/cuda/tests/cudapy/test_sm.py +3 -0
  227. numba_cuda/numba/cuda/tests/cudapy/test_sm_creation.py +3 -0
  228. numba_cuda/numba/cuda/tests/cudapy/test_stream_api.py +3 -0
  229. numba_cuda/numba/cuda/tests/cudapy/test_sync.py +3 -0
  230. numba_cuda/numba/cuda/tests/cudapy/test_transpose.py +3 -0
  231. numba_cuda/numba/cuda/tests/cudapy/test_ufuncs.py +4 -1
  232. numba_cuda/numba/cuda/tests/cudapy/test_userexc.py +3 -0
  233. numba_cuda/numba/cuda/tests/cudapy/test_vector_type.py +3 -0
  234. numba_cuda/numba/cuda/tests/cudapy/test_vectorize.py +3 -0
  235. numba_cuda/numba/cuda/tests/cudapy/test_vectorize_complex.py +3 -0
  236. numba_cuda/numba/cuda/tests/cudapy/test_vectorize_decor.py +3 -0
  237. numba_cuda/numba/cuda/tests/cudapy/test_vectorize_device.py +3 -0
  238. numba_cuda/numba/cuda/tests/cudapy/test_vectorize_scalar_arg.py +3 -0
  239. numba_cuda/numba/cuda/tests/cudapy/test_warning.py +8 -1
  240. numba_cuda/numba/cuda/tests/cudapy/test_warp_ops.py +3 -0
  241. numba_cuda/numba/cuda/tests/cudasim/__init__.py +3 -0
  242. numba_cuda/numba/cuda/tests/cudasim/support.py +3 -0
  243. numba_cuda/numba/cuda/tests/cudasim/test_cudasim_issues.py +3 -0
  244. numba_cuda/numba/cuda/tests/data/__init__.py +2 -0
  245. numba_cuda/numba/cuda/tests/data/cta_barrier.cu +5 -0
  246. numba_cuda/numba/cuda/tests/data/cuda_include.cu +5 -0
  247. numba_cuda/numba/cuda/tests/data/error.cu +5 -0
  248. numba_cuda/numba/cuda/tests/data/include/add.cuh +5 -0
  249. numba_cuda/numba/cuda/tests/data/jitlink.cu +5 -0
  250. numba_cuda/numba/cuda/tests/data/warn.cu +5 -0
  251. numba_cuda/numba/cuda/tests/doc_examples/__init__.py +3 -0
  252. numba_cuda/numba/cuda/tests/doc_examples/ffi/__init__.py +2 -0
  253. numba_cuda/numba/cuda/tests/doc_examples/ffi/functions.cu +5 -0
  254. numba_cuda/numba/cuda/tests/doc_examples/ffi/include/mul.cuh +5 -0
  255. numba_cuda/numba/cuda/tests/doc_examples/ffi/saxpy.cu +5 -0
  256. numba_cuda/numba/cuda/tests/doc_examples/test_cg.py +3 -0
  257. numba_cuda/numba/cuda/tests/doc_examples/test_cpointer.py +4 -1
  258. numba_cuda/numba/cuda/tests/doc_examples/test_cpu_gpu_compat.py +4 -1
  259. numba_cuda/numba/cuda/tests/doc_examples/test_ffi.py +4 -1
  260. numba_cuda/numba/cuda/tests/doc_examples/test_laplace.py +4 -1
  261. numba_cuda/numba/cuda/tests/doc_examples/test_matmul.py +4 -1
  262. numba_cuda/numba/cuda/tests/doc_examples/test_montecarlo.py +4 -1
  263. numba_cuda/numba/cuda/tests/doc_examples/test_random.py +3 -0
  264. numba_cuda/numba/cuda/tests/doc_examples/test_reduction.py +4 -1
  265. numba_cuda/numba/cuda/tests/doc_examples/test_sessionize.py +4 -1
  266. numba_cuda/numba/cuda/tests/doc_examples/test_ufunc.py +4 -1
  267. numba_cuda/numba/cuda/tests/doc_examples/test_vecadd.py +4 -1
  268. numba_cuda/numba/cuda/tests/enum_usecases.py +3 -0
  269. numba_cuda/numba/cuda/tests/nocuda/__init__.py +3 -0
  270. numba_cuda/numba/cuda/tests/nocuda/test_dummyarray.py +3 -0
  271. numba_cuda/numba/cuda/tests/nocuda/test_function_resolution.py +3 -0
  272. numba_cuda/numba/cuda/tests/nocuda/test_import.py +4 -1
  273. numba_cuda/numba/cuda/tests/nocuda/test_library_lookup.py +3 -0
  274. numba_cuda/numba/cuda/tests/nocuda/test_nvvm.py +3 -0
  275. numba_cuda/numba/cuda/tests/nrt/__init__.py +3 -0
  276. numba_cuda/numba/cuda/tests/nrt/test_nrt.py +5 -2
  277. numba_cuda/numba/cuda/tests/nrt/test_nrt_refct.py +4 -1
  278. numba_cuda/numba/cuda/tests/support.py +755 -0
  279. numba_cuda/numba/cuda/tests/test_binary_generation/Makefile +6 -3
  280. numba_cuda/numba/cuda/tests/test_binary_generation/generate_raw_ltoir.py +6 -2
  281. numba_cuda/numba/cuda/tests/test_binary_generation/nrt_extern.cu +5 -0
  282. numba_cuda/numba/cuda/tests/test_binary_generation/test_device_functions.cu +5 -0
  283. numba_cuda/numba/cuda/tests/test_binary_generation/undefined_extern.cu +5 -0
  284. numba_cuda/numba/cuda/types.py +3 -0
  285. numba_cuda/numba/cuda/typing/__init__.py +11 -0
  286. numba_cuda/numba/cuda/typing/templates.py +1448 -0
  287. numba_cuda/numba/cuda/ufuncs.py +3 -0
  288. numba_cuda/numba/cuda/utils.py +3 -0
  289. numba_cuda/numba/cuda/vector_types.py +6 -3
  290. numba_cuda/numba/cuda/vectorizers.py +3 -0
  291. {numba_cuda-0.18.1.dist-info → numba_cuda-0.19.1.dist-info}/METADATA +25 -29
  292. numba_cuda-0.19.1.dist-info/RECORD +302 -0
  293. {numba_cuda-0.18.1.dist-info → numba_cuda-0.19.1.dist-info}/licenses/LICENSE +1 -0
  294. numba_cuda-0.19.1.dist-info/licenses/LICENSE.numba +24 -0
  295. numba_cuda/numba/cuda/include/11/cuda_bf16.h +0 -3749
  296. numba_cuda/numba/cuda/include/11/cuda_bf16.hpp +0 -2683
  297. numba_cuda/numba/cuda/include/11/cuda_fp16.h +0 -3794
  298. numba_cuda/numba/cuda/include/11/cuda_fp16.hpp +0 -2614
  299. numba_cuda-0.18.1.dist-info/RECORD +0 -296
  300. {numba_cuda-0.18.1.dist-info → numba_cuda-0.19.1.dist-info}/WHEEL +0 -0
  301. {numba_cuda-0.18.1.dist-info → numba_cuda-0.19.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,3595 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
+ # SPDX-License-Identifier: BSD-2-Clause
3
+
4
+ import builtins
5
+ import collections
6
+ import dis
7
+ import operator
8
+ import logging
9
+ import textwrap
10
+
11
+ from numba.core import errors, ir, config
12
+ from numba.cuda.errors import UnsupportedBytecodeError
13
+ from numba.core.errors import (
14
+ NotDefinedError,
15
+ error_extras,
16
+ )
17
+ from numba.cuda.core import ir_utils
18
+ from numba.core.utils import PYVERSION
19
+ from numba.cuda.utils import (
20
+ BINOPS_TO_OPERATORS,
21
+ INPLACE_BINOPS_TO_OPERATORS,
22
+ _lazy_pformat,
23
+ )
24
+ from numba.core.byteflow import Flow, AdaptDFA, AdaptCFA, BlockKind
25
+ from numba.core.unsafe import eh
26
+ from numba.cpython.unsafe.tuple import unpack_single_tuple
27
+
28
+
29
+ if PYVERSION in ((3, 12), (3, 13)):
30
+ # Operands for CALL_INTRINSIC_1
31
+ from numba.core.byteflow import CALL_INTRINSIC_1_Operand as ci1op
32
+ elif PYVERSION in ((3, 10), (3, 11)):
33
+ pass
34
+ else:
35
+ raise NotImplementedError(PYVERSION)
36
+
37
+
38
+ class _UNKNOWN_VALUE(object):
39
+ """Represents an unknown value, this is for ease of debugging purposes only."""
40
+
41
+ def __init__(self, varname):
42
+ self._varname = varname
43
+
44
+ def __repr__(self):
45
+ return "_UNKNOWN_VALUE({})".format(self._varname)
46
+
47
+
48
+ _logger = logging.getLogger(__name__)
49
+
50
+
51
+ class Assigner(object):
52
+ """
53
+ This object keeps track of potential assignment simplifications
54
+ inside a code block.
55
+ For example `$O.1 = x` followed by `y = $0.1` can be simplified
56
+ into `y = x`, but it's not possible anymore if we have `x = z`
57
+ in-between those two instructions.
58
+
59
+ NOTE: this is not only an optimization, but is actually necessary
60
+ due to certain limitations of Numba - such as only accepting the
61
+ returning of an array passed as function argument.
62
+ """
63
+
64
+ def __init__(self):
65
+ # { destination variable name -> source Var object }
66
+ self.dest_to_src = {}
67
+ # Basically a reverse mapping of dest_to_src:
68
+ # { source variable name -> all destination names in dest_to_src }
69
+ self.src_invalidate = collections.defaultdict(list)
70
+ self.unused_dests = set()
71
+
72
+ def assign(self, srcvar, destvar):
73
+ """
74
+ Assign *srcvar* to *destvar*. Return either *srcvar* or a possible
75
+ simplified assignment source (earlier assigned to *srcvar*).
76
+ """
77
+ srcname = srcvar.name
78
+ destname = destvar.name
79
+ if destname in self.src_invalidate:
80
+ # destvar will change, invalidate all previously known
81
+ # simplifications
82
+ for d in self.src_invalidate.pop(destname):
83
+ self.dest_to_src.pop(d)
84
+ if srcname in self.dest_to_src:
85
+ srcvar = self.dest_to_src[srcname]
86
+ if destvar.is_temp:
87
+ self.dest_to_src[destname] = srcvar
88
+ self.src_invalidate[srcname].append(destname)
89
+ self.unused_dests.add(destname)
90
+ return srcvar
91
+
92
+ def get_assignment_source(self, destname):
93
+ """
94
+ Get a possible assignment source (a ir.Var instance) to replace
95
+ *destname*, otherwise None.
96
+ """
97
+ if destname in self.dest_to_src:
98
+ return self.dest_to_src[destname]
99
+ self.unused_dests.discard(destname)
100
+ return None
101
+
102
+
103
+ def _remove_assignment_definition(old_body, idx, func_ir, already_deleted_defs):
104
+ """
105
+ Deletes the definition defined for old_body at index idx
106
+ from func_ir. We assume this stmt will be deleted from
107
+ new_body.
108
+
109
+ In some optimizations we may update the same variable multiple times.
110
+ In this situation, we only need to delete a particular definition once,
111
+ this is tracked in already_deleted_def, which is a map from
112
+ assignment name to the set of values that have already been
113
+ deleted.
114
+ """
115
+ lhs = old_body[idx].target.name
116
+ rhs = old_body[idx].value
117
+ if rhs in func_ir._definitions[lhs]:
118
+ func_ir._definitions[lhs].remove(rhs)
119
+ already_deleted_defs[lhs].add(rhs)
120
+ elif rhs not in already_deleted_defs[lhs]:
121
+ raise UnsupportedBytecodeError(
122
+ "Inconsistency found in the definitions while executing"
123
+ " a peephole optimization. This suggests an internal"
124
+ " error or inconsistency elsewhere in the compiler."
125
+ )
126
+
127
+
128
+ def _call_function_ex_replace_kws_small(
129
+ old_body,
130
+ keyword_expr,
131
+ new_body,
132
+ buildmap_idx,
133
+ func_ir,
134
+ already_deleted_defs,
135
+ ):
136
+ """
137
+ Extracts the kws args passed as varkwarg
138
+ for CALL_FUNCTION_EX. This pass is taken when
139
+ n_kws <= 15 and the bytecode looks like:
140
+
141
+ # Start for each argument
142
+ LOAD_FAST # Load each argument.
143
+ # End for each argument
144
+ ...
145
+ BUILD_CONST_KEY_MAP # Build a map
146
+
147
+ In the generated IR, the varkwarg refers
148
+ to a single build_map that contains all of the
149
+ kws. In addition to returning the kws, this
150
+ function updates new_body to remove all usage
151
+ of the map.
152
+ """
153
+ kws = keyword_expr.items.copy()
154
+ # kws are required to have constant keys.
155
+ # We update these with the value_indexes
156
+ value_indexes = keyword_expr.value_indexes
157
+ for key, index in value_indexes.items():
158
+ kws[index] = (key, kws[index][1])
159
+ # Remove the build_map by setting the list
160
+ # index to None. Nones will be removed later.
161
+ new_body[buildmap_idx] = None
162
+ # Remove the definition.
163
+ _remove_assignment_definition(
164
+ old_body, buildmap_idx, func_ir, already_deleted_defs
165
+ )
166
+ return kws
167
+
168
+
169
+ def _call_function_ex_replace_kws_large(
170
+ old_body,
171
+ buildmap_name,
172
+ buildmap_idx,
173
+ search_end,
174
+ new_body,
175
+ func_ir,
176
+ errmsg,
177
+ already_deleted_defs,
178
+ ):
179
+ """
180
+ Extracts the kws args passed as varkwarg
181
+ for CALL_FUNCTION_EX. This pass is taken when
182
+ n_kws > 15 and the bytecode looks like:
183
+
184
+ BUILD_MAP # Construct the map
185
+ # Start for each argument
186
+ LOAD_CONST # Load a constant for the name of the argument
187
+ LOAD_FAST # Load each argument.
188
+ MAP_ADD # Append the (key, value) pair to the map
189
+ # End for each argument
190
+
191
+ In the IR generated, the initial build map is empty and a series
192
+ of setitems are applied afterwards. THE IR looks like:
193
+
194
+ $build_map_var = build_map(items=[])
195
+ $constvar = const(str, ...) # create the const key
196
+ # CREATE THE ARGUMENT, This may take multiple lines.
197
+ $created_arg = ...
198
+ $var = getattr(
199
+ value=$build_map_var,
200
+ attr=__setitem__,
201
+ )
202
+ $unused_var = call $var($constvar, $created_arg)
203
+
204
+ We iterate through the IR, deleting all usages of the buildmap
205
+ from the new_body, and adds the kws to a new kws list.
206
+ """
207
+ # Remove the build_map from the body.
208
+ new_body[buildmap_idx] = None
209
+ # Remove the definition.
210
+ _remove_assignment_definition(
211
+ old_body, buildmap_idx, func_ir, already_deleted_defs
212
+ )
213
+ kws = []
214
+ search_start = buildmap_idx + 1
215
+ while search_start <= search_end:
216
+ # The first value must be a constant.
217
+ const_stmt = old_body[search_start]
218
+ if not (
219
+ isinstance(const_stmt, ir.Assign)
220
+ and isinstance(const_stmt.value, ir.Const)
221
+ ):
222
+ # We cannot handle this format so raise the
223
+ # original error message.
224
+ raise UnsupportedBytecodeError(errmsg)
225
+ key_var_name = const_stmt.target.name
226
+ key_val = const_stmt.value.value
227
+ search_start += 1
228
+ # Now we need to search for a getattr with setitem
229
+ found_getattr = False
230
+ while search_start <= search_end and not found_getattr:
231
+ getattr_stmt = old_body[search_start]
232
+ if (
233
+ isinstance(getattr_stmt, ir.Assign)
234
+ and isinstance(getattr_stmt.value, ir.Expr)
235
+ and getattr_stmt.value.op == "getattr"
236
+ and (getattr_stmt.value.value.name == buildmap_name)
237
+ and getattr_stmt.value.attr == "__setitem__"
238
+ ):
239
+ found_getattr = True
240
+ else:
241
+ # If the argument is "created" in JIT, then there
242
+ # will be intermediate operations in between setitems.
243
+ # For example we have arg5=pow(arg5, 2),
244
+ # then the IR would look like:
245
+ #
246
+ # # Creation of the constant key.
247
+ # $const44.26 = const(str, arg5)
248
+ #
249
+ # # Argument creation. This is the section we are skipping
250
+ # $46load_global.27 = global(pow: <built-in function pow>)
251
+ # $const50.29 = const(int, 2)
252
+ # $call.30 = call $46load_global.27(arg5, $const50.29)
253
+ #
254
+ # # Setitem with arg5
255
+ # $54map_add.31 = getattr(value=$map.2, attr=__setitem__)
256
+ # $54map_add.32 = call $54map_add.31($const44.26, $call.30)
257
+ search_start += 1
258
+ if not found_getattr or search_start == search_end:
259
+ # We cannot handle this format so raise the
260
+ # original error message.
261
+ raise UnsupportedBytecodeError(errmsg)
262
+ setitem_stmt = old_body[search_start + 1]
263
+ if not (
264
+ isinstance(setitem_stmt, ir.Assign)
265
+ and isinstance(setitem_stmt.value, ir.Expr)
266
+ and setitem_stmt.value.op == "call"
267
+ and (setitem_stmt.value.func.name == getattr_stmt.target.name)
268
+ and len(setitem_stmt.value.args) == 2
269
+ and (setitem_stmt.value.args[0].name == key_var_name)
270
+ ):
271
+ # A call statement should always immediately follow the
272
+ # getattr. If for some reason this doesn't match the code
273
+ # format, we raise the original error message. This check
274
+ # is meant as a precaution.
275
+ raise UnsupportedBytecodeError(errmsg)
276
+ arg_var = setitem_stmt.value.args[1]
277
+ # Append the (key, value) pair.
278
+ kws.append((key_val, arg_var))
279
+ # Remove the __setitem__ getattr and call
280
+ new_body[search_start] = None
281
+ new_body[search_start + 1] = None
282
+ # Remove the definitions.
283
+ _remove_assignment_definition(
284
+ old_body, search_start, func_ir, already_deleted_defs
285
+ )
286
+ _remove_assignment_definition(
287
+ old_body, search_start + 1, func_ir, already_deleted_defs
288
+ )
289
+ search_start += 2
290
+ return kws
291
+
292
+
293
+ def _call_function_ex_replace_args_small(
294
+ old_body,
295
+ tuple_expr,
296
+ new_body,
297
+ buildtuple_idx,
298
+ func_ir,
299
+ already_deleted_defs,
300
+ ):
301
+ """
302
+ Extracts the args passed as vararg
303
+ for CALL_FUNCTION_EX. This pass is taken when
304
+ n_args <= 30 and the bytecode looks like:
305
+
306
+ # Start for each argument
307
+ LOAD_FAST # Load each argument.
308
+ # End for each argument
309
+ ...
310
+ BUILD_TUPLE # Create a tuple of the arguments
311
+
312
+ In the IR generated, the vararg refer
313
+ to a single build_tuple that contains all of the
314
+ args. In addition to returning the args, this
315
+ function updates new_body to remove all usage
316
+ of the tuple.
317
+ """
318
+ # Delete the build tuple
319
+ new_body[buildtuple_idx] = None
320
+ # Remove the definition.
321
+ _remove_assignment_definition(
322
+ old_body, buildtuple_idx, func_ir, already_deleted_defs
323
+ )
324
+ # Return the args.
325
+ return tuple_expr.items
326
+
327
+
328
+ def _call_function_ex_replace_args_large(
329
+ old_body,
330
+ vararg_stmt,
331
+ new_body,
332
+ search_end,
333
+ func_ir,
334
+ errmsg,
335
+ already_deleted_defs,
336
+ ):
337
+ """
338
+ Extracts the args passed as vararg
339
+ for CALL_FUNCTION_EX. This pass is taken when
340
+ n_args > 30 and the bytecode looks like:
341
+
342
+ BUILD_TUPLE # Create a list to append to
343
+ # Start for each argument
344
+ LOAD_FAST # Load each argument.
345
+ LIST_APPEND # Add the argument to the list
346
+ # End for each argument
347
+ ...
348
+ LIST_TO_TUPLE # Convert the args to a tuple.
349
+
350
+ In the IR generated, the tuple is created by concatenating
351
+ together several 1 element tuples to an initial empty tuple.
352
+ We traverse backwards in the IR, collecting args, until we
353
+ find the original empty tuple. For example, the IR might
354
+ look like:
355
+
356
+ $orig_tuple = build_tuple(items=[])
357
+ $first_var = build_tuple(items=[Var(arg0, test.py:6)])
358
+ $next_tuple = $orig_tuple + $first_var
359
+ ...
360
+ $final_var = build_tuple(items=[Var(argn, test.py:6)])
361
+ $final_tuple = $prev_tuple + $final_var
362
+ $varargs_var = $final_tuple
363
+ """
364
+ # We traverse to the front of the block to look for the original
365
+ # tuple.
366
+ search_start = 0
367
+ total_args = []
368
+ if isinstance(vararg_stmt, ir.Assign) and isinstance(
369
+ vararg_stmt.value, ir.Var
370
+ ):
371
+ target_name = vararg_stmt.value.name
372
+ # If there is an initial assignment, delete it
373
+ new_body[search_end] = None
374
+ # Remove the definition.
375
+ _remove_assignment_definition(
376
+ old_body, search_end, func_ir, already_deleted_defs
377
+ )
378
+ search_end -= 1
379
+ else:
380
+ # There must always be an initial assignment
381
+ # https://github.com/numba/numba/blob/59fa2e335be68148b3bd72a29de3ff011430038d/numba/core/interpreter.py#L259-L260
382
+ # If this changes we may need to support this branch.
383
+ raise AssertionError("unreachable")
384
+ # Traverse backwards to find all concatenations
385
+ # until eventually reaching the original empty tuple.
386
+ while search_end >= search_start:
387
+ concat_stmt = old_body[search_end]
388
+ if (
389
+ isinstance(concat_stmt, ir.Assign)
390
+ and concat_stmt.target.name == target_name
391
+ and isinstance(concat_stmt.value, ir.Expr)
392
+ and concat_stmt.value.op == "build_tuple"
393
+ and not concat_stmt.value.items
394
+ ):
395
+ new_body[search_end] = None
396
+ # Remove the definition.
397
+ _remove_assignment_definition(
398
+ old_body, search_end, func_ir, already_deleted_defs
399
+ )
400
+ # If we have reached the build_tuple we exit.
401
+ break
402
+ else:
403
+ # We expect to find another arg to append.
404
+ # The first stmt must be a binop "add"
405
+ if (search_end == search_start) or not (
406
+ isinstance(concat_stmt, ir.Assign)
407
+ and (concat_stmt.target.name == target_name)
408
+ and isinstance(concat_stmt.value, ir.Expr)
409
+ and concat_stmt.value.op == "binop"
410
+ and concat_stmt.value.fn == operator.add
411
+ ):
412
+ # We cannot handle this format.
413
+ raise UnsupportedBytecodeError(errmsg)
414
+ lhs_name = concat_stmt.value.lhs.name
415
+ rhs_name = concat_stmt.value.rhs.name
416
+ # The previous statement should be a
417
+ # build_tuple containing the arg.
418
+ arg_tuple_stmt = old_body[search_end - 1]
419
+ if not (
420
+ isinstance(arg_tuple_stmt, ir.Assign)
421
+ and isinstance(arg_tuple_stmt.value, ir.Expr)
422
+ and (arg_tuple_stmt.value.op == "build_tuple")
423
+ and len(arg_tuple_stmt.value.items) == 1
424
+ ):
425
+ # We cannot handle this format.
426
+ raise UnsupportedBytecodeError(errmsg)
427
+ if arg_tuple_stmt.target.name == lhs_name:
428
+ # The tuple should always be generated on the RHS.
429
+ raise AssertionError("unreachable")
430
+ elif arg_tuple_stmt.target.name == rhs_name:
431
+ target_name = lhs_name
432
+ else:
433
+ # We cannot handle this format.
434
+ raise UnsupportedBytecodeError(errmsg)
435
+ total_args.append(arg_tuple_stmt.value.items[0])
436
+ new_body[search_end] = None
437
+ new_body[search_end - 1] = None
438
+ # Remove the definitions.
439
+ _remove_assignment_definition(
440
+ old_body, search_end, func_ir, already_deleted_defs
441
+ )
442
+ _remove_assignment_definition(
443
+ old_body, search_end - 1, func_ir, already_deleted_defs
444
+ )
445
+ search_end -= 2
446
+ # Avoid any space between appends
447
+ keep_looking = True
448
+ while search_end >= search_start and keep_looking:
449
+ next_stmt = old_body[search_end]
450
+ if isinstance(next_stmt, ir.Assign) and (
451
+ next_stmt.target.name == target_name
452
+ ):
453
+ keep_looking = False
454
+ else:
455
+ # If the argument is "created" in JIT, then there
456
+ # will be intermediate operations in between appends.
457
+ # For example if the next arg after arg4 is pow(arg5, 2),
458
+ # then the IR would look like:
459
+ #
460
+ # # Appending arg4
461
+ # $arg4_tup = build_tuple(items=[arg4])
462
+ # $append_var.5 = $append_var.4 + $arg4_tup
463
+ #
464
+ # # Creation of arg5.
465
+ # # This is the section that we are skipping.
466
+ # $32load_global.20 = global(pow: <built-in function pow>)
467
+ # $const36.22 = const(int, 2)
468
+ # $call.23 = call $32load_global.20(arg5, $const36.22)
469
+ #
470
+ # # Appending arg5
471
+ # $arg5_tup = build_tuple(items=[$call.23])
472
+ # $append_var.6 = $append_var.5 + $arg5_tup
473
+ search_end -= 1
474
+ if search_end == search_start:
475
+ # If we reached the start we never found the build_tuple.
476
+ # We cannot handle this format so raise the
477
+ # original error message.
478
+ raise UnsupportedBytecodeError(errmsg)
479
+ # Reverse the arguments so we get the correct order.
480
+ return total_args[::-1]
481
+
482
+
483
+ def peep_hole_call_function_ex_to_call_function_kw(func_ir):
484
+ """
485
+ This peephole rewrites a bytecode sequence unique to Python 3.10
486
+ where CALL_FUNCTION_EX is used instead of CALL_FUNCTION_KW because of
487
+ stack limitations set by CPython. This limitation is imposed whenever
488
+ a function call has too many arguments or keyword arguments.
489
+
490
+ https://github.com/python/cpython/blob/a58ebcc701dd6c43630df941481475ff0f615a81/Python/compile.c#L55
491
+ https://github.com/python/cpython/blob/a58ebcc701dd6c43630df941481475ff0f615a81/Python/compile.c#L4442
492
+
493
+ In particular, this change is imposed whenever (n_args / 2) + n_kws > 15.
494
+
495
+ Different bytecode is generated for args depending on if n_args > 30
496
+ or n_args <= 30 and similarly if n_kws > 15 or n_kws <= 15.
497
+
498
+ This function unwraps the *args and **kwargs in the function call
499
+ and places these values directly into the args and kwargs of the call.
500
+ """
501
+ # All changes are local to the a single block
502
+ # so it can be traversed in any order.
503
+ errmsg = textwrap.dedent("""
504
+ CALL_FUNCTION_EX with **kwargs not supported.
505
+ If you are not using **kwargs this may indicate that
506
+ you have a large number of kwargs and are using inlined control
507
+ flow. You can resolve this issue by moving the control flow out of
508
+ the function call. For example, if you have
509
+
510
+ f(a=1 if flag else 0, ...)
511
+
512
+ Replace that with:
513
+
514
+ a_val = 1 if flag else 0
515
+ f(a=a_val, ...)""")
516
+
517
+ # Track which definitions have already been deleted
518
+ already_deleted_defs = collections.defaultdict(set)
519
+ for blk in func_ir.blocks.values():
520
+ blk_changed = False
521
+ new_body = []
522
+ for i, stmt in enumerate(blk.body):
523
+ if (
524
+ isinstance(stmt, ir.Assign)
525
+ and isinstance(stmt.value, ir.Expr)
526
+ and stmt.value.op == "call"
527
+ and stmt.value.varkwarg is not None
528
+ ):
529
+ blk_changed = True
530
+ call = stmt.value
531
+ args = call.args
532
+ kws = call.kws
533
+ # We need to check the call expression contents if
534
+ # it contains either vararg or varkwarg. If it contains
535
+ # varkwarg we need to update the IR. If it just contains
536
+ # vararg we don't need to update the IR, but we need to
537
+ # check if peep_hole_list_to_tuple failed to replace the
538
+ # vararg list with a tuple. If so, we output an error
539
+ # message with suggested code changes.
540
+ vararg = call.vararg
541
+ varkwarg = call.varkwarg
542
+ start_search = i - 1
543
+ # varkwarg should be defined second so we start there.
544
+ varkwarg_loc = start_search
545
+ keyword_def = None
546
+ found = False
547
+ while varkwarg_loc >= 0 and not found:
548
+ keyword_def = blk.body[varkwarg_loc]
549
+ if (
550
+ isinstance(keyword_def, ir.Assign)
551
+ and keyword_def.target.name == varkwarg.name
552
+ ):
553
+ found = True
554
+ else:
555
+ varkwarg_loc -= 1
556
+ if (
557
+ kws
558
+ or not found
559
+ or not (
560
+ isinstance(keyword_def.value, ir.Expr)
561
+ and keyword_def.value.op == "build_map"
562
+ )
563
+ ):
564
+ # If we couldn't find where the kwargs are created
565
+ # then it should be a normal **kwargs call
566
+ # so we produce an unsupported message.
567
+ raise UnsupportedBytecodeError(errmsg)
568
+ # Determine the kws
569
+ if keyword_def.value.items:
570
+ # n_kws <= 15 case.
571
+ # Here the IR looks like a series of
572
+ # constants, then the arguments and finally
573
+ # a build_map that contains all of the pairs.
574
+ # For Example:
575
+ #
576
+ # $const_n = const("arg_name")
577
+ # $arg_n = ...
578
+ # $kwargs_var = build_map(items=[
579
+ # ($const_0, $arg_0),
580
+ # ...,
581
+ # ($const_n, $arg_n),])
582
+ kws = _call_function_ex_replace_kws_small(
583
+ blk.body,
584
+ keyword_def.value,
585
+ new_body,
586
+ varkwarg_loc,
587
+ func_ir,
588
+ already_deleted_defs,
589
+ )
590
+ else:
591
+ # n_kws > 15 case.
592
+ # Here the IR is an initial empty build_map
593
+ # followed by a series of setitems with a constant
594
+ # key and then the argument.
595
+ # For example:
596
+ #
597
+ # $kwargs_var = build_map(items=[])
598
+ # $const_0 = const("arg_name")
599
+ # $arg_0 = ...
600
+ # $my_attr = getattr(const_0, attr=__setitem__)
601
+ # $unused_var = call $my_attr($const_0, $arg_0)
602
+ # ...
603
+ kws = _call_function_ex_replace_kws_large(
604
+ blk.body,
605
+ varkwarg.name,
606
+ varkwarg_loc,
607
+ i - 1,
608
+ new_body,
609
+ func_ir,
610
+ errmsg,
611
+ already_deleted_defs,
612
+ )
613
+ start_search = varkwarg_loc
614
+ # Vararg isn't required to be provided.
615
+ if vararg is not None:
616
+ if args:
617
+ # If we have vararg then args is expected to
618
+ # be an empty list.
619
+ raise UnsupportedBytecodeError(errmsg)
620
+ vararg_loc = start_search
621
+ args_def = None
622
+ found = False
623
+ while vararg_loc >= 0 and not found:
624
+ args_def = blk.body[vararg_loc]
625
+ if (
626
+ isinstance(args_def, ir.Assign)
627
+ and args_def.target.name == vararg.name
628
+ ):
629
+ found = True
630
+ else:
631
+ vararg_loc -= 1
632
+ if not found:
633
+ # If we couldn't find where the args are created
634
+ # then we can't handle this format.
635
+ raise UnsupportedBytecodeError(errmsg)
636
+ if (
637
+ isinstance(args_def.value, ir.Expr)
638
+ and args_def.value.op == "build_tuple"
639
+ ):
640
+ # n_args <= 30 case.
641
+ # Here the IR is a simple build_tuple containing
642
+ # all of the args.
643
+ # For example:
644
+ #
645
+ # $arg_n = ...
646
+ # $varargs = build_tuple(
647
+ # items=[$arg_0, ..., $arg_n]
648
+ # )
649
+ args = _call_function_ex_replace_args_small(
650
+ blk.body,
651
+ args_def.value,
652
+ new_body,
653
+ vararg_loc,
654
+ func_ir,
655
+ already_deleted_defs,
656
+ )
657
+ elif (
658
+ isinstance(args_def.value, ir.Expr)
659
+ and args_def.value.op == "list_to_tuple"
660
+ ):
661
+ # If there is a call with vararg we need to check
662
+ # if the list -> tuple conversion failed and if so
663
+ # throw an error.
664
+ raise UnsupportedBytecodeError(errmsg)
665
+ else:
666
+ # Here the IR is an initial empty build_tuple.
667
+ # Then for each arg, a new tuple with a single
668
+ # element is created and one by one these are
669
+ # added to a growing tuple.
670
+ # For example:
671
+ #
672
+ # $combo_tup_0 = build_tuple(items=[])
673
+ # $arg0 = ...
674
+ # $arg0_tup = build_tuple(items=[$arg0])
675
+ # $combo_tup_1 = $combo_tup_0 + $arg0_tup
676
+ # $arg1 = ...
677
+ # $arg1_tup = build_tuple(items=[$arg1])
678
+ # $combo_tup_2 = $combo_tup_1 + $arg1_tup
679
+ # ...
680
+ # $combo_tup_n = $combo_tup_{n-1} + $argn_tup
681
+ #
682
+ # In addition, the IR contains a final
683
+ # assignment for the varargs that looks like:
684
+ #
685
+ # $varargs_var = $combo_tup_n
686
+ #
687
+ # Here args_def is expected to be a simple assignment.
688
+ args = _call_function_ex_replace_args_large(
689
+ blk.body,
690
+ args_def,
691
+ new_body,
692
+ vararg_loc,
693
+ func_ir,
694
+ errmsg,
695
+ already_deleted_defs,
696
+ )
697
+ # Create a new call updating the args and kws
698
+ new_call = ir.Expr.call(
699
+ call.func, args, kws, call.loc, target=call.target
700
+ )
701
+ # Drop the existing definition for this stmt.
702
+ _remove_assignment_definition(
703
+ blk.body, i, func_ir, already_deleted_defs
704
+ )
705
+ # Update the statement
706
+ stmt = ir.Assign(new_call, stmt.target, stmt.loc)
707
+ # Update the definition
708
+ func_ir._definitions[stmt.target.name].append(new_call)
709
+ elif (
710
+ isinstance(stmt, ir.Assign)
711
+ and isinstance(stmt.value, ir.Expr)
712
+ and stmt.value.op == "call"
713
+ and stmt.value.vararg is not None
714
+ ):
715
+ # If there is a call with vararg we need to check
716
+ # if the list -> tuple conversion failed and if so
717
+ # throw an error.
718
+ call = stmt.value
719
+ vararg_name = call.vararg.name
720
+ if (
721
+ vararg_name in func_ir._definitions
722
+ and len(func_ir._definitions[vararg_name]) == 1
723
+ ):
724
+ # If this value is still a list to tuple raise the
725
+ # exception.
726
+ expr = func_ir._definitions[vararg_name][0]
727
+ if isinstance(expr, ir.Expr) and expr.op == "list_to_tuple":
728
+ raise UnsupportedBytecodeError(errmsg)
729
+
730
+ new_body.append(stmt)
731
+ # Replace the block body if we changed the IR
732
+ if blk_changed:
733
+ blk.body.clear()
734
+ blk.body.extend([x for x in new_body if x is not None])
735
+ return func_ir
736
+
737
+
738
+ def peep_hole_list_to_tuple(func_ir):
739
+ """
740
+ This peephole rewrites a bytecode sequence new to Python 3.9 that looks
741
+ like e.g.:
742
+
743
+ def foo(a):
744
+ return (*a,)
745
+
746
+ 41 0 BUILD_LIST 0
747
+ 2 LOAD_FAST 0 (a)
748
+ 4 LIST_EXTEND 1
749
+ 6 LIST_TO_TUPLE
750
+ 8 RETURN_VAL
751
+
752
+ essentially, the unpacking of tuples is written as a list which is appended
753
+ to/extended and then "magicked" into a tuple by the new LIST_TO_TUPLE
754
+ opcode.
755
+
756
+ This peephole repeatedly analyses the bytecode in a block looking for a
757
+ window between a `LIST_TO_TUPLE` and `BUILD_LIST` and...
758
+
759
+ 1. Turns the BUILD_LIST into a BUILD_TUPLE
760
+ 2. Sets an accumulator's initial value as the target of the BUILD_TUPLE
761
+ 3. Searches for 'extend' on the original list and turns these into binary
762
+ additions on the accumulator.
763
+ 4. Searches for 'append' on the original list and turns these into a
764
+ `BUILD_TUPLE` which is then appended via binary addition to the
765
+ accumulator.
766
+ 5. Assigns the accumulator to the variable that exits the peephole and the
767
+ rest of the block/code refers to as the result of the unpack operation.
768
+ 6. Patches up
769
+ """
770
+ _DEBUG = False
771
+
772
+ # For all blocks
773
+ for offset, blk in func_ir.blocks.items():
774
+ # keep doing the peephole rewrite until nothing is left that matches
775
+ while True:
776
+ # first try and find a matching region
777
+ # i.e. BUILD_LIST...<stuff>...LIST_TO_TUPLE
778
+ def find_postive_region():
779
+ found = False
780
+ for idx in reversed(range(len(blk.body))):
781
+ stmt = blk.body[idx]
782
+ if isinstance(stmt, ir.Assign):
783
+ value = stmt.value
784
+ if (
785
+ isinstance(value, ir.Expr)
786
+ and value.op == "list_to_tuple"
787
+ ):
788
+ target_list = value.info[0]
789
+ found = True
790
+ bt = (idx, stmt)
791
+ if found:
792
+ if isinstance(stmt, ir.Assign):
793
+ if stmt.target.name == target_list:
794
+ region = (bt, (idx, stmt))
795
+ return region
796
+
797
+ region = find_postive_region()
798
+ # if there's a peep hole region then do something with it
799
+ if region is not None:
800
+ peep_hole = blk.body[region[1][0] : region[0][0]]
801
+ if _DEBUG:
802
+ print("\nWINDOW:")
803
+ for x in peep_hole:
804
+ print(x)
805
+ print("")
806
+
807
+ appends = []
808
+ extends = []
809
+ init = region[1][1]
810
+ const_list = init.target.name
811
+ # Walk through the peep_hole and find things that are being
812
+ # "extend"ed and "append"ed to the BUILD_LIST
813
+ for x in peep_hole:
814
+ if isinstance(x, ir.Assign):
815
+ if isinstance(x.value, ir.Expr):
816
+ expr = x.value
817
+ if (
818
+ expr.op == "getattr"
819
+ and expr.value.name == const_list
820
+ ):
821
+ # it's not strictly necessary to split out
822
+ # extends and appends, but it helps with
823
+ # debugging to do so!
824
+ if expr.attr == "extend":
825
+ extends.append(x.target.name)
826
+ elif expr.attr == "append":
827
+ appends.append(x.target.name)
828
+ else:
829
+ assert 0
830
+ # go back through the peep hole build new IR based on it.
831
+ new_hole = []
832
+
833
+ def append_and_fix(x):
834
+ """Adds to the new_hole and fixes up definitions"""
835
+ new_hole.append(x)
836
+ if x.target.name in func_ir._definitions:
837
+ # if there's already a definition, drop it, should only
838
+ # be 1 as the way cpython emits the sequence for
839
+ # `list_to_tuple` should ensure this.
840
+ assert len(func_ir._definitions[x.target.name]) == 1
841
+ func_ir._definitions[x.target.name].clear()
842
+ func_ir._definitions[x.target.name].append(x.value)
843
+
844
+ the_build_list = init.target
845
+
846
+ # Do the transform on the peep hole
847
+ if _DEBUG:
848
+ print("\nBLOCK:")
849
+ blk.dump()
850
+
851
+ # This section basically accumulates list appends and extends
852
+ # as binop(+) on tuples, it drops all the getattr() for extend
853
+ # and append as they are now dead and replaced with binop(+).
854
+ # It also switches out the build_list for a build_tuple and then
855
+ # ensures everything is wired up and defined ok.
856
+ t2l_agn = region[0][1]
857
+ acc = the_build_list
858
+ for x in peep_hole:
859
+ if isinstance(x, ir.Assign):
860
+ if isinstance(x.value, ir.Expr):
861
+ expr = x.value
862
+ if expr.op == "getattr":
863
+ if (
864
+ x.target.name in extends
865
+ or x.target.name in appends
866
+ ):
867
+ # drop definition, it's being wholesale
868
+ # replaced.
869
+ func_ir._definitions.pop(x.target.name)
870
+ continue
871
+ else:
872
+ # a getattr on something we're not
873
+ # interested in
874
+ new_hole.append(x)
875
+ elif expr.op == "call":
876
+ fname = expr.func.name
877
+ if fname in extends or fname in appends:
878
+ arg = expr.args[0]
879
+ if isinstance(arg, ir.Var):
880
+ tmp_name = "%s_var_%s" % (
881
+ fname,
882
+ arg.name,
883
+ )
884
+ if fname in appends:
885
+ bt = ir.Expr.build_tuple(
886
+ [
887
+ arg,
888
+ ],
889
+ expr.loc,
890
+ )
891
+ else:
892
+ # Extend as tuple
893
+ gv_tuple = ir.Global(
894
+ name="tuple",
895
+ value=tuple,
896
+ loc=expr.loc,
897
+ )
898
+ tuple_var = arg.scope.redefine(
899
+ "$_list_extend_gv_tuple",
900
+ loc=expr.loc,
901
+ )
902
+ new_hole.append(
903
+ ir.Assign(
904
+ target=tuple_var,
905
+ value=gv_tuple,
906
+ loc=expr.loc,
907
+ ),
908
+ )
909
+ bt = ir.Expr.call(
910
+ tuple_var,
911
+ (arg,),
912
+ (),
913
+ loc=expr.loc,
914
+ )
915
+ var = ir.Var(
916
+ arg.scope, tmp_name, expr.loc
917
+ )
918
+ asgn = ir.Assign(bt, var, expr.loc)
919
+ append_and_fix(asgn)
920
+ arg = var
921
+
922
+ # this needs to be a binary add
923
+ new = ir.Expr.binop(
924
+ fn=operator.add,
925
+ lhs=acc,
926
+ rhs=arg,
927
+ loc=x.loc,
928
+ )
929
+ asgn = ir.Assign(new, x.target, expr.loc)
930
+ append_and_fix(asgn)
931
+ acc = asgn.target
932
+ else:
933
+ # there could be a call in the unpack, like
934
+ # *(a, x.append(y))
935
+ new_hole.append(x)
936
+ elif (
937
+ expr.op == "build_list"
938
+ and x.target.name == const_list
939
+ ):
940
+ new = ir.Expr.build_tuple(expr.items, expr.loc)
941
+ asgn = ir.Assign(new, x.target, expr.loc)
942
+ # Not a temporary any more
943
+ append_and_fix(asgn)
944
+ else:
945
+ new_hole.append(x)
946
+ else:
947
+ new_hole.append(x)
948
+
949
+ else:
950
+ # stick everything else in as-is
951
+ new_hole.append(x)
952
+ # Finally write the result back into the original build list as
953
+ # everything refers to it.
954
+ append_and_fix(
955
+ ir.Assign(acc, t2l_agn.target, the_build_list.loc)
956
+ )
957
+ if _DEBUG:
958
+ print("\nNEW HOLE:")
959
+ for x in new_hole:
960
+ print(x)
961
+
962
+ # and then update the block body with the modified region
963
+ cpy = blk.body[:]
964
+ head = cpy[: region[1][0]]
965
+ tail = blk.body[region[0][0] + 1 :]
966
+ tmp = head + new_hole + tail
967
+ blk.body.clear()
968
+ blk.body.extend(tmp)
969
+
970
+ if _DEBUG:
971
+ print("\nDUMP post hole:")
972
+ blk.dump()
973
+
974
+ else:
975
+ # else escape
976
+ break
977
+
978
+ return func_ir
979
+
980
+
981
+ def peep_hole_delete_with_exit(func_ir):
982
+ """
983
+ This rewrite removes variables used to store the `__exit__` function
984
+ loaded by SETUP_WITH.
985
+ """
986
+ dead_vars = set()
987
+
988
+ for blk in func_ir.blocks.values():
989
+ for stmt in blk.body:
990
+ # Any statement that uses a variable with the '$setup_with_exitfn'
991
+ # prefix is considered dead.
992
+ used = set(stmt.list_vars())
993
+ for v in used:
994
+ if v.name.startswith("$setup_with_exitfn"):
995
+ dead_vars.add(v)
996
+ # Any assignment that uses any of the dead variable is considered
997
+ # dead.
998
+ if used & dead_vars:
999
+ if isinstance(stmt, ir.Assign):
1000
+ dead_vars.add(stmt.target)
1001
+
1002
+ new_body = []
1003
+ for stmt in blk.body:
1004
+ # Skip any statements that uses anyone of the dead variable.
1005
+ if not (set(stmt.list_vars()) & dead_vars):
1006
+ new_body.append(stmt)
1007
+ blk.body.clear()
1008
+ blk.body.extend(new_body)
1009
+
1010
+ return func_ir
1011
+
1012
+
1013
+ def peep_hole_fuse_dict_add_updates(func_ir):
1014
+ """
1015
+ This rewrite removes d1._update_from_bytecode(d2)
1016
+ calls that are between two dictionaries, d1 and d2,
1017
+ in the same basic block. This pattern can appear as a
1018
+ result of Python 3.10 bytecode emission changes, which
1019
+ prevent large constant literal dictionaries
1020
+ (> 15 elements) from being constant. If both dictionaries
1021
+ are constant dictionaries defined in the same block and
1022
+ neither is used between the update call, then we replace d1
1023
+ with a new definition that combines the two dictionaries. At
1024
+ the bytecode translation stage we convert DICT_UPDATE into
1025
+ _update_from_bytecode, so we know that _update_from_bytecode
1026
+ always comes from the bytecode change and not user code.
1027
+
1028
+ Python 3.10 may also rewrite the individual dictionaries
1029
+ as an empty build_map + many map_add. Here we again look
1030
+ for an _update_from_bytecode, and if so we replace these
1031
+ with a single constant dictionary.
1032
+
1033
+ When running this algorithm we can always safely remove d2.
1034
+
1035
+ This is the relevant section of the CPython 3.10 that causes
1036
+ this bytecode change:
1037
+ https://github.com/python/cpython/blob/3.10/Python/compile.c#L4048
1038
+ """
1039
+
1040
+ # This algorithm fuses build_map expressions into the largest
1041
+ # possible build map before use. For example, if we have an
1042
+ # IR that looks like this:
1043
+ #
1044
+ # $d1 = build_map([])
1045
+ # $key = const("a")
1046
+ # $value = const(2)
1047
+ # $setitem_func = getattr($d1, "__setitem__")
1048
+ # $unused1 = call (setitem_func, ($key, $value))
1049
+ # $key2 = const("b")
1050
+ # $value2 = const(3)
1051
+ # $d2 = build_map([($key2, $value2)])
1052
+ # $update_func = getattr($d1, "_update_from_bytecode")
1053
+ # $unused2 = call ($update_func, ($d2,))
1054
+ # $othervar = None
1055
+ # $retvar = cast($othervar)
1056
+ # return $retvar
1057
+ #
1058
+ # Then the IR is rewritten such that any __setitem__ and
1059
+ # _update_from_bytecode operations are fused into the original buildmap.
1060
+ # The new buildmap is then added to the
1061
+ # last location where it had previously had encountered a __setitem__,
1062
+ # _update_from_bytecode, or build_map before any other uses.
1063
+ # The new IR would look like:
1064
+ #
1065
+ # $key = const("a")
1066
+ # $value = const(2)
1067
+ # $key2 = const("b")
1068
+ # $value2 = const(3)
1069
+ # $d1 = build_map([($key, $value), ($key2, $value2)])
1070
+ # $othervar = None
1071
+ # $retvar = cast($othervar)
1072
+ # return $retvar
1073
+ #
1074
+ # Note that we don't push $d1 to the bottom of the block. This is because
1075
+ # some values may be found below this block (e.g pop_block) that are pattern
1076
+ # matched in other locations, such as objmode handling. It should be safe to
1077
+ # move a map to the last location at which there was _update_from_bytecode.
1078
+
1079
+ errmsg = textwrap.dedent("""
1080
+ A DICT_UPDATE op-code was encountered that could not be replaced.
1081
+ If you have created a large constant dictionary, this may
1082
+ be an an indication that you are using inlined control
1083
+ flow. You can resolve this issue by moving the control flow out of
1084
+ the dicitonary constructor. For example, if you have
1085
+
1086
+ d = {a: 1 if flag else 0, ...)
1087
+
1088
+ Replace that with:
1089
+
1090
+ a_val = 1 if flag else 0
1091
+ d = {a: a_val, ...)""")
1092
+
1093
+ already_deleted_defs = collections.defaultdict(set)
1094
+ for blk in func_ir.blocks.values():
1095
+ new_body = []
1096
+ # literal map var name -> block idx of the original build_map
1097
+ lit_map_def_idx = {}
1098
+ # literal map var name -> list(map_uses)
1099
+ # This is the index of every build_map or __setitem__
1100
+ # in the IR that will need to be removed if the map
1101
+ # is updated.
1102
+ lit_map_use_idx = collections.defaultdict(list)
1103
+ # literal map var name -> list of key/value items for build map
1104
+ map_updates = {}
1105
+ blk_changed = False
1106
+
1107
+ for i, stmt in enumerate(blk.body):
1108
+ # What instruction should we append
1109
+ new_inst = stmt
1110
+ # Name that should be skipped when tracking used
1111
+ # vars in statement. This is always the lhs with
1112
+ # a build_map.
1113
+ stmt_build_map_out = None
1114
+ if isinstance(stmt, ir.Assign) and isinstance(stmt.value, ir.Expr):
1115
+ if stmt.value.op == "build_map":
1116
+ # Skip the output build_map when looking for used vars.
1117
+ stmt_build_map_out = stmt.target.name
1118
+ # If we encounter a build map add it to the
1119
+ # tracked maps.
1120
+ lit_map_def_idx[stmt.target.name] = i
1121
+ lit_map_use_idx[stmt.target.name].append(i)
1122
+ map_updates[stmt.target.name] = stmt.value.items.copy()
1123
+ elif stmt.value.op == "call" and i > 0:
1124
+ # If we encounter a call we may need to replace
1125
+ # the body
1126
+ func_name = stmt.value.func.name
1127
+ # If we have an update or a setitem
1128
+ # it will be the previous expression.
1129
+ getattr_stmt = blk.body[i - 1]
1130
+ args = stmt.value.args
1131
+ if (
1132
+ isinstance(getattr_stmt, ir.Assign)
1133
+ and getattr_stmt.target.name == func_name
1134
+ and isinstance(getattr_stmt.value, ir.Expr)
1135
+ and getattr_stmt.value.op == "getattr"
1136
+ and getattr_stmt.value.attr
1137
+ in ("__setitem__", "_update_from_bytecode")
1138
+ ):
1139
+ update_map_name = getattr_stmt.value.value.name
1140
+ attr = getattr_stmt.value.attr
1141
+ if (
1142
+ attr == "__setitem__"
1143
+ and update_map_name in lit_map_use_idx
1144
+ ):
1145
+ # If we have a setitem, update the lists
1146
+ map_updates[update_map_name].append(args)
1147
+ # Update the list of instructions that would
1148
+ # need to be removed to include the setitem
1149
+ # and the the getattr
1150
+ lit_map_use_idx[update_map_name].extend([i - 1, i])
1151
+ elif attr == "_update_from_bytecode":
1152
+ d2_map_name = args[0].name
1153
+ if (
1154
+ update_map_name in lit_map_use_idx
1155
+ and d2_map_name in lit_map_use_idx
1156
+ ):
1157
+ # If we have an update and the arg is also
1158
+ # a literal dictionary, fuse the lists.
1159
+ map_updates[update_map_name].extend(
1160
+ map_updates[d2_map_name]
1161
+ )
1162
+ # Delete the old IR for d1 and d2
1163
+ lit_map_use_idx[update_map_name].extend(
1164
+ lit_map_use_idx[d2_map_name]
1165
+ )
1166
+ lit_map_use_idx[update_map_name].append(i - 1)
1167
+ for linenum in lit_map_use_idx[update_map_name]:
1168
+ # Drop the existing definition.
1169
+ _remove_assignment_definition(
1170
+ blk.body,
1171
+ linenum,
1172
+ func_ir,
1173
+ already_deleted_defs,
1174
+ )
1175
+ # Delete it from the new block
1176
+ new_body[linenum] = None
1177
+ # Delete the maps from dicts
1178
+ del lit_map_def_idx[d2_map_name]
1179
+ del lit_map_use_idx[d2_map_name]
1180
+ del map_updates[d2_map_name]
1181
+ # Add d1 as the new instruction, removing the
1182
+ # old definition.
1183
+ _remove_assignment_definition(
1184
+ blk.body, i, func_ir, already_deleted_defs
1185
+ )
1186
+ new_inst = _build_new_build_map(
1187
+ func_ir,
1188
+ update_map_name,
1189
+ blk.body,
1190
+ lit_map_def_idx[update_map_name],
1191
+ map_updates[update_map_name],
1192
+ )
1193
+ # Update d1 in lit_map_use_idx to just the new
1194
+ # definition and clear the previous list.
1195
+ lit_map_use_idx[update_map_name].clear()
1196
+ lit_map_use_idx[update_map_name].append(i)
1197
+ # Mark that this block has been modified
1198
+ blk_changed = True
1199
+ else:
1200
+ # If we cannot remove _update_from_bytecode
1201
+ # Then raise an error for the user.
1202
+ raise UnsupportedBytecodeError(errmsg)
1203
+
1204
+ # Check if we need to drop any maps from being tracked.
1205
+ # Skip the setitem/_update_from_bytecode getattr that
1206
+ # will be removed when handling their call in the next
1207
+ # iteration.
1208
+ if not (
1209
+ isinstance(stmt, ir.Assign)
1210
+ and isinstance(stmt.value, ir.Expr)
1211
+ and stmt.value.op == "getattr"
1212
+ and stmt.value.value.name in lit_map_use_idx
1213
+ and stmt.value.attr in ("__setitem__", "_update_from_bytecode")
1214
+ ):
1215
+ for var in stmt.list_vars():
1216
+ # If a map is used it cannot be fused later in
1217
+ # the block. As a result we delete it from
1218
+ # the dicitonaries
1219
+ if (
1220
+ var.name in lit_map_use_idx
1221
+ and var.name != stmt_build_map_out
1222
+ ):
1223
+ del lit_map_def_idx[var.name]
1224
+ del lit_map_use_idx[var.name]
1225
+ del map_updates[var.name]
1226
+
1227
+ # Append the instruction to the new block
1228
+ new_body.append(new_inst)
1229
+
1230
+ if blk_changed:
1231
+ # If the block is changed replace the block body.
1232
+ blk.body.clear()
1233
+ blk.body.extend([x for x in new_body if x is not None])
1234
+
1235
+ return func_ir
1236
+
1237
+
1238
+ def peep_hole_split_at_pop_block(func_ir):
1239
+ """
1240
+ Split blocks that contain ir.PopBlock.
1241
+
1242
+ This rewrite restores the IR structure to pre 3.11 so that withlifting
1243
+ can work correctly.
1244
+ """
1245
+ new_block_map = {}
1246
+ sorted_blocks = sorted(func_ir.blocks.items())
1247
+ for blk_idx, (label, blk) in enumerate(sorted_blocks):
1248
+ # Gather locations of PopBlock
1249
+ pop_block_locs = []
1250
+ for i, inst in enumerate(blk.body):
1251
+ if isinstance(inst, ir.PopBlock):
1252
+ pop_block_locs.append(i)
1253
+ # Rewrite block with PopBlock
1254
+ if pop_block_locs:
1255
+ new_blocks = []
1256
+ for i in pop_block_locs:
1257
+ before_blk = ir.Block(blk.scope, loc=blk.loc)
1258
+ before_blk.body.extend(blk.body[:i])
1259
+ new_blocks.append(before_blk)
1260
+
1261
+ popblk_blk = ir.Block(blk.scope, loc=blk.loc)
1262
+ popblk_blk.body.append(blk.body[i])
1263
+ new_blocks.append(popblk_blk)
1264
+ # Add jump instructions
1265
+ prev_label = label
1266
+ for newblk in new_blocks:
1267
+ new_block_map[prev_label] = newblk
1268
+ next_label = prev_label + 1
1269
+ newblk.body.append(ir.Jump(next_label, loc=blk.loc))
1270
+ prev_label = next_label
1271
+ # Check prev_label does not exceed current new block label
1272
+ if blk_idx + 1 < len(sorted_blocks):
1273
+ if prev_label >= sorted_blocks[blk_idx + 1][0]:
1274
+ # Panic! Due to heuristic in with-lifting, block labels
1275
+ # must be monotonically increasing. We cannot continue if we
1276
+ # run out of usable label between the two blocks.
1277
+ raise errors.InternalError("POP_BLOCK peephole failed")
1278
+ # Add tail block, which will get the original terminator
1279
+ tail_blk = ir.Block(blk.scope, loc=blk.loc)
1280
+ tail_blk.body.extend(blk.body[pop_block_locs[-1] + 1 :])
1281
+ new_block_map[prev_label] = tail_blk
1282
+
1283
+ func_ir.blocks.update(new_block_map)
1284
+ return func_ir
1285
+
1286
+
1287
+ def _build_new_build_map(func_ir, name, old_body, old_lineno, new_items):
1288
+ """
1289
+ Create a new build_map with a new set of key/value items
1290
+ but all the other info the same.
1291
+ """
1292
+ old_assign = old_body[old_lineno]
1293
+ old_target = old_assign.target
1294
+ old_bm = old_assign.value
1295
+ # Build the literals
1296
+ literal_keys = []
1297
+ # Track the constant key/values to set the literal_value
1298
+ # field of build_map properly
1299
+ values = []
1300
+ for pair in new_items:
1301
+ k, v = pair
1302
+ key_def = ir_utils.guard(ir_utils.get_definition, func_ir, k)
1303
+ if isinstance(key_def, (ir.Const, ir.Global, ir.FreeVar)):
1304
+ literal_keys.append(key_def.value)
1305
+ value_def = ir_utils.guard(ir_utils.get_definition, func_ir, v)
1306
+ if isinstance(value_def, (ir.Const, ir.Global, ir.FreeVar)):
1307
+ values.append(value_def.value)
1308
+ else:
1309
+ # Append unknown value if not a literal.
1310
+ values.append(_UNKNOWN_VALUE(v.name))
1311
+
1312
+ value_indexes = {}
1313
+ if len(literal_keys) == len(new_items):
1314
+ # All keys must be literals to have any literal values.
1315
+ literal_value = {x: y for x, y in zip(literal_keys, values)}
1316
+ for i, k in enumerate(literal_keys):
1317
+ value_indexes[k] = i
1318
+ else:
1319
+ literal_value = None
1320
+
1321
+ # Construct a new build map.
1322
+ new_bm = ir.Expr.build_map(
1323
+ items=new_items,
1324
+ size=len(new_items),
1325
+ literal_value=literal_value,
1326
+ value_indexes=value_indexes,
1327
+ loc=old_bm.loc,
1328
+ )
1329
+
1330
+ # The previous definition has already been removed
1331
+ # when updating the IR in peep_hole_fuse_dict_add_updates
1332
+ func_ir._definitions[name].append(new_bm)
1333
+
1334
+ # Return a new assign.
1335
+ return ir.Assign(
1336
+ new_bm, ir.Var(old_target.scope, name, old_target.loc), new_bm.loc
1337
+ )
1338
+
1339
+
1340
+ class Interpreter(object):
1341
+ """A bytecode interpreter that builds up the IR."""
1342
+
1343
+ _DEBUG_PRINT = False
1344
+
1345
+ def __init__(self, func_id):
1346
+ self.func_id = func_id
1347
+ if self._DEBUG_PRINT:
1348
+ print(func_id.func)
1349
+ self.arg_count = func_id.arg_count
1350
+ self.arg_names = func_id.arg_names
1351
+ self.loc = self.first_loc = ir.Loc.from_function_id(func_id)
1352
+ self.is_generator = func_id.is_generator
1353
+
1354
+ # { inst offset : ir.Block }
1355
+ self.blocks = {}
1356
+ # { name: [definitions] } of local variables
1357
+ self.definitions = collections.defaultdict(list)
1358
+ # A set to keep track of all exception variables.
1359
+ # To be used in _legalize_exception_vars()
1360
+ self._exception_vars = set()
1361
+
1362
+ def interpret(self, bytecode):
1363
+ """
1364
+ Generate IR for this bytecode.
1365
+ """
1366
+ self.bytecode = bytecode
1367
+
1368
+ self.scopes = []
1369
+ global_scope = ir.Scope(parent=None, loc=self.loc)
1370
+ self.scopes.append(global_scope)
1371
+
1372
+ flow = Flow(bytecode)
1373
+ flow.run()
1374
+ self.dfa = AdaptDFA(flow)
1375
+ self.cfa = AdaptCFA(flow)
1376
+ if config.DUMP_CFG:
1377
+ self.cfa.dump()
1378
+
1379
+ # Temp states during interpretation
1380
+ self.current_block = None
1381
+ self.current_block_offset = None
1382
+ last_active_offset = 0
1383
+ for _, inst_blocks in self.cfa.blocks.items():
1384
+ if inst_blocks.body:
1385
+ last_active_offset = max(
1386
+ last_active_offset, max(inst_blocks.body)
1387
+ )
1388
+ self.last_active_offset = last_active_offset
1389
+
1390
+ if PYVERSION in ((3, 12), (3, 13)):
1391
+ self.active_exception_entries = tuple(
1392
+ [
1393
+ entry
1394
+ for entry in self.bytecode.exception_entries
1395
+ if entry.start < self.last_active_offset
1396
+ ]
1397
+ )
1398
+ elif PYVERSION in ((3, 10), (3, 11)):
1399
+ pass
1400
+ else:
1401
+ raise NotImplementedError(PYVERSION)
1402
+ self.syntax_blocks = []
1403
+ self.dfainfo = None
1404
+
1405
+ self.scopes.append(ir.Scope(parent=self.current_scope, loc=self.loc))
1406
+
1407
+ # Interpret loop
1408
+ for inst, kws in self._iter_inst():
1409
+ self._dispatch(inst, kws)
1410
+ if PYVERSION in ((3, 11), (3, 12), (3, 13)):
1411
+ # Insert end of try markers
1412
+ self._end_try_blocks()
1413
+ elif PYVERSION in ((3, 10),):
1414
+ pass
1415
+ else:
1416
+ raise NotImplementedError(PYVERSION)
1417
+ self._legalize_exception_vars()
1418
+ # Prepare FunctionIR
1419
+ func_ir = ir.FunctionIR(
1420
+ self.blocks,
1421
+ self.is_generator,
1422
+ self.func_id,
1423
+ self.first_loc,
1424
+ self.definitions,
1425
+ self.arg_count,
1426
+ self.arg_names,
1427
+ )
1428
+ _logger.debug(
1429
+ _lazy_pformat(func_ir, lazy_func=lambda x: x.dump_to_string())
1430
+ )
1431
+
1432
+ # post process the IR to rewrite opcodes/byte sequences that are too
1433
+ # involved to risk handling as part of direct interpretation
1434
+ peepholes = []
1435
+ if PYVERSION in ((3, 11), (3, 12), (3, 13)):
1436
+ peepholes.append(peep_hole_split_at_pop_block)
1437
+ if PYVERSION in ((3, 10), (3, 11), (3, 12), (3, 13)):
1438
+ peepholes.append(peep_hole_list_to_tuple)
1439
+ peepholes.append(peep_hole_delete_with_exit)
1440
+ if PYVERSION in ((3, 10), (3, 11), (3, 12), (3, 13)):
1441
+ # peep_hole_call_function_ex_to_call_function_kw
1442
+ # depends on peep_hole_list_to_tuple converting
1443
+ # any large number of arguments from a list to a
1444
+ # tuple.
1445
+ peepholes.append(peep_hole_call_function_ex_to_call_function_kw)
1446
+ peepholes.append(peep_hole_fuse_dict_add_updates)
1447
+
1448
+ post_processed_ir = self.post_process(peepholes, func_ir)
1449
+
1450
+ return post_processed_ir
1451
+
1452
+ def post_process(self, peepholes, func_ir):
1453
+ for peep in peepholes:
1454
+ func_ir = peep(func_ir)
1455
+ return func_ir
1456
+
1457
+ def _end_try_blocks(self):
1458
+ """Closes all try blocks by inserting the required marker at the
1459
+ exception handler
1460
+
1461
+ This is only needed for py3.11 because of the changes in exception
1462
+ handling. This merely maps the new py3.11 semantics back to the old way.
1463
+
1464
+ What the code does:
1465
+
1466
+ - For each block, compute the difference of blockstack to its incoming
1467
+ blocks' blockstack.
1468
+ - If the incoming blockstack has an extra TRY, the current block must
1469
+ be the EXCEPT block and we need to insert a marker.
1470
+
1471
+ See also: _insert_try_block_end
1472
+ """
1473
+ assert PYVERSION in ((3, 11), (3, 12), (3, 13))
1474
+ graph = self.cfa.graph
1475
+ for offset, block in self.blocks.items():
1476
+ # Get current blockstack
1477
+ cur_bs = self.dfa.infos[offset].blockstack
1478
+ # Check blockstack of the incoming blocks
1479
+ for inc, _ in graph.predecessors(offset):
1480
+ inc_bs = self.dfa.infos[inc].blockstack
1481
+
1482
+ # find first diff in the blockstack
1483
+ for i, (x, y) in enumerate(zip(cur_bs, inc_bs)):
1484
+ if x != y:
1485
+ break
1486
+ else:
1487
+ i = min(len(cur_bs), len(inc_bs))
1488
+
1489
+ def do_change(remain):
1490
+ while remain:
1491
+ ent = remain.pop()
1492
+ if ent["kind"] == BlockKind("TRY"):
1493
+ # Extend block with marker for end of try
1494
+ self.current_block = block
1495
+ oldbody = list(block.body)
1496
+ block.body.clear()
1497
+ self._insert_try_block_end()
1498
+ block.body.extend(oldbody)
1499
+ return True
1500
+
1501
+ if do_change(list(inc_bs[i:])):
1502
+ break
1503
+
1504
+ def _legalize_exception_vars(self):
1505
+ """Search for unsupported use of exception variables.
1506
+ Note, they cannot be stored into user variable.
1507
+ """
1508
+ # Build a set of exception variables
1509
+ excvars = self._exception_vars.copy()
1510
+ # Propagate the exception variables to LHS of assignment
1511
+ for varname, defnvars in self.definitions.items():
1512
+ for v in defnvars:
1513
+ if isinstance(v, ir.Var):
1514
+ k = v.name
1515
+ if k in excvars:
1516
+ excvars.add(varname)
1517
+ # Filter out the user variables.
1518
+ uservar = list(filter(lambda x: not x.startswith("$"), excvars))
1519
+ if uservar:
1520
+ # Complain about the first user-variable storing an exception
1521
+ first = uservar[0]
1522
+ loc = self.current_scope.get(first).loc
1523
+ msg = "Exception object cannot be stored into variable ({})."
1524
+ raise errors.UnsupportedBytecodeError(msg.format(first), loc=loc)
1525
+
1526
+ def init_first_block(self):
1527
+ # Define variables receiving the function arguments
1528
+ for index, name in enumerate(self.arg_names):
1529
+ val = ir.Arg(index=index, name=name, loc=self.loc)
1530
+ self.store(val, name)
1531
+
1532
+ def _iter_inst(self):
1533
+ for blkct, block in enumerate(self.cfa.iterliveblocks()):
1534
+ firstinst = self.bytecode[block.offset]
1535
+ # If its an END_FOR instruction, the start location of block
1536
+ # is set to start of the FOR loop, so take the location of
1537
+ # next instruction. This only affects the source location
1538
+ # marking and has no impact to semantic.
1539
+ if firstinst.opname == "END_FOR":
1540
+ firstinst = self.bytecode[firstinst.next]
1541
+ self.loc = self.loc.with_lineno(firstinst.lineno)
1542
+ self._start_new_block(block.offset)
1543
+ if blkct == 0:
1544
+ # Is first block
1545
+ self.init_first_block()
1546
+ for offset, kws in self.dfainfo.insts:
1547
+ inst = self.bytecode[offset]
1548
+ self.loc = self.loc.with_lineno(inst.lineno)
1549
+ yield inst, kws
1550
+ self._end_current_block()
1551
+
1552
+ def _start_new_block(self, offset):
1553
+ oldblock = self.current_block
1554
+ self.insert_block(offset)
1555
+
1556
+ tryblk = self.dfainfo.active_try_block if self.dfainfo else None
1557
+ # Ensure the last block is terminated
1558
+ if oldblock is not None and not oldblock.is_terminated:
1559
+ # Handle ending try block.
1560
+ # If there's an active try-block and the handler block is live.
1561
+ if tryblk is not None and tryblk["end"] in self.cfa.graph.nodes():
1562
+ # We are in a try-block, insert a branch to except-block.
1563
+ # This logic cannot be in self._end_current_block()
1564
+ # because we don't know the non-raising next block-offset.
1565
+ branch = ir.Branch(
1566
+ cond=self.get("$exception_check"),
1567
+ truebr=tryblk["end"],
1568
+ falsebr=offset,
1569
+ loc=self.loc,
1570
+ )
1571
+ oldblock.append(branch)
1572
+ # Handle normal case
1573
+ else:
1574
+ jmp = ir.Jump(offset, loc=self.loc)
1575
+ oldblock.append(jmp)
1576
+
1577
+ # Get DFA block info
1578
+ self.dfainfo = self.dfa.infos[self.current_block_offset]
1579
+ self.assigner = Assigner()
1580
+ # Check out-of-scope syntactic-block
1581
+ if PYVERSION in ((3, 11), (3, 12), (3, 13)):
1582
+ # This is recreating pre-3.11 code structure
1583
+ while self.syntax_blocks:
1584
+ if offset >= self.syntax_blocks[-1].exit:
1585
+ synblk = self.syntax_blocks.pop()
1586
+ if isinstance(synblk, ir.With):
1587
+ self.current_block.append(ir.PopBlock(self.loc))
1588
+ else:
1589
+ break
1590
+ # inject try block:
1591
+ newtryblk = self.dfainfo.active_try_block
1592
+ if newtryblk is not None:
1593
+ if newtryblk is not tryblk:
1594
+ self._insert_try_block_begin()
1595
+ elif PYVERSION in ((3, 10),):
1596
+ while self.syntax_blocks:
1597
+ if offset >= self.syntax_blocks[-1].exit:
1598
+ self.syntax_blocks.pop()
1599
+ else:
1600
+ break
1601
+ else:
1602
+ raise NotImplementedError(PYVERSION)
1603
+
1604
+ def _end_current_block(self):
1605
+ # Handle try block
1606
+ if not self.current_block.is_terminated:
1607
+ tryblk = self.dfainfo.active_try_block
1608
+ if tryblk is not None:
1609
+ self._insert_exception_check()
1610
+ # Handle normal block cleanup
1611
+ self._remove_unused_temporaries()
1612
+ self._insert_outgoing_phis()
1613
+
1614
+ def _inject_call(self, func, gv_name, res_name=None):
1615
+ """A helper function to inject a call to *func* which is a python
1616
+ function.
1617
+ Parameters
1618
+ ----------
1619
+ func : callable
1620
+ The function object to be called.
1621
+ gv_name : str
1622
+ The variable name to be used to store the function object.
1623
+ res_name : str; optional
1624
+ The variable name to be used to store the call result.
1625
+ If ``None``, a name is created automatically.
1626
+ """
1627
+ gv_fn = ir.Global(gv_name, func, loc=self.loc)
1628
+ self.store(value=gv_fn, name=gv_name, redefine=True)
1629
+ callres = ir.Expr.call(self.get(gv_name), (), (), loc=self.loc)
1630
+ res_name = res_name or "$callres_{}".format(gv_name)
1631
+ self.store(value=callres, name=res_name, redefine=True)
1632
+
1633
+ def _insert_try_block_begin(self):
1634
+ """Insert IR-nodes to mark the start of a `try` block."""
1635
+ self._inject_call(eh.mark_try_block, "mark_try_block")
1636
+
1637
+ def _insert_try_block_end(self):
1638
+ """Insert IR-nodes to mark the end of a `try` block."""
1639
+ self._inject_call(eh.end_try_block, "end_try_block")
1640
+
1641
+ def _insert_exception_variables(self):
1642
+ """Insert IR-nodes to initialize the exception variables."""
1643
+ tryblk = self.dfainfo.active_try_block
1644
+ # Get exception variables
1645
+ endblk = tryblk["end"]
1646
+ edgepushed = self.dfainfo.outgoing_edgepushed.get(endblk)
1647
+ # Note: the last value on the stack is the exception value
1648
+ # Note: due to the current limitation, all exception variables are None
1649
+ if edgepushed:
1650
+ const_none = ir.Const(value=None, loc=self.loc)
1651
+ # For each variable going to the handler block.
1652
+ for var in edgepushed:
1653
+ if var in self.definitions:
1654
+ raise AssertionError(
1655
+ "exception variable CANNOT be defined by other code",
1656
+ )
1657
+ self.store(value=const_none, name=var)
1658
+ self._exception_vars.add(var)
1659
+
1660
+ def _insert_exception_check(self):
1661
+ """Called before the end of a block to inject checks if raised."""
1662
+ self._insert_exception_variables()
1663
+ # Do exception check
1664
+ self._inject_call(
1665
+ eh.exception_check, "exception_check", "$exception_check"
1666
+ )
1667
+
1668
+ def _remove_unused_temporaries(self):
1669
+ """
1670
+ Remove assignments to unused temporary variables from the
1671
+ current block.
1672
+ """
1673
+ new_body = []
1674
+ replaced_var = {}
1675
+ for inst in self.current_block.body:
1676
+ # the same temporary is assigned to multiple variables in cases
1677
+ # like a = b[i] = 1, so need to handle replaced temporaries in
1678
+ # later setitem/setattr nodes
1679
+ if (
1680
+ isinstance(inst, (ir.SetItem, ir.SetAttr))
1681
+ and inst.value.name in replaced_var
1682
+ ):
1683
+ inst.value = replaced_var[inst.value.name]
1684
+ elif isinstance(inst, ir.Assign):
1685
+ if (
1686
+ inst.target.is_temp
1687
+ and inst.target.name in self.assigner.unused_dests
1688
+ ):
1689
+ continue
1690
+ # the same temporary is assigned to multiple variables in cases
1691
+ # like a = b = 1, so need to handle replaced temporaries in
1692
+ # later assignments
1693
+ if (
1694
+ isinstance(inst.value, ir.Var)
1695
+ and inst.value.name in replaced_var
1696
+ ):
1697
+ inst.value = replaced_var[inst.value.name]
1698
+ new_body.append(inst)
1699
+ continue
1700
+ # chained unpack cases may reuse temporary
1701
+ # e.g. a = (b, c) = (x, y)
1702
+ if (
1703
+ isinstance(inst.value, ir.Expr)
1704
+ and inst.value.op == "exhaust_iter"
1705
+ and inst.value.value.name in replaced_var
1706
+ ):
1707
+ inst.value.value = replaced_var[inst.value.value.name]
1708
+ new_body.append(inst)
1709
+ continue
1710
+ # eliminate temporary variables that are assigned to user
1711
+ # variables right after creation. E.g.:
1712
+ # $1 = f(); a = $1 -> a = f()
1713
+ # the temporary variable is not reused elsewhere since CPython
1714
+ # bytecode is stack-based and this pattern corresponds to a pop
1715
+ if (
1716
+ isinstance(inst.value, ir.Var)
1717
+ and inst.value.is_temp
1718
+ and new_body
1719
+ and isinstance(new_body[-1], ir.Assign)
1720
+ ):
1721
+ prev_assign = new_body[-1]
1722
+ # _var_used_in_binop check makes sure we don't create a new
1723
+ # inplace binop operation which can fail
1724
+ # (see TestFunctionType.test_in_iter_func_call)
1725
+ if (
1726
+ prev_assign.target.name == inst.value.name
1727
+ and not self._var_used_in_binop(
1728
+ inst.target.name, prev_assign.value
1729
+ )
1730
+ ):
1731
+ replaced_var[inst.value.name] = inst.target
1732
+ prev_assign.target = inst.target
1733
+ # replace temp var definition in target with proper defs
1734
+ self.definitions[inst.target.name].remove(inst.value)
1735
+ self.definitions[inst.target.name].extend(
1736
+ self.definitions.pop(inst.value.name)
1737
+ )
1738
+ continue
1739
+
1740
+ new_body.append(inst)
1741
+
1742
+ self.current_block.body = new_body
1743
+
1744
+ def _var_used_in_binop(self, varname, expr):
1745
+ """return True if 'expr' is a binary expression and 'varname' is used
1746
+ in it as an argument
1747
+ """
1748
+ return (
1749
+ isinstance(expr, ir.Expr)
1750
+ and expr.op in ("binop", "inplace_binop")
1751
+ and (varname == expr.lhs.name or varname == expr.rhs.name)
1752
+ )
1753
+
1754
+ def _insert_outgoing_phis(self):
1755
+ """
1756
+ Add assignments to forward requested outgoing values
1757
+ to subsequent blocks.
1758
+ """
1759
+ for phiname, varname in self.dfainfo.outgoing_phis.items():
1760
+ target = self.current_scope.get_or_define(phiname, loc=self.loc)
1761
+ try:
1762
+ val = self.get(varname)
1763
+ except ir.NotDefinedError:
1764
+ # Hack to make sure exception variables are defined
1765
+ assert PYVERSION in ((3, 11), (3, 12), (3, 13)), (
1766
+ "unexpected missing definition"
1767
+ )
1768
+ val = ir.Const(value=None, loc=self.loc)
1769
+ stmt = ir.Assign(value=val, target=target, loc=self.loc)
1770
+ self.definitions[target.name].append(stmt.value)
1771
+ if not self.current_block.is_terminated:
1772
+ self.current_block.append(stmt)
1773
+ else:
1774
+ self.current_block.insert_before_terminator(stmt)
1775
+
1776
+ def get_global_value(self, name):
1777
+ """
1778
+ Get a global value from the func_global (first) or
1779
+ as a builtins (second). If both failed, return a ir.UNDEFINED.
1780
+ """
1781
+ try:
1782
+ return self.func_id.func.__globals__[name]
1783
+ except KeyError:
1784
+ return getattr(builtins, name, ir.UNDEFINED)
1785
+
1786
+ def get_closure_value(self, index):
1787
+ """
1788
+ Get a value from the cell contained in this function's closure.
1789
+ If not set, return a ir.UNDEFINED.
1790
+ """
1791
+ cell = self.func_id.func.__closure__[index]
1792
+ try:
1793
+ return cell.cell_contents
1794
+ except ValueError:
1795
+ return ir.UNDEFINED
1796
+
1797
+ @property
1798
+ def current_scope(self):
1799
+ return self.scopes[-1]
1800
+
1801
+ @property
1802
+ def code_consts(self):
1803
+ return self.bytecode.co_consts
1804
+
1805
+ @property
1806
+ def code_locals(self):
1807
+ return self.bytecode.co_varnames
1808
+
1809
+ @property
1810
+ def code_names(self):
1811
+ return self.bytecode.co_names
1812
+
1813
+ @property
1814
+ def code_cellvars(self):
1815
+ return self.bytecode.co_cellvars
1816
+
1817
+ @property
1818
+ def code_freevars(self):
1819
+ return self.bytecode.co_freevars
1820
+
1821
+ def _dispatch(self, inst, kws):
1822
+ if self._DEBUG_PRINT:
1823
+ print(inst)
1824
+ assert self.current_block is not None
1825
+ if PYVERSION in ((3, 11), (3, 12), (3, 13)):
1826
+ if self.syntax_blocks:
1827
+ top = self.syntax_blocks[-1]
1828
+ if isinstance(top, ir.With):
1829
+ if inst.offset >= top.exit:
1830
+ self.current_block.append(ir.PopBlock(loc=self.loc))
1831
+ self.syntax_blocks.pop()
1832
+ elif PYVERSION in ((3, 10),):
1833
+ pass
1834
+ else:
1835
+ raise NotImplementedError(PYVERSION)
1836
+
1837
+ fname = "op_%s" % inst.opname.replace("+", "_")
1838
+ try:
1839
+ fn = getattr(self, fname)
1840
+ except AttributeError:
1841
+ raise NotImplementedError(inst)
1842
+ else:
1843
+ try:
1844
+ return fn(inst, **kws)
1845
+ except errors.NotDefinedError as e:
1846
+ if e.loc is None:
1847
+ loc = self.loc
1848
+ else:
1849
+ loc = e.loc
1850
+
1851
+ err = errors.NotDefinedError(e.name, loc=loc)
1852
+ if not config.FULL_TRACEBACKS:
1853
+ raise err from None
1854
+ else:
1855
+ m = f"handling op: {inst} | offset: {inst.offset}"
1856
+ err.add_context(m)
1857
+ err.add_context(self.bytecode.dump())
1858
+ raise err
1859
+
1860
+ # --- Scope operations ---
1861
+
1862
+ def store(self, value, name, redefine=False):
1863
+ """
1864
+ Store *value* (a Expr or Var instance) into the variable named *name*
1865
+ (a str object). Returns the target variable.
1866
+ """
1867
+ if redefine or self.current_block_offset in self.cfa.backbone:
1868
+ rename = name not in self.code_cellvars
1869
+ target = self.current_scope.redefine(
1870
+ name, loc=self.loc, rename=rename
1871
+ )
1872
+ else:
1873
+ target = self.current_scope.get_or_define(name, loc=self.loc)
1874
+ if isinstance(value, ir.Var):
1875
+ value = self.assigner.assign(value, target)
1876
+ stmt = ir.Assign(value=value, target=target, loc=self.loc)
1877
+ self.current_block.append(stmt)
1878
+ self.definitions[target.name].append(value)
1879
+ return target
1880
+
1881
+ def get(self, name):
1882
+ """
1883
+ Get the variable (a Var instance) with the given *name*.
1884
+ """
1885
+ # Implicit argument for comprehension starts with '.'
1886
+ # See Parameter class in inspect.py (from Python source)
1887
+ if name[0] == "." and name[1:].isdigit():
1888
+ name = "implicit{}".format(name[1:])
1889
+
1890
+ # Try to simplify the variable lookup by returning an earlier
1891
+ # variable assigned to *name*.
1892
+ var = self.assigner.get_assignment_source(name)
1893
+ if var is None:
1894
+ var = self.current_scope.get(name)
1895
+ return var
1896
+
1897
+ # --- Block operations ---
1898
+
1899
+ def insert_block(self, offset, scope=None, loc=None):
1900
+ scope = scope or self.current_scope
1901
+ loc = loc or self.loc
1902
+ blk = ir.Block(scope=scope, loc=loc)
1903
+ self.blocks[offset] = blk
1904
+ self.current_block = blk
1905
+ self.current_block_offset = offset
1906
+ return blk
1907
+
1908
+ # --- Bytecode handlers ---
1909
+
1910
+ def op_NOP(self, inst):
1911
+ pass
1912
+
1913
+ def op_RESUME(self, inst):
1914
+ pass
1915
+
1916
+ def op_CACHE(self, inst):
1917
+ pass
1918
+
1919
+ def op_PRECALL(self, inst):
1920
+ pass
1921
+
1922
+ def op_PUSH_NULL(self, inst):
1923
+ pass
1924
+
1925
+ def op_RETURN_GENERATOR(self, inst):
1926
+ pass
1927
+
1928
+ def op_PRINT_ITEM(self, inst, item, printvar, res):
1929
+ item = self.get(item)
1930
+ printgv = ir.Global("print", print, loc=self.loc)
1931
+ self.store(value=printgv, name=printvar)
1932
+ call = ir.Expr.call(self.get(printvar), (item,), (), loc=self.loc)
1933
+ self.store(value=call, name=res)
1934
+
1935
+ def op_PRINT_NEWLINE(self, inst, printvar, res):
1936
+ printgv = ir.Global("print", print, loc=self.loc)
1937
+ self.store(value=printgv, name=printvar)
1938
+ call = ir.Expr.call(self.get(printvar), (), (), loc=self.loc)
1939
+ self.store(value=call, name=res)
1940
+
1941
+ def op_UNPACK_SEQUENCE(self, inst, iterable, stores, tupleobj):
1942
+ count = len(stores)
1943
+ # Exhaust the iterable into a tuple-like object
1944
+ tup = ir.Expr.exhaust_iter(
1945
+ value=self.get(iterable), loc=self.loc, count=count
1946
+ )
1947
+ self.store(name=tupleobj, value=tup)
1948
+
1949
+ # then index the tuple-like object to extract the values
1950
+ for i, st in enumerate(stores):
1951
+ expr = ir.Expr.static_getitem(
1952
+ self.get(tupleobj), index=i, index_var=None, loc=self.loc
1953
+ )
1954
+ self.store(expr, st)
1955
+
1956
+ def op_FORMAT_SIMPLE(self, inst, value, res, strvar):
1957
+ # Same as FORMAT_VALUE
1958
+ return self.op_FORMAT_VALUE(inst, value, res, strvar)
1959
+
1960
+ def op_FORMAT_VALUE(self, inst, value, res, strvar):
1961
+ """
1962
+ FORMAT_VALUE(flags): flags argument specifies format spec which is not
1963
+ supported yet. Currently, str() is simply called on the value.
1964
+ https://docs.python.org/3/library/dis.html#opcode-FORMAT_VALUE
1965
+ """
1966
+ value = self.get(value)
1967
+ strgv = ir.Global("str", str, loc=self.loc)
1968
+ self.store(value=strgv, name=strvar)
1969
+ call = ir.Expr.call(self.get(strvar), (value,), (), loc=self.loc)
1970
+ self.store(value=call, name=res)
1971
+
1972
+ def op_BUILD_STRING(self, inst, strings, tmps):
1973
+ """
1974
+ BUILD_STRING(count): Concatenates count strings.
1975
+ Required for supporting f-strings.
1976
+ https://docs.python.org/3/library/dis.html#opcode-BUILD_STRING
1977
+ """
1978
+ count = inst.arg
1979
+ # corner case: f""
1980
+ if count == 0:
1981
+ const = ir.Const("", loc=self.loc)
1982
+ self.store(const, tmps[-1])
1983
+ return
1984
+
1985
+ prev = self.get(strings[0])
1986
+ for other, tmp in zip(strings[1:], tmps):
1987
+ other = self.get(other)
1988
+ expr = ir.Expr.binop(
1989
+ operator.add, lhs=prev, rhs=other, loc=self.loc
1990
+ )
1991
+ self.store(expr, tmp)
1992
+ prev = self.get(tmp)
1993
+
1994
+ def op_BUILD_SLICE(self, inst, start, stop, step, res, slicevar):
1995
+ start = self.get(start)
1996
+ stop = self.get(stop)
1997
+
1998
+ slicegv = ir.Global("slice", slice, loc=self.loc)
1999
+ self.store(value=slicegv, name=slicevar)
2000
+
2001
+ if step is None:
2002
+ sliceinst = ir.Expr.call(
2003
+ self.get(slicevar), (start, stop), (), loc=self.loc
2004
+ )
2005
+ else:
2006
+ step = self.get(step)
2007
+ sliceinst = ir.Expr.call(
2008
+ self.get(slicevar), (start, stop, step), (), loc=self.loc
2009
+ )
2010
+ self.store(value=sliceinst, name=res)
2011
+
2012
+ if PYVERSION in ((3, 12), (3, 13)):
2013
+
2014
+ def op_BINARY_SLICE(
2015
+ self, inst, start, end, container, res, slicevar, temp_res
2016
+ ):
2017
+ start = self.get(start)
2018
+ end = self.get(end)
2019
+ slicegv = ir.Global("slice", slice, loc=self.loc)
2020
+ self.store(value=slicegv, name=slicevar)
2021
+ sliceinst = ir.Expr.call(
2022
+ self.get(slicevar), (start, end), (), loc=self.loc
2023
+ )
2024
+ self.store(value=sliceinst, name=temp_res)
2025
+ index = self.get(temp_res)
2026
+ target = self.get(container)
2027
+ expr = ir.Expr.getitem(target, index=index, loc=self.loc)
2028
+ self.store(expr, res)
2029
+ elif PYVERSION in ((3, 10), (3, 11)):
2030
+ pass
2031
+ else:
2032
+ raise NotImplementedError(PYVERSION)
2033
+
2034
+ if PYVERSION in ((3, 12), (3, 13)):
2035
+
2036
+ def op_STORE_SLICE(
2037
+ self, inst, start, end, container, value, res, slicevar
2038
+ ):
2039
+ start = self.get(start)
2040
+ end = self.get(end)
2041
+ slicegv = ir.Global("slice", slice, loc=self.loc)
2042
+ self.store(value=slicegv, name=slicevar)
2043
+ sliceinst = ir.Expr.call(
2044
+ self.get(slicevar), (start, end), (), loc=self.loc
2045
+ )
2046
+ self.store(value=sliceinst, name=res)
2047
+ index = self.get(res)
2048
+ target = self.get(container)
2049
+ value = self.get(value)
2050
+
2051
+ stmt = ir.SetItem(
2052
+ target=target, index=index, value=value, loc=self.loc
2053
+ )
2054
+ self.current_block.append(stmt)
2055
+ elif PYVERSION in ((3, 10), (3, 11)):
2056
+ pass
2057
+ else:
2058
+ raise NotImplementedError(PYVERSION)
2059
+
2060
+ def op_SLICE_0(self, inst, base, res, slicevar, indexvar, nonevar):
2061
+ base = self.get(base)
2062
+
2063
+ slicegv = ir.Global("slice", slice, loc=self.loc)
2064
+ self.store(value=slicegv, name=slicevar)
2065
+
2066
+ nonegv = ir.Const(None, loc=self.loc)
2067
+ self.store(value=nonegv, name=nonevar)
2068
+ none = self.get(nonevar)
2069
+
2070
+ index = ir.Expr.call(self.get(slicevar), (none, none), (), loc=self.loc)
2071
+ self.store(value=index, name=indexvar)
2072
+
2073
+ expr = ir.Expr.getitem(base, self.get(indexvar), loc=self.loc)
2074
+ self.store(value=expr, name=res)
2075
+
2076
+ def op_SLICE_1(self, inst, base, start, nonevar, res, slicevar, indexvar):
2077
+ base = self.get(base)
2078
+ start = self.get(start)
2079
+
2080
+ nonegv = ir.Const(None, loc=self.loc)
2081
+ self.store(value=nonegv, name=nonevar)
2082
+ none = self.get(nonevar)
2083
+
2084
+ slicegv = ir.Global("slice", slice, loc=self.loc)
2085
+ self.store(value=slicegv, name=slicevar)
2086
+
2087
+ index = ir.Expr.call(
2088
+ self.get(slicevar), (start, none), (), loc=self.loc
2089
+ )
2090
+ self.store(value=index, name=indexvar)
2091
+
2092
+ expr = ir.Expr.getitem(base, self.get(indexvar), loc=self.loc)
2093
+ self.store(value=expr, name=res)
2094
+
2095
+ def op_SLICE_2(self, inst, base, nonevar, stop, res, slicevar, indexvar):
2096
+ base = self.get(base)
2097
+ stop = self.get(stop)
2098
+
2099
+ nonegv = ir.Const(None, loc=self.loc)
2100
+ self.store(value=nonegv, name=nonevar)
2101
+ none = self.get(nonevar)
2102
+
2103
+ slicegv = ir.Global("slice", slice, loc=self.loc)
2104
+ self.store(value=slicegv, name=slicevar)
2105
+
2106
+ index = ir.Expr.call(
2107
+ self.get(slicevar),
2108
+ (
2109
+ none,
2110
+ stop,
2111
+ ),
2112
+ (),
2113
+ loc=self.loc,
2114
+ )
2115
+ self.store(value=index, name=indexvar)
2116
+
2117
+ expr = ir.Expr.getitem(base, self.get(indexvar), loc=self.loc)
2118
+ self.store(value=expr, name=res)
2119
+
2120
+ def op_SLICE_3(self, inst, base, start, stop, res, slicevar, indexvar):
2121
+ base = self.get(base)
2122
+ start = self.get(start)
2123
+ stop = self.get(stop)
2124
+
2125
+ slicegv = ir.Global("slice", slice, loc=self.loc)
2126
+ self.store(value=slicegv, name=slicevar)
2127
+
2128
+ index = ir.Expr.call(
2129
+ self.get(slicevar), (start, stop), (), loc=self.loc
2130
+ )
2131
+ self.store(value=index, name=indexvar)
2132
+
2133
+ expr = ir.Expr.getitem(base, self.get(indexvar), loc=self.loc)
2134
+ self.store(value=expr, name=res)
2135
+
2136
+ def op_STORE_SLICE_0(self, inst, base, value, slicevar, indexvar, nonevar):
2137
+ base = self.get(base)
2138
+
2139
+ slicegv = ir.Global("slice", slice, loc=self.loc)
2140
+ self.store(value=slicegv, name=slicevar)
2141
+
2142
+ nonegv = ir.Const(None, loc=self.loc)
2143
+ self.store(value=nonegv, name=nonevar)
2144
+ none = self.get(nonevar)
2145
+
2146
+ index = ir.Expr.call(self.get(slicevar), (none, none), (), loc=self.loc)
2147
+ self.store(value=index, name=indexvar)
2148
+
2149
+ stmt = ir.SetItem(
2150
+ base, self.get(indexvar), self.get(value), loc=self.loc
2151
+ )
2152
+ self.current_block.append(stmt)
2153
+
2154
+ def op_STORE_SLICE_1(
2155
+ self, inst, base, start, nonevar, value, slicevar, indexvar
2156
+ ):
2157
+ base = self.get(base)
2158
+ start = self.get(start)
2159
+
2160
+ nonegv = ir.Const(None, loc=self.loc)
2161
+ self.store(value=nonegv, name=nonevar)
2162
+ none = self.get(nonevar)
2163
+
2164
+ slicegv = ir.Global("slice", slice, loc=self.loc)
2165
+ self.store(value=slicegv, name=slicevar)
2166
+
2167
+ index = ir.Expr.call(
2168
+ self.get(slicevar), (start, none), (), loc=self.loc
2169
+ )
2170
+ self.store(value=index, name=indexvar)
2171
+
2172
+ stmt = ir.SetItem(
2173
+ base, self.get(indexvar), self.get(value), loc=self.loc
2174
+ )
2175
+ self.current_block.append(stmt)
2176
+
2177
+ def op_STORE_SLICE_2(
2178
+ self, inst, base, nonevar, stop, value, slicevar, indexvar
2179
+ ):
2180
+ base = self.get(base)
2181
+ stop = self.get(stop)
2182
+
2183
+ nonegv = ir.Const(None, loc=self.loc)
2184
+ self.store(value=nonegv, name=nonevar)
2185
+ none = self.get(nonevar)
2186
+
2187
+ slicegv = ir.Global("slice", slice, loc=self.loc)
2188
+ self.store(value=slicegv, name=slicevar)
2189
+
2190
+ index = ir.Expr.call(
2191
+ self.get(slicevar),
2192
+ (
2193
+ none,
2194
+ stop,
2195
+ ),
2196
+ (),
2197
+ loc=self.loc,
2198
+ )
2199
+ self.store(value=index, name=indexvar)
2200
+
2201
+ stmt = ir.SetItem(
2202
+ base, self.get(indexvar), self.get(value), loc=self.loc
2203
+ )
2204
+ self.current_block.append(stmt)
2205
+
2206
+ def op_STORE_SLICE_3(
2207
+ self, inst, base, start, stop, value, slicevar, indexvar
2208
+ ):
2209
+ base = self.get(base)
2210
+ start = self.get(start)
2211
+ stop = self.get(stop)
2212
+
2213
+ slicegv = ir.Global("slice", slice, loc=self.loc)
2214
+ self.store(value=slicegv, name=slicevar)
2215
+
2216
+ index = ir.Expr.call(
2217
+ self.get(slicevar), (start, stop), (), loc=self.loc
2218
+ )
2219
+ self.store(value=index, name=indexvar)
2220
+ stmt = ir.SetItem(
2221
+ base, self.get(indexvar), self.get(value), loc=self.loc
2222
+ )
2223
+ self.current_block.append(stmt)
2224
+
2225
+ def op_DELETE_SLICE_0(self, inst, base, slicevar, indexvar, nonevar):
2226
+ base = self.get(base)
2227
+
2228
+ slicegv = ir.Global("slice", slice, loc=self.loc)
2229
+ self.store(value=slicegv, name=slicevar)
2230
+
2231
+ nonegv = ir.Const(None, loc=self.loc)
2232
+ self.store(value=nonegv, name=nonevar)
2233
+ none = self.get(nonevar)
2234
+
2235
+ index = ir.Expr.call(self.get(slicevar), (none, none), (), loc=self.loc)
2236
+ self.store(value=index, name=indexvar)
2237
+
2238
+ stmt = ir.DelItem(base, self.get(indexvar), loc=self.loc)
2239
+ self.current_block.append(stmt)
2240
+
2241
+ def op_DELETE_SLICE_1(self, inst, base, start, nonevar, slicevar, indexvar):
2242
+ base = self.get(base)
2243
+ start = self.get(start)
2244
+
2245
+ nonegv = ir.Const(None, loc=self.loc)
2246
+ self.store(value=nonegv, name=nonevar)
2247
+ none = self.get(nonevar)
2248
+
2249
+ slicegv = ir.Global("slice", slice, loc=self.loc)
2250
+ self.store(value=slicegv, name=slicevar)
2251
+
2252
+ index = ir.Expr.call(
2253
+ self.get(slicevar), (start, none), (), loc=self.loc
2254
+ )
2255
+ self.store(value=index, name=indexvar)
2256
+
2257
+ stmt = ir.DelItem(base, self.get(indexvar), loc=self.loc)
2258
+ self.current_block.append(stmt)
2259
+
2260
+ def op_DELETE_SLICE_2(self, inst, base, nonevar, stop, slicevar, indexvar):
2261
+ base = self.get(base)
2262
+ stop = self.get(stop)
2263
+
2264
+ nonegv = ir.Const(None, loc=self.loc)
2265
+ self.store(value=nonegv, name=nonevar)
2266
+ none = self.get(nonevar)
2267
+
2268
+ slicegv = ir.Global("slice", slice, loc=self.loc)
2269
+ self.store(value=slicegv, name=slicevar)
2270
+
2271
+ index = ir.Expr.call(
2272
+ self.get(slicevar),
2273
+ (
2274
+ none,
2275
+ stop,
2276
+ ),
2277
+ (),
2278
+ loc=self.loc,
2279
+ )
2280
+ self.store(value=index, name=indexvar)
2281
+
2282
+ stmt = ir.DelItem(base, self.get(indexvar), loc=self.loc)
2283
+ self.current_block.append(stmt)
2284
+
2285
+ def op_DELETE_SLICE_3(self, inst, base, start, stop, slicevar, indexvar):
2286
+ base = self.get(base)
2287
+ start = self.get(start)
2288
+ stop = self.get(stop)
2289
+
2290
+ slicegv = ir.Global("slice", slice, loc=self.loc)
2291
+ self.store(value=slicegv, name=slicevar)
2292
+
2293
+ index = ir.Expr.call(
2294
+ self.get(slicevar), (start, stop), (), loc=self.loc
2295
+ )
2296
+ self.store(value=index, name=indexvar)
2297
+ stmt = ir.DelItem(base, self.get(indexvar), loc=self.loc)
2298
+ self.current_block.append(stmt)
2299
+
2300
+ def _op_LOAD_FAST(self, inst, res):
2301
+ srcname = self.code_locals[inst.arg]
2302
+ self.store(value=self.get(srcname), name=res)
2303
+
2304
+ if PYVERSION in ((3, 13),):
2305
+
2306
+ def op_LOAD_FAST(self, inst, res, as_load_deref=False):
2307
+ if as_load_deref:
2308
+ self.op_LOAD_DEREF(inst, res)
2309
+ else:
2310
+ self._op_LOAD_FAST(inst, res)
2311
+
2312
+ else:
2313
+ op_LOAD_FAST = _op_LOAD_FAST
2314
+
2315
+ if PYVERSION in ((3, 13),):
2316
+
2317
+ def op_LOAD_FAST_LOAD_FAST(self, inst, res1, res2):
2318
+ oparg = inst.arg
2319
+ oparg1 = oparg >> 4
2320
+ oparg2 = oparg & 15
2321
+ src1 = self.get(self.code_locals[oparg1])
2322
+ src2 = self.get(self.code_locals[oparg2])
2323
+ self.store(value=src1, name=res1)
2324
+ self.store(value=src2, name=res2)
2325
+
2326
+ def op_STORE_FAST_LOAD_FAST(self, inst, store_value, load_res):
2327
+ oparg = inst.arg
2328
+ oparg1 = oparg >> 4
2329
+ oparg2 = oparg & 15
2330
+
2331
+ dstname = self.code_locals[oparg1]
2332
+ dst_value = self.get(store_value)
2333
+ self.store(value=dst_value, name=dstname)
2334
+
2335
+ src_value = self.get(self.code_locals[oparg2])
2336
+ self.store(value=src_value, name=load_res)
2337
+
2338
+ def op_STORE_FAST_STORE_FAST(self, inst, value1, value2):
2339
+ oparg = inst.arg
2340
+ oparg1 = oparg >> 4
2341
+ oparg2 = oparg & 15
2342
+
2343
+ dstname = self.code_locals[oparg1]
2344
+ self.store(value=self.get(value1), name=dstname)
2345
+ dstname = self.code_locals[oparg2]
2346
+ self.store(value=self.get(value2), name=dstname)
2347
+
2348
+ elif PYVERSION in ((3, 10), (3, 11), (3, 12)):
2349
+ pass
2350
+ else:
2351
+ raise NotImplementedError(PYVERSION)
2352
+
2353
+ if PYVERSION in ((3, 12), (3, 13)):
2354
+ op_LOAD_FAST_CHECK = op_LOAD_FAST
2355
+
2356
+ def op_LOAD_FAST_AND_CLEAR(self, inst, res):
2357
+ try:
2358
+ # try the regular LOAD_FAST logic
2359
+ srcname = self.code_locals[inst.arg]
2360
+ self.store(value=self.get(srcname), name=res)
2361
+ except NotDefinedError:
2362
+ # If the variable is not in the scope, set it to `undef`
2363
+ undef = ir.Expr.undef(loc=self.loc)
2364
+ self.store(undef, name=res)
2365
+
2366
+ elif PYVERSION in ((3, 10), (3, 11)):
2367
+ pass
2368
+ else:
2369
+ raise NotImplementedError(PYVERSION)
2370
+
2371
+ def op_STORE_FAST(self, inst, value):
2372
+ dstname = self.code_locals[inst.arg]
2373
+ value = self.get(value)
2374
+ self.store(value=value, name=dstname)
2375
+
2376
+ def op_DELETE_FAST(self, inst):
2377
+ dstname = self.code_locals[inst.arg]
2378
+ self.current_block.append(ir.Del(dstname, loc=self.loc))
2379
+
2380
+ def op_DUP_TOPX(self, inst, orig, duped):
2381
+ for src, dst in zip(orig, duped):
2382
+ self.store(value=self.get(src), name=dst)
2383
+
2384
+ op_DUP_TOP = op_DUP_TOPX
2385
+ op_DUP_TOP_TWO = op_DUP_TOPX
2386
+
2387
+ def op_STORE_ATTR(self, inst, target, value):
2388
+ attr = self.code_names[inst.arg]
2389
+ sa = ir.SetAttr(
2390
+ target=self.get(target),
2391
+ value=self.get(value),
2392
+ attr=attr,
2393
+ loc=self.loc,
2394
+ )
2395
+ self.current_block.append(sa)
2396
+
2397
+ def op_DELETE_ATTR(self, inst, target):
2398
+ attr = self.code_names[inst.arg]
2399
+ sa = ir.DelAttr(target=self.get(target), attr=attr, loc=self.loc)
2400
+ self.current_block.append(sa)
2401
+
2402
+ def op_LOAD_ATTR(self, inst, item, res):
2403
+ item = self.get(item)
2404
+ if PYVERSION in ((3, 12), (3, 13)):
2405
+ attr = self.code_names[inst.arg >> 1]
2406
+ elif PYVERSION in ((3, 10), (3, 11)):
2407
+ attr = self.code_names[inst.arg]
2408
+ else:
2409
+ raise NotImplementedError(PYVERSION)
2410
+ getattr = ir.Expr.getattr(item, attr, loc=self.loc)
2411
+ self.store(getattr, res)
2412
+
2413
+ def op_LOAD_CONST(self, inst, res):
2414
+ value = self.code_consts[inst.arg]
2415
+ if isinstance(value, tuple):
2416
+ st = []
2417
+ for x in value:
2418
+ nm = "$const_%s" % str(x)
2419
+ val_const = ir.Const(x, loc=self.loc)
2420
+ target = self.store(val_const, name=nm, redefine=True)
2421
+ st.append(target)
2422
+ const = ir.Expr.build_tuple(st, loc=self.loc)
2423
+ elif isinstance(value, frozenset):
2424
+ st = []
2425
+ for x in value:
2426
+ nm = "$const_%s" % str(x)
2427
+ val_const = ir.Const(x, loc=self.loc)
2428
+ target = self.store(val_const, name=nm, redefine=True)
2429
+ st.append(target)
2430
+ const = ir.Expr.build_set(st, loc=self.loc)
2431
+ else:
2432
+ const = ir.Const(value, loc=self.loc)
2433
+ self.store(const, res)
2434
+
2435
+ if PYVERSION in ((3, 11), (3, 12), (3, 13)):
2436
+
2437
+ def op_LOAD_GLOBAL(self, inst, idx, res):
2438
+ name = self.code_names[idx]
2439
+ value = self.get_global_value(name)
2440
+ gl = ir.Global(name, value, loc=self.loc)
2441
+ self.store(gl, res)
2442
+ elif PYVERSION in ((3, 10),):
2443
+
2444
+ def op_LOAD_GLOBAL(self, inst, res):
2445
+ name = self.code_names[inst.arg]
2446
+ value = self.get_global_value(name)
2447
+ gl = ir.Global(name, value, loc=self.loc)
2448
+ self.store(gl, res)
2449
+ else:
2450
+ raise NotImplementedError(PYVERSION)
2451
+
2452
+ def op_COPY_FREE_VARS(self, inst):
2453
+ pass
2454
+
2455
+ if PYVERSION in ((3, 11), (3, 12), (3, 13)):
2456
+
2457
+ def op_LOAD_DEREF(self, inst, res):
2458
+ name = self.func_id.func.__code__._varname_from_oparg(inst.arg)
2459
+ if name in self.code_cellvars:
2460
+ try:
2461
+ gl = self.get(name)
2462
+ except NotDefinedError:
2463
+ msg = "Unsupported use of cell variable encountered"
2464
+ raise NotImplementedError(msg)
2465
+ elif name in self.code_freevars:
2466
+ idx = self.code_freevars.index(name)
2467
+ value = self.get_closure_value(idx)
2468
+ gl = ir.FreeVar(idx, name, value, loc=self.loc)
2469
+ self.store(gl, res)
2470
+ elif PYVERSION in ((3, 10),):
2471
+
2472
+ def op_LOAD_DEREF(self, inst, res):
2473
+ n_cellvars = len(self.code_cellvars)
2474
+ if inst.arg < n_cellvars:
2475
+ name = self.code_cellvars[inst.arg]
2476
+ gl = self.get(name)
2477
+ else:
2478
+ idx = inst.arg - n_cellvars
2479
+ name = self.code_freevars[idx]
2480
+ value = self.get_closure_value(idx)
2481
+ gl = ir.FreeVar(idx, name, value, loc=self.loc)
2482
+ self.store(gl, res)
2483
+ else:
2484
+ raise NotImplementedError(PYVERSION)
2485
+
2486
+ if PYVERSION in ((3, 11), (3, 12), (3, 13)):
2487
+
2488
+ def op_MAKE_CELL(self, inst):
2489
+ pass # ignored bytecode
2490
+
2491
+ if PYVERSION in ((3, 11), (3, 12), (3, 13)):
2492
+
2493
+ def op_STORE_DEREF(self, inst, value):
2494
+ name = self.func_id.func.__code__._varname_from_oparg(inst.arg)
2495
+ value = self.get(value)
2496
+ self.store(value=value, name=name)
2497
+ elif PYVERSION in ((3, 10),):
2498
+
2499
+ def op_STORE_DEREF(self, inst, value):
2500
+ n_cellvars = len(self.code_cellvars)
2501
+ if inst.arg < n_cellvars:
2502
+ dstname = self.code_cellvars[inst.arg]
2503
+ else:
2504
+ dstname = self.code_freevars[inst.arg - n_cellvars]
2505
+ value = self.get(value)
2506
+ self.store(value=value, name=dstname)
2507
+ else:
2508
+ raise NotImplementedError(PYVERSION)
2509
+
2510
+ def op_SETUP_LOOP(self, inst):
2511
+ assert self.blocks[inst.offset] is self.current_block
2512
+ loop = ir.Loop(inst.offset, exit=(inst.next + inst.arg))
2513
+ self.syntax_blocks.append(loop)
2514
+
2515
+ def op_SETUP_WITH(self, inst, contextmanager, exitfn=None):
2516
+ assert self.blocks[inst.offset] is self.current_block
2517
+ # Handle with
2518
+ exitpt = inst.next + inst.arg
2519
+
2520
+ wth = ir.With(inst.offset, exit=exitpt)
2521
+ self.syntax_blocks.append(wth)
2522
+ ctxmgr = self.get(contextmanager)
2523
+ self.current_block.append(
2524
+ ir.EnterWith(
2525
+ contextmanager=ctxmgr,
2526
+ begin=inst.offset,
2527
+ end=exitpt,
2528
+ loc=self.loc,
2529
+ )
2530
+ )
2531
+
2532
+ # Store exit fn
2533
+ exit_fn_obj = ir.Const(None, loc=self.loc)
2534
+ self.store(value=exit_fn_obj, name=exitfn)
2535
+
2536
+ def op_BEFORE_WITH(self, inst, contextmanager, exitfn, end):
2537
+ assert self.blocks[inst.offset] is self.current_block
2538
+ if PYVERSION in ((3, 12), (3, 13)):
2539
+ # Python 3.12 hack for handling nested with blocks
2540
+ if end > self.last_active_offset:
2541
+ # Use exception entries to figure out end of syntax block
2542
+ end = max(
2543
+ [
2544
+ ex.end
2545
+ for ex in self.active_exception_entries
2546
+ if ex.target == end
2547
+ ]
2548
+ )
2549
+ elif PYVERSION in ((3, 10), (3, 11)):
2550
+ pass
2551
+ else:
2552
+ raise NotImplementedError(PYVERSION)
2553
+ # Handle with
2554
+ wth = ir.With(inst.offset, exit=end)
2555
+ self.syntax_blocks.append(wth)
2556
+ ctxmgr = self.get(contextmanager)
2557
+ self.current_block.append(
2558
+ ir.EnterWith(
2559
+ contextmanager=ctxmgr,
2560
+ begin=inst.offset,
2561
+ end=end,
2562
+ loc=self.loc,
2563
+ )
2564
+ )
2565
+
2566
+ # Store exit function
2567
+ exit_fn_obj = ir.Const(None, loc=self.loc)
2568
+ self.store(value=exit_fn_obj, name=exitfn)
2569
+
2570
+ def op_SETUP_FINALLY(self, inst):
2571
+ # Removed since python3.11
2572
+ self._insert_try_block_begin()
2573
+
2574
+ def op_WITH_CLEANUP(self, inst):
2575
+ "no-op"
2576
+
2577
+ def op_WITH_CLEANUP_START(self, inst):
2578
+ "no-op"
2579
+
2580
+ def op_WITH_CLEANUP_FINISH(self, inst):
2581
+ "no-op"
2582
+
2583
+ def op_END_FINALLY(self, inst):
2584
+ "no-op"
2585
+
2586
+ def op_BEGIN_FINALLY(self, inst, temps):
2587
+ # The *temps* are the exception variables
2588
+ const_none = ir.Const(None, loc=self.loc)
2589
+ for tmp in temps:
2590
+ # Set to None for now
2591
+ self.store(const_none, name=tmp)
2592
+ self._exception_vars.add(tmp)
2593
+
2594
+ def op_CALL(self, inst, func, args, kw_names, res):
2595
+ func = self.get(func)
2596
+ args = [self.get(x) for x in args]
2597
+ if kw_names is not None:
2598
+ assert PYVERSION < (3, 13)
2599
+ names = self.code_consts[kw_names]
2600
+ kwargs = list(zip(names, args[-len(names) :]))
2601
+ args = args[: -len(names)]
2602
+ else:
2603
+ kwargs = ()
2604
+ expr = ir.Expr.call(func, args, kwargs, loc=self.loc)
2605
+ self.store(expr, res)
2606
+
2607
+ if PYVERSION in ((3, 13),):
2608
+
2609
+ def op_CALL_KW(self, inst, func, args, kw_names, res):
2610
+ func = self.get(func)
2611
+ args = [self.get(x) for x in args]
2612
+ consti = int(kw_names.rsplit(".", 2)[-1])
2613
+ names = self.code_consts[consti]
2614
+ kwargs = list(zip(names, args[-len(names) :]))
2615
+ args = args[: -len(names)]
2616
+ expr = ir.Expr.call(func, args, kwargs, loc=self.loc)
2617
+ self.store(expr, res)
2618
+ else:
2619
+ assert PYVERSION < (3, 13)
2620
+
2621
+ def op_CALL_FUNCTION(self, inst, func, args, res):
2622
+ func = self.get(func)
2623
+ args = [self.get(x) for x in args]
2624
+ expr = ir.Expr.call(func, args, (), loc=self.loc)
2625
+ self.store(expr, res)
2626
+
2627
+ def op_CALL_FUNCTION_KW(self, inst, func, args, names, res):
2628
+ func = self.get(func)
2629
+ args = [self.get(x) for x in args]
2630
+ # Find names const
2631
+ names = self.get(names)
2632
+ for inst in self.current_block.body:
2633
+ if isinstance(inst, ir.Assign) and inst.target is names:
2634
+ self.current_block.remove(inst)
2635
+ # scan up the block looking for the values, remove them
2636
+ # and find their name strings
2637
+ named_items = []
2638
+ for x in inst.value.items:
2639
+ for y in self.current_block.body[::-1]:
2640
+ if x == y.target:
2641
+ self.current_block.remove(y)
2642
+ named_items.append(y.value.value)
2643
+ break
2644
+ keys = named_items
2645
+ break
2646
+
2647
+ nkeys = len(keys)
2648
+ posvals = args[:-nkeys]
2649
+ kwvals = args[-nkeys:]
2650
+ keyvalues = list(zip(keys, kwvals))
2651
+
2652
+ expr = ir.Expr.call(func, posvals, keyvalues, loc=self.loc)
2653
+ self.store(expr, res)
2654
+
2655
+ def op_CALL_FUNCTION_EX(self, inst, func, vararg, varkwarg, res):
2656
+ func = self.get(func)
2657
+ vararg = self.get(vararg)
2658
+ if varkwarg is not None:
2659
+ varkwarg = self.get(varkwarg)
2660
+ expr = ir.Expr.call(
2661
+ func, [], [], loc=self.loc, vararg=vararg, varkwarg=varkwarg
2662
+ )
2663
+ self.store(expr, res)
2664
+
2665
+ def _build_tuple_unpack(self, inst, tuples, temps, is_assign):
2666
+ first = self.get(tuples[0])
2667
+ if is_assign:
2668
+ # it's assign-like, defer handling to an intrinsic that will have
2669
+ # type information.
2670
+ # Can deal with tuples only, i.e. y = (*x,). where x = <tuple>
2671
+ gv_name = "unpack_single_tuple"
2672
+ gv_fn = ir.Global(
2673
+ gv_name,
2674
+ unpack_single_tuple,
2675
+ loc=self.loc,
2676
+ )
2677
+ self.store(value=gv_fn, name=gv_name, redefine=True)
2678
+ exc = ir.Expr.call(
2679
+ self.get(gv_name),
2680
+ args=(first,),
2681
+ kws=(),
2682
+ loc=self.loc,
2683
+ )
2684
+ self.store(exc, temps[0])
2685
+ else:
2686
+ loc = self.loc
2687
+ for other, tmp in zip(map(self.get, tuples[1:]), temps):
2688
+ # Emit as `first + tuple(other)`
2689
+ gv_tuple = ir.Global(
2690
+ name="tuple",
2691
+ value=tuple,
2692
+ loc=loc,
2693
+ )
2694
+ tuple_var = self.store(
2695
+ gv_tuple,
2696
+ "$_list_extend_gv_tuple",
2697
+ redefine=True,
2698
+ )
2699
+ tuplify_val = ir.Expr.call(
2700
+ tuple_var,
2701
+ (other,),
2702
+ (),
2703
+ loc=loc,
2704
+ )
2705
+ tuplify_var = self.store(
2706
+ tuplify_val, "$_tuplify", redefine=True
2707
+ )
2708
+ out = ir.Expr.binop(
2709
+ fn=operator.add,
2710
+ lhs=first,
2711
+ rhs=self.get(tuplify_var.name),
2712
+ loc=self.loc,
2713
+ )
2714
+ self.store(out, tmp)
2715
+ first = self.get(tmp)
2716
+
2717
+ def op_BUILD_TUPLE_UNPACK_WITH_CALL(self, inst, tuples, temps, is_assign):
2718
+ # just unpack the input tuple, call inst will be handled afterwards
2719
+ self._build_tuple_unpack(inst, tuples, temps, is_assign)
2720
+
2721
+ def op_BUILD_TUPLE_UNPACK(self, inst, tuples, temps, is_assign):
2722
+ self._build_tuple_unpack(inst, tuples, temps, is_assign)
2723
+
2724
+ def op_LIST_TO_TUPLE(self, inst, const_list, res):
2725
+ expr = ir.Expr.dummy("list_to_tuple", (const_list,), loc=self.loc)
2726
+ self.store(expr, res)
2727
+
2728
+ def op_BUILD_CONST_KEY_MAP(self, inst, keys, keytmps, values, res):
2729
+ # Unpack the constant key-tuple and reused build_map which takes
2730
+ # a sequence of (key, value) pair.
2731
+ keyvar = self.get(keys)
2732
+ # TODO: refactor this pattern. occurred several times.
2733
+ for inst in self.current_block.body:
2734
+ if isinstance(inst, ir.Assign) and inst.target is keyvar:
2735
+ self.current_block.remove(inst)
2736
+ # scan up the block looking for the values, remove them
2737
+ # and find their name strings
2738
+ named_items = []
2739
+ for x in inst.value.items:
2740
+ for y in self.current_block.body[::-1]:
2741
+ if x == y.target:
2742
+ self.current_block.remove(y)
2743
+ named_items.append(y.value.value)
2744
+ break
2745
+ keytup = named_items
2746
+ break
2747
+ assert len(keytup) == len(values)
2748
+ keyconsts = [ir.Const(value=x, loc=self.loc) for x in keytup]
2749
+ for kval, tmp in zip(keyconsts, keytmps):
2750
+ self.store(kval, tmp)
2751
+ items = list(zip(map(self.get, keytmps), map(self.get, values)))
2752
+
2753
+ # sort out literal values
2754
+ literal_items = []
2755
+ for v in values:
2756
+ defns = self.definitions[v]
2757
+ if len(defns) != 1:
2758
+ break
2759
+ defn = defns[0]
2760
+ if not isinstance(defn, ir.Const):
2761
+ break
2762
+ literal_items.append(defn.value)
2763
+
2764
+ def resolve_const(v):
2765
+ defns = self.definitions[v]
2766
+ if len(defns) != 1:
2767
+ return _UNKNOWN_VALUE(self.get(v).name)
2768
+ defn = defns[0]
2769
+ if not isinstance(defn, ir.Const):
2770
+ return _UNKNOWN_VALUE(self.get(v).name)
2771
+ return defn.value
2772
+
2773
+ if len(literal_items) != len(values):
2774
+ literal_dict = {x: resolve_const(y) for x, y in zip(keytup, values)}
2775
+ else:
2776
+ literal_dict = {x: y for x, y in zip(keytup, literal_items)}
2777
+
2778
+ # to deal with things like {'a': 1, 'a': 'cat', 'b': 2, 'a': 2j}
2779
+ # store the index of the actual used value for a given key, this is
2780
+ # used when lowering to pull the right value out into the tuple repr
2781
+ # of a mixed value type dictionary.
2782
+ value_indexes = {}
2783
+ for i, k in enumerate(keytup):
2784
+ value_indexes[k] = i
2785
+
2786
+ expr = ir.Expr.build_map(
2787
+ items=items,
2788
+ size=2,
2789
+ literal_value=literal_dict,
2790
+ value_indexes=value_indexes,
2791
+ loc=self.loc,
2792
+ )
2793
+
2794
+ self.store(expr, res)
2795
+
2796
+ def op_GET_ITER(self, inst, value, res):
2797
+ expr = ir.Expr.getiter(value=self.get(value), loc=self.loc)
2798
+ self.store(expr, res)
2799
+
2800
+ def op_FOR_ITER(self, inst, iterator, pair, indval, pred):
2801
+ """
2802
+ Assign new block other this instruction.
2803
+ """
2804
+ assert inst.offset in self.blocks, "FOR_ITER must be block head"
2805
+
2806
+ # Emit code
2807
+ val = self.get(iterator)
2808
+
2809
+ pairval = ir.Expr.iternext(value=val, loc=self.loc)
2810
+ self.store(pairval, pair)
2811
+
2812
+ iternext = ir.Expr.pair_first(value=self.get(pair), loc=self.loc)
2813
+ self.store(iternext, indval)
2814
+
2815
+ isvalid = ir.Expr.pair_second(value=self.get(pair), loc=self.loc)
2816
+ self.store(isvalid, pred)
2817
+
2818
+ # Conditional jump
2819
+ br = ir.Branch(
2820
+ cond=self.get(pred),
2821
+ truebr=inst.next,
2822
+ falsebr=inst.get_jump_target(),
2823
+ loc=self.loc,
2824
+ )
2825
+ self.current_block.append(br)
2826
+
2827
+ def op_BINARY_SUBSCR(self, inst, target, index, res):
2828
+ index = self.get(index)
2829
+ target = self.get(target)
2830
+ expr = ir.Expr.getitem(target, index=index, loc=self.loc)
2831
+ self.store(expr, res)
2832
+
2833
+ def op_STORE_SUBSCR(self, inst, target, index, value):
2834
+ index = self.get(index)
2835
+ target = self.get(target)
2836
+ value = self.get(value)
2837
+ stmt = ir.SetItem(target=target, index=index, value=value, loc=self.loc)
2838
+ self.current_block.append(stmt)
2839
+
2840
+ def op_DELETE_SUBSCR(self, inst, target, index):
2841
+ index = self.get(index)
2842
+ target = self.get(target)
2843
+ stmt = ir.DelItem(target=target, index=index, loc=self.loc)
2844
+ self.current_block.append(stmt)
2845
+
2846
+ def op_BUILD_TUPLE(self, inst, items, res):
2847
+ expr = ir.Expr.build_tuple(
2848
+ items=[self.get(x) for x in items], loc=self.loc
2849
+ )
2850
+ self.store(expr, res)
2851
+
2852
+ def op_BUILD_LIST(self, inst, items, res):
2853
+ expr = ir.Expr.build_list(
2854
+ items=[self.get(x) for x in items], loc=self.loc
2855
+ )
2856
+ self.store(expr, res)
2857
+
2858
+ def op_BUILD_SET(self, inst, items, res):
2859
+ expr = ir.Expr.build_set(
2860
+ items=[self.get(x) for x in items], loc=self.loc
2861
+ )
2862
+ self.store(expr, res)
2863
+
2864
+ def op_SET_UPDATE(self, inst, target, value, updatevar, res):
2865
+ target = self.get(target)
2866
+ value = self.get(value)
2867
+ updateattr = ir.Expr.getattr(target, "update", loc=self.loc)
2868
+ self.store(value=updateattr, name=updatevar)
2869
+ updateinst = ir.Expr.call(
2870
+ self.get(updatevar), (value,), (), loc=self.loc
2871
+ )
2872
+ self.store(value=updateinst, name=res)
2873
+
2874
+ def op_DICT_UPDATE(self, inst, target, value, updatevar, res):
2875
+ target = self.get(target)
2876
+ value = self.get(value)
2877
+ # We generate _update_from_bytecode instead of update so we can
2878
+ # differentiate between user .update() calls and those from the
2879
+ # bytecode. This is then used to recombine dictionaries in peephole
2880
+ # optimizations. See the dicussion in this PR about why:
2881
+ # https://github.com/numba/numba/pull/7964/files#r868229306
2882
+ updateattr = ir.Expr.getattr(
2883
+ target, "_update_from_bytecode", loc=self.loc
2884
+ )
2885
+ self.store(value=updateattr, name=updatevar)
2886
+ updateinst = ir.Expr.call(
2887
+ self.get(updatevar), (value,), (), loc=self.loc
2888
+ )
2889
+ self.store(value=updateinst, name=res)
2890
+
2891
+ def op_BUILD_MAP(self, inst, items, size, res):
2892
+ got_items = [(self.get(k), self.get(v)) for k, v in items]
2893
+
2894
+ # sort out literal values, this is a bit contrived but is to handle
2895
+ # situations like `{1: 10, 1: 10}` where the size of the literal dict
2896
+ # is smaller than the definition
2897
+ def get_literals(target):
2898
+ literal_items = []
2899
+ values = [self.get(v.name) for v in target]
2900
+ for v in values:
2901
+ defns = self.definitions[v.name]
2902
+ if len(defns) != 1:
2903
+ break
2904
+ defn = defns[0]
2905
+ if not isinstance(defn, ir.Const):
2906
+ break
2907
+ literal_items.append(defn.value)
2908
+ return literal_items
2909
+
2910
+ literal_keys = get_literals(x[0] for x in got_items)
2911
+ literal_values = get_literals(x[1] for x in got_items)
2912
+
2913
+ has_literal_keys = len(literal_keys) == len(got_items)
2914
+ has_literal_values = len(literal_values) == len(got_items)
2915
+
2916
+ value_indexes = {}
2917
+ if not has_literal_keys and not has_literal_values:
2918
+ literal_dict = None
2919
+ elif has_literal_keys and not has_literal_values:
2920
+ literal_dict = {
2921
+ x: _UNKNOWN_VALUE(y[1]) for x, y in zip(literal_keys, got_items)
2922
+ }
2923
+ for i, k in enumerate(literal_keys):
2924
+ value_indexes[k] = i
2925
+ else:
2926
+ literal_dict = {x: y for x, y in zip(literal_keys, literal_values)}
2927
+ for i, k in enumerate(literal_keys):
2928
+ value_indexes[k] = i
2929
+
2930
+ expr = ir.Expr.build_map(
2931
+ items=got_items,
2932
+ size=size,
2933
+ literal_value=literal_dict,
2934
+ value_indexes=value_indexes,
2935
+ loc=self.loc,
2936
+ )
2937
+ self.store(expr, res)
2938
+
2939
+ def op_STORE_MAP(self, inst, dct, key, value):
2940
+ stmt = ir.StoreMap(
2941
+ dct=self.get(dct),
2942
+ key=self.get(key),
2943
+ value=self.get(value),
2944
+ loc=self.loc,
2945
+ )
2946
+ self.current_block.append(stmt)
2947
+
2948
+ def op_UNARY_NEGATIVE(self, inst, value, res):
2949
+ value = self.get(value)
2950
+ expr = ir.Expr.unary("-", value=value, loc=self.loc)
2951
+ return self.store(expr, res)
2952
+
2953
+ def op_UNARY_POSITIVE(self, inst, value, res):
2954
+ value = self.get(value)
2955
+ expr = ir.Expr.unary("+", value=value, loc=self.loc)
2956
+ return self.store(expr, res)
2957
+
2958
+ def op_UNARY_INVERT(self, inst, value, res):
2959
+ value = self.get(value)
2960
+ expr = ir.Expr.unary("~", value=value, loc=self.loc)
2961
+ return self.store(expr, res)
2962
+
2963
+ def op_UNARY_NOT(self, inst, value, res):
2964
+ value = self.get(value)
2965
+ expr = ir.Expr.unary("not", value=value, loc=self.loc)
2966
+ return self.store(expr, res)
2967
+
2968
+ def _binop(self, op, lhs, rhs, res):
2969
+ op = BINOPS_TO_OPERATORS[op]
2970
+ lhs = self.get(lhs)
2971
+ rhs = self.get(rhs)
2972
+ expr = ir.Expr.binop(op, lhs=lhs, rhs=rhs, loc=self.loc)
2973
+ self.store(expr, res)
2974
+
2975
+ def _inplace_binop(self, op, lhs, rhs, res):
2976
+ immuop = BINOPS_TO_OPERATORS[op]
2977
+ op = INPLACE_BINOPS_TO_OPERATORS[op + "="]
2978
+ lhs = self.get(lhs)
2979
+ rhs = self.get(rhs)
2980
+ expr = ir.Expr.inplace_binop(op, immuop, lhs=lhs, rhs=rhs, loc=self.loc)
2981
+ self.store(expr, res)
2982
+
2983
+ def op_BINARY_OP(self, inst, op, lhs, rhs, res):
2984
+ if "=" in op:
2985
+ self._inplace_binop(op[:-1], lhs, rhs, res)
2986
+ else:
2987
+ self._binop(op, lhs, rhs, res)
2988
+
2989
+ def op_BINARY_ADD(self, inst, lhs, rhs, res):
2990
+ self._binop("+", lhs, rhs, res)
2991
+
2992
+ def op_BINARY_SUBTRACT(self, inst, lhs, rhs, res):
2993
+ self._binop("-", lhs, rhs, res)
2994
+
2995
+ def op_BINARY_MULTIPLY(self, inst, lhs, rhs, res):
2996
+ self._binop("*", lhs, rhs, res)
2997
+
2998
+ def op_BINARY_DIVIDE(self, inst, lhs, rhs, res):
2999
+ self._binop("/?", lhs, rhs, res)
3000
+
3001
+ def op_BINARY_TRUE_DIVIDE(self, inst, lhs, rhs, res):
3002
+ self._binop("/", lhs, rhs, res)
3003
+
3004
+ def op_BINARY_FLOOR_DIVIDE(self, inst, lhs, rhs, res):
3005
+ self._binop("//", lhs, rhs, res)
3006
+
3007
+ def op_BINARY_MODULO(self, inst, lhs, rhs, res):
3008
+ self._binop("%", lhs, rhs, res)
3009
+
3010
+ def op_BINARY_POWER(self, inst, lhs, rhs, res):
3011
+ self._binop("**", lhs, rhs, res)
3012
+
3013
+ def op_BINARY_MATRIX_MULTIPLY(self, inst, lhs, rhs, res):
3014
+ self._binop("@", lhs, rhs, res)
3015
+
3016
+ def op_BINARY_LSHIFT(self, inst, lhs, rhs, res):
3017
+ self._binop("<<", lhs, rhs, res)
3018
+
3019
+ def op_BINARY_RSHIFT(self, inst, lhs, rhs, res):
3020
+ self._binop(">>", lhs, rhs, res)
3021
+
3022
+ def op_BINARY_AND(self, inst, lhs, rhs, res):
3023
+ self._binop("&", lhs, rhs, res)
3024
+
3025
+ def op_BINARY_OR(self, inst, lhs, rhs, res):
3026
+ self._binop("|", lhs, rhs, res)
3027
+
3028
+ def op_BINARY_XOR(self, inst, lhs, rhs, res):
3029
+ self._binop("^", lhs, rhs, res)
3030
+
3031
+ def op_INPLACE_ADD(self, inst, lhs, rhs, res):
3032
+ self._inplace_binop("+", lhs, rhs, res)
3033
+
3034
+ def op_INPLACE_SUBTRACT(self, inst, lhs, rhs, res):
3035
+ self._inplace_binop("-", lhs, rhs, res)
3036
+
3037
+ def op_INPLACE_MULTIPLY(self, inst, lhs, rhs, res):
3038
+ self._inplace_binop("*", lhs, rhs, res)
3039
+
3040
+ def op_INPLACE_DIVIDE(self, inst, lhs, rhs, res):
3041
+ self._inplace_binop("/?", lhs, rhs, res)
3042
+
3043
+ def op_INPLACE_TRUE_DIVIDE(self, inst, lhs, rhs, res):
3044
+ self._inplace_binop("/", lhs, rhs, res)
3045
+
3046
+ def op_INPLACE_FLOOR_DIVIDE(self, inst, lhs, rhs, res):
3047
+ self._inplace_binop("//", lhs, rhs, res)
3048
+
3049
+ def op_INPLACE_MODULO(self, inst, lhs, rhs, res):
3050
+ self._inplace_binop("%", lhs, rhs, res)
3051
+
3052
+ def op_INPLACE_POWER(self, inst, lhs, rhs, res):
3053
+ self._inplace_binop("**", lhs, rhs, res)
3054
+
3055
+ def op_INPLACE_MATRIX_MULTIPLY(self, inst, lhs, rhs, res):
3056
+ self._inplace_binop("@", lhs, rhs, res)
3057
+
3058
+ def op_INPLACE_LSHIFT(self, inst, lhs, rhs, res):
3059
+ self._inplace_binop("<<", lhs, rhs, res)
3060
+
3061
+ def op_INPLACE_RSHIFT(self, inst, lhs, rhs, res):
3062
+ self._inplace_binop(">>", lhs, rhs, res)
3063
+
3064
+ def op_INPLACE_AND(self, inst, lhs, rhs, res):
3065
+ self._inplace_binop("&", lhs, rhs, res)
3066
+
3067
+ def op_INPLACE_OR(self, inst, lhs, rhs, res):
3068
+ self._inplace_binop("|", lhs, rhs, res)
3069
+
3070
+ def op_INPLACE_XOR(self, inst, lhs, rhs, res):
3071
+ self._inplace_binop("^", lhs, rhs, res)
3072
+
3073
+ def op_JUMP_ABSOLUTE(self, inst):
3074
+ jmp = ir.Jump(inst.get_jump_target(), loc=self.loc)
3075
+ self.current_block.append(jmp)
3076
+
3077
+ def op_JUMP_FORWARD(self, inst):
3078
+ jmp = ir.Jump(inst.get_jump_target(), loc=self.loc)
3079
+ self.current_block.append(jmp)
3080
+
3081
+ def op_JUMP_BACKWARD(self, inst):
3082
+ jmp = ir.Jump(inst.get_jump_target(), loc=self.loc)
3083
+ self.current_block.append(jmp)
3084
+
3085
+ op_JUMP_BACKWARD_NO_INTERRUPT = op_JUMP_BACKWARD
3086
+
3087
+ def op_POP_BLOCK(self, inst, kind=None):
3088
+ if kind is None:
3089
+ self.syntax_blocks.pop()
3090
+ elif kind == "with":
3091
+ d = ir.PopBlock(loc=self.loc)
3092
+ self.current_block.append(d)
3093
+ elif kind == "try":
3094
+ self._insert_try_block_end()
3095
+
3096
+ def op_RETURN_VALUE(self, inst, retval, castval):
3097
+ self.store(ir.Expr.cast(self.get(retval), loc=self.loc), castval)
3098
+ ret = ir.Return(self.get(castval), loc=self.loc)
3099
+ self.current_block.append(ret)
3100
+
3101
+ if PYVERSION in ((3, 12), (3, 13)):
3102
+
3103
+ def op_RETURN_CONST(self, inst, retval, castval):
3104
+ value = self.code_consts[inst.arg]
3105
+ const = ir.Const(value, loc=self.loc)
3106
+ self.store(const, retval)
3107
+ self.store(ir.Expr.cast(self.get(retval), loc=self.loc), castval)
3108
+ ret = ir.Return(self.get(castval), loc=self.loc)
3109
+ self.current_block.append(ret)
3110
+ elif PYVERSION in ((3, 10), (3, 11)):
3111
+ pass
3112
+ else:
3113
+ raise NotImplementedError(PYVERSION)
3114
+
3115
+ if PYVERSION in ((3, 13),):
3116
+
3117
+ def op_TO_BOOL(self, inst, val, res):
3118
+ self.store(self.get(val), res) # TODO: just a lazy hack
3119
+
3120
+ elif PYVERSION in ((3, 10), (3, 11), (3, 12)):
3121
+ pass
3122
+ else:
3123
+ raise NotImplementedError(PYVERSION)
3124
+
3125
+ def op_COMPARE_OP(self, inst, lhs, rhs, res):
3126
+ if PYVERSION in ((3, 13),):
3127
+ op = dis.cmp_op[inst.arg >> 5]
3128
+ # TODO: fifth lowest bit now indicates a forced version to bool.
3129
+ elif PYVERSION in ((3, 12),):
3130
+ op = dis.cmp_op[inst.arg >> 4]
3131
+ elif PYVERSION in ((3, 10), (3, 11)):
3132
+ op = dis.cmp_op[inst.arg]
3133
+ else:
3134
+ raise NotImplementedError(PYVERSION)
3135
+ if op == "in" or op == "not in":
3136
+ lhs, rhs = rhs, lhs
3137
+
3138
+ if op == "not in":
3139
+ self._binop("in", lhs, rhs, res)
3140
+ tmp = self.get(res)
3141
+ out = ir.Expr.unary("not", value=tmp, loc=self.loc)
3142
+ self.store(out, res)
3143
+ elif op == "exception match":
3144
+ gv_fn = ir.Global(
3145
+ "exception_match",
3146
+ eh.exception_match,
3147
+ loc=self.loc,
3148
+ )
3149
+ exc_match_name = "$exc_match"
3150
+ self.store(value=gv_fn, name=exc_match_name, redefine=True)
3151
+ lhs = self.get(lhs)
3152
+ rhs = self.get(rhs)
3153
+ exc = ir.Expr.call(
3154
+ self.get(exc_match_name),
3155
+ args=(lhs, rhs),
3156
+ kws=(),
3157
+ loc=self.loc,
3158
+ )
3159
+ self.store(exc, res)
3160
+ else:
3161
+ self._binop(op, lhs, rhs, res)
3162
+
3163
+ def op_IS_OP(self, inst, lhs, rhs, res):
3164
+ # invert if op case is 1
3165
+ op = "is not" if inst.arg == 1 else "is"
3166
+ self._binop(op, lhs, rhs, res)
3167
+
3168
+ def op_CONTAINS_OP(self, inst, lhs, rhs, res):
3169
+ lhs, rhs = rhs, lhs
3170
+ self._binop("in", lhs, rhs, res)
3171
+ # invert if op case is 1
3172
+ if inst.arg == 1:
3173
+ tmp = self.get(res)
3174
+ out = ir.Expr.unary("not", value=tmp, loc=self.loc)
3175
+ self.store(out, res)
3176
+
3177
+ def op_BREAK_LOOP(self, inst, end=None):
3178
+ if end is None:
3179
+ loop = self.syntax_blocks[-1]
3180
+ assert isinstance(loop, ir.Loop)
3181
+ end = loop.exit
3182
+ jmp = ir.Jump(target=end, loc=self.loc)
3183
+ self.current_block.append(jmp)
3184
+
3185
+ def _op_JUMP_IF(self, inst, pred, iftrue):
3186
+ brs = {
3187
+ True: inst.get_jump_target(),
3188
+ False: inst.next,
3189
+ }
3190
+ truebr = brs[iftrue]
3191
+ falsebr = brs[not iftrue]
3192
+
3193
+ name = "$bool%s" % (inst.offset)
3194
+ gv_fn = ir.Global("bool", bool, loc=self.loc)
3195
+ self.store(value=gv_fn, name=name)
3196
+
3197
+ callres = ir.Expr.call(
3198
+ self.get(name), (self.get(pred),), (), loc=self.loc
3199
+ )
3200
+
3201
+ pname = "$%spred" % (inst.offset)
3202
+ predicate = self.store(value=callres, name=pname)
3203
+ bra = ir.Branch(
3204
+ cond=predicate, truebr=truebr, falsebr=falsebr, loc=self.loc
3205
+ )
3206
+ self.current_block.append(bra)
3207
+
3208
+ def op_JUMP_IF_FALSE(self, inst, pred):
3209
+ self._op_JUMP_IF(inst, pred=pred, iftrue=False)
3210
+
3211
+ def op_JUMP_IF_TRUE(self, inst, pred):
3212
+ self._op_JUMP_IF(inst, pred=pred, iftrue=True)
3213
+
3214
+ def _jump_if_none(self, inst, pred, iftrue):
3215
+ # branch pruning assumes true falls through and false is jump
3216
+ truebr = inst.next
3217
+ falsebr = inst.get_jump_target()
3218
+
3219
+ # this seems strange
3220
+ if not iftrue:
3221
+ op = BINOPS_TO_OPERATORS["is"]
3222
+ else:
3223
+ op = BINOPS_TO_OPERATORS["is not"]
3224
+
3225
+ rhs = self.store(
3226
+ value=ir.Const(None, loc=self.loc), name=f"$constNone{inst.offset}"
3227
+ )
3228
+ lhs = self.get(pred)
3229
+ isnone = ir.Expr.binop(op, lhs=lhs, rhs=rhs, loc=self.loc)
3230
+
3231
+ maybeNone = f"$maybeNone{inst.offset}"
3232
+ self.store(value=isnone, name=maybeNone)
3233
+
3234
+ name = f"$bool{inst.offset}"
3235
+ gv_fn = ir.Global("bool", bool, loc=self.loc)
3236
+ self.store(value=gv_fn, name=name)
3237
+
3238
+ callres = ir.Expr.call(
3239
+ self.get(name), (self.get(maybeNone),), (), loc=self.loc
3240
+ )
3241
+
3242
+ pname = f"$pred{inst.offset}"
3243
+ predicate = self.store(value=callres, name=pname)
3244
+ branch = ir.Branch(
3245
+ cond=predicate, truebr=truebr, falsebr=falsebr, loc=self.loc
3246
+ )
3247
+ self.current_block.append(branch)
3248
+
3249
+ def op_POP_JUMP_FORWARD_IF_NONE(self, inst, pred):
3250
+ self._jump_if_none(inst, pred, True)
3251
+
3252
+ def op_POP_JUMP_FORWARD_IF_NOT_NONE(self, inst, pred):
3253
+ self._jump_if_none(inst, pred, False)
3254
+
3255
+ if PYVERSION in ((3, 12), (3, 13)):
3256
+
3257
+ def op_POP_JUMP_IF_NONE(self, inst, pred):
3258
+ self._jump_if_none(inst, pred, True)
3259
+
3260
+ def op_POP_JUMP_IF_NOT_NONE(self, inst, pred):
3261
+ self._jump_if_none(inst, pred, False)
3262
+ elif PYVERSION in ((3, 10), (3, 11)):
3263
+ pass
3264
+ else:
3265
+ raise NotImplementedError(PYVERSION)
3266
+
3267
+ def op_POP_JUMP_BACKWARD_IF_NONE(self, inst, pred):
3268
+ self._jump_if_none(inst, pred, True)
3269
+
3270
+ def op_POP_JUMP_BACKWARD_IF_NOT_NONE(self, inst, pred):
3271
+ self._jump_if_none(inst, pred, False)
3272
+
3273
+ def op_POP_JUMP_FORWARD_IF_FALSE(self, inst, pred):
3274
+ self._op_JUMP_IF(inst, pred=pred, iftrue=False)
3275
+
3276
+ def op_POP_JUMP_FORWARD_IF_TRUE(self, inst, pred):
3277
+ self._op_JUMP_IF(inst, pred=pred, iftrue=True)
3278
+
3279
+ def op_POP_JUMP_BACKWARD_IF_FALSE(self, inst, pred):
3280
+ self._op_JUMP_IF(inst, pred=pred, iftrue=False)
3281
+
3282
+ def op_POP_JUMP_BACKWARD_IF_TRUE(self, inst, pred):
3283
+ self._op_JUMP_IF(inst, pred=pred, iftrue=True)
3284
+
3285
+ def op_POP_JUMP_IF_FALSE(self, inst, pred):
3286
+ self._op_JUMP_IF(inst, pred=pred, iftrue=False)
3287
+
3288
+ def op_POP_JUMP_IF_TRUE(self, inst, pred):
3289
+ self._op_JUMP_IF(inst, pred=pred, iftrue=True)
3290
+
3291
+ def op_JUMP_IF_FALSE_OR_POP(self, inst, pred):
3292
+ self._op_JUMP_IF(inst, pred=pred, iftrue=False)
3293
+
3294
+ def op_JUMP_IF_TRUE_OR_POP(self, inst, pred):
3295
+ self._op_JUMP_IF(inst, pred=pred, iftrue=True)
3296
+
3297
+ def op_CHECK_EXC_MATCH(self, inst, pred, tos, tos1):
3298
+ gv_fn = ir.Global(
3299
+ "exception_match",
3300
+ eh.exception_match,
3301
+ loc=self.loc,
3302
+ )
3303
+ exc_match_name = "$exc_match"
3304
+ self.store(value=gv_fn, name=exc_match_name, redefine=True)
3305
+ lhs = self.get(tos1)
3306
+ rhs = self.get(tos)
3307
+ exc = ir.Expr.call(
3308
+ self.get(exc_match_name),
3309
+ args=(lhs, rhs),
3310
+ kws=(),
3311
+ loc=self.loc,
3312
+ )
3313
+ self.store(exc, pred)
3314
+
3315
+ def op_JUMP_IF_NOT_EXC_MATCH(self, inst, pred, tos, tos1):
3316
+ truebr = inst.next
3317
+ falsebr = inst.get_jump_target()
3318
+ gv_fn = ir.Global(
3319
+ "exception_match",
3320
+ eh.exception_match,
3321
+ loc=self.loc,
3322
+ )
3323
+ exc_match_name = "$exc_match"
3324
+ self.store(value=gv_fn, name=exc_match_name, redefine=True)
3325
+ lhs = self.get(tos1)
3326
+ rhs = self.get(tos)
3327
+ exc = ir.Expr.call(
3328
+ self.get(exc_match_name),
3329
+ args=(lhs, rhs),
3330
+ kws=(),
3331
+ loc=self.loc,
3332
+ )
3333
+ predicate = self.store(exc, pred)
3334
+ bra = ir.Branch(
3335
+ cond=predicate, truebr=truebr, falsebr=falsebr, loc=self.loc
3336
+ )
3337
+ self.current_block.append(bra)
3338
+
3339
+ def op_RERAISE(self, inst, exc):
3340
+ tryblk = self.dfainfo.active_try_block
3341
+ if tryblk is not None:
3342
+ stmt = ir.TryRaise(exception=None, loc=self.loc)
3343
+ self.current_block.append(stmt)
3344
+ self._insert_try_block_end()
3345
+ self.current_block.append(ir.Jump(tryblk["end"], loc=self.loc))
3346
+ else:
3347
+ # Numba can't handle this case and it's caught else where, this is a
3348
+ # runtime guard in case this is reached by unknown means.
3349
+ msg = (
3350
+ f"Unreachable condition reached (op code RERAISE executed)"
3351
+ f"{error_extras['reportable']}"
3352
+ )
3353
+ stmt = ir.StaticRaise(AssertionError, (msg,), self.loc)
3354
+ self.current_block.append(stmt)
3355
+
3356
+ def op_RAISE_VARARGS(self, inst, exc):
3357
+ if exc is not None:
3358
+ exc = self.get(exc)
3359
+ tryblk = self.dfainfo.active_try_block
3360
+ if tryblk is not None:
3361
+ # In a try block
3362
+ stmt = ir.TryRaise(exception=exc, loc=self.loc)
3363
+ self.current_block.append(stmt)
3364
+ self._insert_try_block_end()
3365
+ self.current_block.append(ir.Jump(tryblk["end"], loc=self.loc))
3366
+ else:
3367
+ # Not in a try block
3368
+ stmt = ir.Raise(exception=exc, loc=self.loc)
3369
+ self.current_block.append(stmt)
3370
+
3371
+ def op_YIELD_VALUE(self, inst, value, res):
3372
+ # initialize index to None. it's being set later in post-processing
3373
+ index = None
3374
+ inst = ir.Yield(value=self.get(value), index=index, loc=self.loc)
3375
+ return self.store(inst, res)
3376
+
3377
+ def op_MAKE_FUNCTION(
3378
+ self, inst, name, code, closure, annotations, kwdefaults, defaults, res
3379
+ ):
3380
+ # annotations are ignored by numba but useful for static analysis
3381
+ # re. https://github.com/numba/numba/issues/7269
3382
+ if kwdefaults is not None:
3383
+ msg = "op_MAKE_FUNCTION with kwdefaults is not implemented"
3384
+ raise NotImplementedError(msg)
3385
+ if defaults:
3386
+ if isinstance(defaults, tuple):
3387
+ defaults = tuple([self.get(name) for name in defaults])
3388
+ else:
3389
+ defaults = self.get(defaults)
3390
+
3391
+ assume_code_const = self.definitions[code][0]
3392
+ if not isinstance(assume_code_const, ir.Const):
3393
+ msg = (
3394
+ "Unsupported use of closure. "
3395
+ "Probably caused by complex control-flow constructs; "
3396
+ "e.g. try-except"
3397
+ )
3398
+ raise errors.UnsupportedBytecodeError(msg, loc=self.loc)
3399
+ fcode = assume_code_const.value
3400
+ if name:
3401
+ name = self.get(name)
3402
+ if closure:
3403
+ closure = self.get(closure)
3404
+ expr = ir.Expr.make_function(name, fcode, closure, defaults, self.loc)
3405
+ self.store(expr, res)
3406
+
3407
+ def op_MAKE_CLOSURE(
3408
+ self, inst, name, code, closure, annotations, kwdefaults, defaults, res
3409
+ ):
3410
+ self.op_MAKE_FUNCTION(
3411
+ inst, name, code, closure, annotations, kwdefaults, defaults, res
3412
+ )
3413
+
3414
+ if PYVERSION in ((3, 11), (3, 12), (3, 13)):
3415
+
3416
+ def op_LOAD_CLOSURE(self, inst, res):
3417
+ name = self.func_id.func.__code__._varname_from_oparg(inst.arg)
3418
+ if name in self.code_cellvars:
3419
+ try:
3420
+ gl = self.get(name)
3421
+ except NotDefinedError:
3422
+ msg = "Unsupported use of cell variable encountered"
3423
+ raise NotImplementedError(msg)
3424
+ elif name in self.code_freevars:
3425
+ idx = self.code_freevars.index(name)
3426
+ value = self.get_closure_value(idx)
3427
+ gl = ir.FreeVar(idx, name, value, loc=self.loc)
3428
+ else:
3429
+ assert 0, "unreachable"
3430
+ self.store(gl, res)
3431
+
3432
+ elif PYVERSION in ((3, 10),):
3433
+
3434
+ def op_LOAD_CLOSURE(self, inst, res):
3435
+ n_cellvars = len(self.code_cellvars)
3436
+ if inst.arg < n_cellvars:
3437
+ name = self.code_cellvars[inst.arg]
3438
+ try:
3439
+ gl = self.get(name)
3440
+ except NotDefinedError:
3441
+ msg = "Unsupported use of cell variable encountered"
3442
+ raise NotImplementedError(msg)
3443
+ else:
3444
+ idx = inst.arg - n_cellvars
3445
+ name = self.code_freevars[idx]
3446
+ value = self.get_closure_value(idx)
3447
+ gl = ir.FreeVar(idx, name, value, loc=self.loc)
3448
+ self.store(gl, res)
3449
+ else:
3450
+ raise NotImplementedError(PYVERSION)
3451
+
3452
+ def op_LIST_APPEND(self, inst, target, value, appendvar, res):
3453
+ target = self.get(target)
3454
+ value = self.get(value)
3455
+ appendattr = ir.Expr.getattr(target, "append", loc=self.loc)
3456
+ self.store(value=appendattr, name=appendvar)
3457
+ appendinst = ir.Expr.call(
3458
+ self.get(appendvar), (value,), (), loc=self.loc
3459
+ )
3460
+ self.store(value=appendinst, name=res)
3461
+
3462
+ def op_LIST_EXTEND(self, inst, target, value, extendvar, res):
3463
+ target = self.get(target)
3464
+ value = self.get(value)
3465
+ # If the statements between the current instruction and the target
3466
+ # are N * consts followed by build_tuple AND the target has no items,
3467
+ # it's a situation where a list is being statically initialised, rewrite
3468
+ # the build_tuple as a build_list, drop the extend, and wire up the
3469
+ # target as the result from the build_tuple that's been rewritten.
3470
+
3471
+ # See if this is the first statement in a block, if so its probably from
3472
+ # control flow in a tuple unpack like:
3473
+ # `(*(1, (2,) if predicate else (3,)))`
3474
+ # this cannot be handled as present so raise
3475
+ msg = (
3476
+ "An unsupported bytecode sequence has been encountered: "
3477
+ "op_LIST_EXTEND at the start of a block.\n\nThis could be "
3478
+ "due to the use of a branch in a tuple unpacking statement."
3479
+ )
3480
+ if not self.current_block.body:
3481
+ raise errors.UnsupportedBytecodeError(msg)
3482
+
3483
+ # is last emitted statement a build_tuple?
3484
+ stmt = self.current_block.body[-1]
3485
+ ok = isinstance(stmt.value, ir.Expr) and stmt.value.op == "build_tuple"
3486
+ # check statements from self.current_block.body[-1] through to target,
3487
+ # make sure they are consts
3488
+ build_empty_list = None
3489
+ if ok:
3490
+ for stmt in reversed(self.current_block.body[:-1]):
3491
+ if not isinstance(stmt, ir.Assign):
3492
+ ok = False
3493
+ break
3494
+ # if its not a const, it needs to be the `build_list` for the
3495
+ # target, else it's something else we don't know about so just
3496
+ # bail
3497
+ if isinstance(stmt.value, ir.Const):
3498
+ continue
3499
+
3500
+ # it's not a const, check for target
3501
+ elif isinstance(stmt.value, ir.Expr) and stmt.target == target:
3502
+ build_empty_list = stmt
3503
+ # it's only ok to do this if the target has no initializer
3504
+ # already
3505
+ ok = not stmt.value.items
3506
+ break
3507
+ else:
3508
+ ok = False
3509
+ break
3510
+ if ok and build_empty_list is None:
3511
+ raise errors.UnsupportedBytecodeError(msg)
3512
+ if ok:
3513
+ stmts = self.current_block.body
3514
+ build_tuple_asgn = self.current_block.body[-1]
3515
+ # move build list to last issued statement
3516
+ stmts.append(stmts.pop(stmts.index(build_empty_list)))
3517
+ # fix the build list
3518
+ build_tuple = build_tuple_asgn.value
3519
+ build_list = build_empty_list.value
3520
+ build_list.items = build_tuple.items
3521
+ else:
3522
+ # it's just a list extend with no static init, let it be
3523
+ extendattr = ir.Expr.getattr(target, "extend", loc=self.loc)
3524
+ self.store(value=extendattr, name=extendvar)
3525
+ extendinst = ir.Expr.call(
3526
+ self.get(extendvar), (value,), (), loc=self.loc
3527
+ )
3528
+ self.store(value=extendinst, name=res)
3529
+
3530
+ def op_MAP_ADD(self, inst, target, key, value, setitemvar, res):
3531
+ target = self.get(target)
3532
+ key = self.get(key)
3533
+ value = self.get(value)
3534
+ setitemattr = ir.Expr.getattr(target, "__setitem__", loc=self.loc)
3535
+ self.store(value=setitemattr, name=setitemvar)
3536
+ appendinst = ir.Expr.call(
3537
+ self.get(setitemvar),
3538
+ (
3539
+ key,
3540
+ value,
3541
+ ),
3542
+ (),
3543
+ loc=self.loc,
3544
+ )
3545
+ self.store(value=appendinst, name=res)
3546
+
3547
+ def op_LOAD_ASSERTION_ERROR(self, inst, res):
3548
+ gv_fn = ir.Global("AssertionError", AssertionError, loc=self.loc)
3549
+ self.store(value=gv_fn, name=res)
3550
+
3551
+ # NOTE: The LOAD_METHOD opcode is implemented as a LOAD_ATTR for ease,
3552
+ # however this means a new object (the bound-method instance) could be
3553
+ # created. Conversely, using a pure LOAD_METHOD no intermediary is present
3554
+ # and it is essentially like a pointer grab and forward to CALL_METHOD. The
3555
+ # net outcome is that the implementation in Numba produces the same result,
3556
+ # but in object mode it may be that it runs more slowly than it would if
3557
+ # run in CPython.
3558
+
3559
+ def op_LOAD_METHOD(self, *args, **kws):
3560
+ self.op_LOAD_ATTR(*args, **kws)
3561
+
3562
+ def op_CALL_METHOD(self, *args, **kws):
3563
+ self.op_CALL_FUNCTION(*args, **kws)
3564
+
3565
+ if PYVERSION in ((3, 12), (3, 13)):
3566
+
3567
+ def op_CALL_INTRINSIC_1(self, inst, operand, **kwargs):
3568
+ if operand == ci1op.INTRINSIC_STOPITERATION_ERROR:
3569
+ stmt = ir.StaticRaise(
3570
+ INTRINSIC_STOPITERATION_ERROR, (), self.loc
3571
+ )
3572
+ self.current_block.append(stmt)
3573
+ return
3574
+ elif operand == ci1op.UNARY_POSITIVE:
3575
+ self.op_UNARY_POSITIVE(inst, **kwargs)
3576
+ return
3577
+ elif operand == ci1op.INTRINSIC_LIST_TO_TUPLE:
3578
+ self.op_LIST_TO_TUPLE(inst, **kwargs)
3579
+ return
3580
+ else:
3581
+ raise NotImplementedError(operand)
3582
+ elif PYVERSION in ((3, 10), (3, 11)):
3583
+ pass
3584
+ else:
3585
+ raise NotImplementedError(PYVERSION)
3586
+
3587
+
3588
+ if PYVERSION in ((3, 12), (3, 13)):
3589
+
3590
+ class INTRINSIC_STOPITERATION_ERROR(AssertionError):
3591
+ pass
3592
+ elif PYVERSION in ((3, 10), (3, 11)):
3593
+ pass
3594
+ else:
3595
+ raise NotImplementedError(PYVERSION)