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