def _runPass(self, index, pss, internal_state): mutated = False def check(func, compiler_state): mangled = func(compiler_state) if mangled not in (True, False): msg = ( "CompilerPass implementations should return True/False. " "CompilerPass with name '%s' did not.") raise ValueError(msg % pss.name()) return mangled def debug_print(pass_name, print_condition, printable_condition): if pass_name in print_condition: fid = internal_state.func_id args = (fid.modname, fid.func_qualname, self.pipeline_name, printable_condition, pass_name) print(("%s.%s: %s: %s %s" % args).center(120, '-')) if internal_state.func_ir is not None: internal_state.func_ir.dump() else: print("func_ir is None") # debug print before this pass? debug_print(pss.name(), self._print_before + self._print_wrap, "BEFORE") # wire in the analysis info so it's accessible pss.analysis = self._analysis with SimpleTimer() as init_time: mutated |= check(pss.run_initialization, internal_state) with SimpleTimer() as pass_time: mutated |= check(pss.run_pass, internal_state) with SimpleTimer() as finalize_time: mutated |= check(pss.run_finalizer, internal_state) # Check that if the pass is an instance of a FunctionPass that it hasn't # emitted ir.Dels. if isinstance(pss, FunctionPass): enforce_no_dels(internal_state.func_ir) if self._ENFORCING: # TODO: Add in self consistency enforcement for # `func_ir._definitions` etc if _pass_registry.get(pss.__class__).mutates_CFG: if mutated: # block level changes, rebuild all PostProcessor(internal_state.func_ir).run() else: # CFG level changes rebuild CFG internal_state.func_ir.blocks = transforms.canonicalize_cfg( internal_state.func_ir.blocks) # inject runtimes pt = pass_timings(init_time.elapsed, pass_time.elapsed, finalize_time.elapsed) self.exec_times["%s_%s" % (index, pss.name())] = pt # debug print after this pass? debug_print(pss.name(), self._print_after + self._print_wrap, "AFTER")
def _pre_looplift_transform(func_ir): """Canonicalize loops for looplifting. """ from numba.core.postproc import PostProcessor cfg = compute_cfg_from_blocks(func_ir.blocks) # For every loop that has multiple exits, combine the exits into one. for loop_info in cfg.loops().values(): if _has_multiple_loop_exits(cfg, loop_info): func_ir, _common_key = _fix_multi_exit_blocks( func_ir, loop_info.exits) # Reset and reprocess the func_ir func_ir._reset_analysis_variables() PostProcessor(func_ir).run() return func_ir
def relatively_deep_copy(obj, memo): # WARNING: there are some issues with genarators which were not investigated # and root cause is not found. Though copied IR seems to work fine there are # some extra references kept on generator objects which may result in a # memory leak. obj_id = id(obj) if obj_id in memo: return memo[obj_id] from ctypes import _CFuncPtr from types import ModuleType from numba.core.bytecode import FunctionIdentity from numba.core.compiler import CompileResult from numba.core.dispatcher import _DispatcherBase from numba.core.types.abstract import Type from numba.core.types.functions import Dispatcher, Function from numba.core.typing.templates import Signature from numba.np.ufunc.dufunc import DUFunc from numba_dppy.compiler import DPPYFunctionTemplate # objects which shouldn't or can't be copied and it's ok not to copy it. if isinstance( obj, ( FunctionIdentity, _DispatcherBase, Function, Type, Dispatcher, ModuleType, Signature, DPPYFunctionTemplate, CompileResult, DUFunc, _CFuncPtr, type, str, bool, type(None), ), ): return obj from numba.core.funcdesc import FunctionDescriptor from numba.core.ir import FreeVar, FunctionIR, Global from numba.core.postproc import PostProcessor if isinstance(obj, FunctionDescriptor): cpy = FunctionDescriptor( native=obj.native, modname=obj.modname, qualname=obj.qualname, unique_name=obj.unique_name, doc=obj.doc, typemap=relatively_deep_copy(obj.typemap, memo), restype=obj.restype, calltypes=relatively_deep_copy(obj.calltypes, memo), args=obj.args, kws=obj.kws, mangler=None, argtypes=relatively_deep_copy(obj.argtypes, memo), inline=obj.inline, noalias=obj.noalias, env_name=obj.env_name, global_dict=obj.global_dict, ) # mangler parameter is not saved in FunctionDescriptor, but used to generated name. # So pass None as mangler parameter and then copy mangled_name by hands cpy.mangled_name = obj.mangled_name memo[obj_id] = cpy return cpy if isinstance(obj, FunctionIR): # PostProcessor do the following: # 1. canonicolize cfg, modifying IR # 2. fills internal generators status # 3. creates and fills VariableLifetime object # We can't copy this objects. So in order to have copy of it we need run PostProcessor on copied IR. # This means, that in case PostProcess wasn't run for original object copied object would defer. # In order to avoid this we are running PostProcess on original object firstly. # This means that copy of IR actually has a side effect on it. pp = PostProcessor(obj) pp.run() cpy = FunctionIR( blocks=relatively_deep_copy(obj.blocks, memo), is_generator=relatively_deep_copy(obj.is_generator, memo), func_id=relatively_deep_copy(obj.func_id, memo), loc=obj.loc, definitions=relatively_deep_copy(obj._definitions, memo), arg_count=obj.arg_count, arg_names=relatively_deep_copy(obj.arg_names, memo), ) pp = PostProcessor(cpy) pp.run() memo[obj_id] = cpy return cpy if isinstance(obj, Global): cpy = Global(name=obj.name, value=obj.value, loc=obj.loc) memo[obj_id] = cpy return cpy if isinstance(obj, FreeVar): cpy = FreeVar(index=obj.index, name=obj.name, value=obj.value, loc=obj.loc) memo[obj_id] = cpy return cpy # for containers we need to copy container itself first. And then fill it with copied items. if isinstance(obj, list): cpy = copy.copy(obj) cpy.clear() for item in obj: cpy.append(relatively_deep_copy(item, memo)) memo[obj_id] = cpy return cpy elif isinstance(obj, dict): cpy = copy.copy(obj) cpy.clear() for key, item in obj.items(): cpy[relatively_deep_copy(key, memo)] = relatively_deep_copy(item, memo) memo[obj_id] = cpy return cpy elif isinstance(obj, tuple): # subclass constructors could have different parameters than superclass. # e.g. tuple and namedtuple constructors accepts quite different parameters. # it is better to have separate section for namedtuple tpl = tuple([relatively_deep_copy(item, memo) for item in obj]) if type(obj) == tuple: cpy = tpl else: cpy = type(obj)(*tpl) memo[obj_id] = cpy return cpy elif isinstance(obj, set): cpy = copy.copy(obj) cpy.clear() for item in obj: cpy.add(relatively_deep_copy(item, memo)) memo[obj_id] = cpy return cpy # some python objects are not copyable. In such case exception would be raised # it is just a convinient point to find such objects try: cpy = copy.copy(obj) except Exception as e: raise e # __slots__ for subclass specify only members declared in subclass. So to get all members we need to go through # all supeclasses def get_slots_members(obj): keys = [] typ = obj if not isinstance(typ, type): typ = type(obj) try: if len(typ.__slots__): keys.extend(typ.__slots__) if len(typ.__bases__): for base in typ.__bases__: keys.extend(get_slots_members(base)) except: pass return keys memo[obj_id] = cpy keys = [] # Objects have either __dict__ or __slots__ or neither. # If object has none of it and it is copyable we already made a copy, just return it # If object is not copyable we shouldn't reach this point. try: keys = obj.__dict__.keys() except: try: obj.__slots__ keys = get_slots_members(obj) except: return cpy for key in keys: attr = getattr(obj, key) attr_cpy = relatively_deep_copy(attr, memo) setattr(cpy, key, attr_cpy) return cpy