def run_pass(self, state): """Run inlining of overloads """ if self._DEBUG: print('before overload inline'.center(80, '-')) print(state.func_id.unique_name) print(state.func_ir.dump()) print(''.center(80, '-')) from numba.core.inline_closurecall import (InlineWorker, callee_ir_validator) inline_worker = InlineWorker( state.typingctx, state.targetctx, state.locals, state.pipeline, state.flags, callee_ir_validator, state.typemap, state.calltypes, ) modified = False work_list = list(state.func_ir.blocks.items()) # use a work list, look for call sites via `ir.Expr.op == call` and # then pass these to `self._do_work` to make decisions about inlining. while work_list: label, block = work_list.pop() for i, instr in enumerate(block.body): # TO-DO: other statements (setitem) if isinstance(instr, ir.Assign): expr = instr.value if isinstance(expr, ir.Expr): workfn = self._do_work_expr if guard(workfn, state, work_list, block, i, expr, inline_worker): modified = True break # because block structure changed if self._DEBUG: print('after overload inline'.center(80, '-')) print(state.func_id.unique_name) print(state.func_ir.dump()) print(''.center(80, '-')) if modified: # Remove dead blocks, this is safe as it relies on the CFG only. cfg = compute_cfg_from_blocks(state.func_ir.blocks) for dead in cfg.dead_nodes(): del state.func_ir.blocks[dead] # clean up blocks dead_code_elimination(state.func_ir, typemap=state.type_annotation.typemap) # clean up unconditional branches that appear due to inlined # functions introducing blocks state.func_ir.blocks = simplify_CFG(state.func_ir.blocks) if self._DEBUG: print('after overload inline DCE'.center(80, '-')) print(state.func_id.unique_name) print(state.func_ir.dump()) print(''.center(80, '-')) return True
def generic(self, args, kws): """ Type the overloaded function by compiling the appropriate implementation for the given args. """ disp, new_args = self._get_impl(args, kws) if disp is None: return # Compile and type it for the given types disp_type = types.Dispatcher(disp) # Store the compiled overload for use in the lowering phase if there's # no inlining required (else functions are being compiled which will # never be used as they are inlined) if not self._inline.is_never_inline: # need to run the compiler front end up to type inference to compute # a signature from numba.core import typed_passes, compiler from numba.core.inline_closurecall import InlineWorker fcomp = disp._compiler flags = compiler.Flags() # Updating these causes problems?! #fcomp.targetdescr.options.parse_as_flags(flags, # fcomp.targetoptions) #flags = fcomp._customize_flags(flags) # spoof a compiler pipline like the one that will be in use tyctx = fcomp.targetdescr.typing_context tgctx = fcomp.targetdescr.target_context compiler_inst = fcomp.pipeline_class( tyctx, tgctx, None, None, None, flags, None, ) inline_worker = InlineWorker( tyctx, tgctx, fcomp.locals, compiler_inst, flags, None, ) # If the inlinee contains something to trigger literal arg dispatch # then the pipeline call will unconditionally fail due to a raised # ForceLiteralArg exception. Therefore `resolve` is run first, as # type resolution must occur at some point, this will hit any # `literally` calls and because it's going via the dispatcher will # handle them correctly i.e. ForceLiteralArg propagates. This having # the desired effect of ensuring the pipeline call is only made in # situations that will succeed. For context see #5887. resolve = disp_type.dispatcher.get_call_template template, pysig, folded_args, kws = resolve(new_args, kws) ir = inline_worker.run_untyped_passes(disp_type.dispatcher.py_func) (typemap, return_type, calltypes, _) = typed_passes.type_inference_stage(self.context, ir, folded_args, None) sig = Signature(return_type, folded_args, None) # this stores a load of info for the cost model function if supplied # it by default is None self._inline_overloads[sig.args] = {'folded_args': folded_args} # this stores the compiled overloads, if there's no compiled # overload available i.e. function is always inlined, the key still # needs to exist for type resolution # NOTE: If lowering is failing on a `_EmptyImplementationEntry`, # the inliner has failed to inline this entry corretly. impl_init = _EmptyImplementationEntry('always inlined') self._compiled_overloads[sig.args] = impl_init if not self._inline.is_always_inline: # this branch is here because a user has supplied a function to # determine whether to inline or not. As a result both compiled # function and inliner info needed, delaying the computation of # this leads to an internal state mess at present. TODO: Fix! sig = disp_type.get_call_type(self.context, new_args, kws) self._compiled_overloads[sig.args] = disp_type.get_overload( sig) # store the inliner information, it's used later in the cost # model function call iinfo = _inline_info(ir, typemap, calltypes, sig) self._inline_overloads[sig.args] = { 'folded_args': folded_args, 'iinfo': iinfo } else: sig = disp_type.get_call_type(self.context, new_args, kws) self._compiled_overloads[sig.args] = disp_type.get_overload(sig) return sig