def __init__(self, bytecode): self.bytecode = bytecode self.scopes = [] self.loc = ir.Loc(filename=bytecode.filename, line=1) self.argspec = bytecode.argspec # Control flow analysis self.cfa = controlflow.ControlFlowAnalysis(bytecode) self.cfa.run() # Data flow analysis self.dfa = dataflow.DataFlowAnalysis(self.cfa) self.dfa.run() global_scope = ir.Scope(parent=None, loc=self.loc) self._fill_global_scope(global_scope) self.scopes.append(global_scope) # { inst offset : ir.Block } self.blocks = {} # { name: value } of global variables used by the bytecode self.used_globals = {} # Temp states during interpretation self.current_block = None self.current_block_offset = None self.syntax_blocks = [] self.dfainfo = None self.constants = {}
class CheckEquality(unittest.TestCase): var_a = ir.Var(None, 'a', ir.unknown_loc) var_b = ir.Var(None, 'b', ir.unknown_loc) var_c = ir.Var(None, 'c', ir.unknown_loc) var_d = ir.Var(None, 'd', ir.unknown_loc) var_e = ir.Var(None, 'e', ir.unknown_loc) loc1 = ir.Loc('mock', 1, 0) loc2 = ir.Loc('mock', 2, 0) loc3 = ir.Loc('mock', 3, 0) def check(self, base, same=[], different=[]): for s in same: self.assertTrue(base == s) for d in different: self.assertTrue(base != d)
def _iter_inst(self): for block in self.cfa.iterliveblocks(): firstinst = self.bytecode[block.body[0]] self._start_new_block(firstinst) for offset, kws in self.dfainfo.insts: inst = self.bytecode[offset] self.loc = ir.Loc(filename=self.bytecode.filename, line=inst.lineno) yield inst, kws
def _start_new_block(self, inst): self.loc = ir.Loc(filename=self.bytecode.filename, line=inst.lineno) oldblock = self.current_block self.insert_block(inst.offset) # Ensure the last block is terminated if oldblock is not None and not oldblock.is_terminated: jmp = ir.Jump(inst.offset, loc=self.loc) oldblock.append(jmp) # Get DFA block info self.dfainfo = self.dfa.infos[self.current_block_offset] self.assigner = Assigner()
def interpret(self): firstblk = min(self.cfa.blocks.keys()) self.loc = ir.Loc(filename=self.bytecode.filename, line=self.bytecode[firstblk].lineno) self.scopes.append(ir.Scope(parent=self.current_scope, loc=self.loc)) self._fill_args_into_scope(self.current_scope) # Interpret loop for inst, kws in self._iter_inst(): self._dispatch(inst, kws) # Clean up self._insert_var_dels()
def _iter_inst(self): for blkct, block in enumerate(self.cfa.iterliveblocks()): firstinst = self.bytecode[block.body[0]] self._start_new_block(firstinst) if blkct == 0: # Is first block self.init_first_block() for offset, kws in self.dfainfo.insts: inst = self.bytecode[offset] self.loc = ir.Loc(filename=self.bytecode.filename, line=inst.lineno) yield inst, kws
def test_IRScope(self): filename = "<?>" top = ir.Scope(parent=None, loc=ir.Loc(filename=filename, line=1)) local = ir.Scope(parent=top, loc=ir.Loc(filename=filename, line=2)) apple = local.define('apple', loc=ir.Loc(filename=filename, line=3)) self.assertTrue(local.get('apple') is apple) self.assertEqual(len(local.localvars), 1) orange = top.define('orange', loc=ir.Loc(filename=filename, line=4)) self.assertEqual(len(local.localvars), 1) self.assertEqual(len(top.localvars), 1) self.assertTrue(top.get('orange') is orange) self.assertTrue(local.get('orange') is orange) more_orange = local.define('orange', loc=ir.Loc(filename=filename, line=5)) self.assertTrue(top.get('orange') is orange) self.assertTrue(local.get('orange') is not orange) self.assertTrue(local.get('orange') is more_orange) try: bad_orange = local.define('orange', loc=ir.Loc(filename=filename, line=5)) except ir.RedefinedError: pass else: self.fail("Expecting an %s" % ir.RedefinedError)
def interpret(self): firstblk = min(self.cfa.blocks.keys()) self.loc = ir.Loc(filename=self.bytecode.filename, line=self.bytecode[firstblk].lineno) self.scopes.append(ir.Scope(parent=self.current_scope, loc=self.loc)) # Interpret loop for inst, kws in self._iter_inst(): self._dispatch(inst, kws) # Post-processing and analysis on generated IR self._insert_var_dels() self._compute_live_variables() if self.generator_info: self._compute_generator_info()
def test_loc(self): a = ir.Loc('file', 1, 0) b = ir.Loc('file', 1, 0) c = ir.Loc('pile', 1, 0) d = ir.Loc('file', 2, 0) e = ir.Loc('file', 1, 1) self.check(a, same=[b,], different=[c, d, e]) f = ir.Loc('file', 1, 0, maybe_decorator=False) g = ir.Loc('file', 1, 0, maybe_decorator=True) self.check(a, same=[f, g])
def _start_new_block(self, inst): self.loc = ir.Loc(filename=self.bytecode.filename, line=inst.lineno) oldblock = self.current_block self.insert_block(inst.offset) # Ensure the last block is terminated if oldblock is not None and not oldblock.is_terminated: jmp = ir.Jump(inst.offset, loc=self.loc) oldblock.append(jmp) # Get DFA block info self.dfainfo = self.dfa.infos[self.current_block_offset] # Insert PHI self._insert_phi() # Notify listeners for the new block for fn in utils.dict_itervalues(self._block_actions): fn(self.current_block_offset, self.current_block)
def __init__(self, bytecode): self.bytecode = bytecode self.scopes = [] self.loc = ir.Loc(filename=bytecode.filename, line=1) self.arg_count = bytecode.arg_count self.arg_names = bytecode.arg_names # Control flow analysis self.cfa = controlflow.ControlFlowAnalysis(bytecode) self.cfa.run() # Data flow analysis self.dfa = dataflow.DataFlowAnalysis(self.cfa) self.dfa.run() global_scope = ir.Scope(parent=None, loc=self.loc) self._fill_global_scope(global_scope) self.scopes.append(global_scope) # { inst offset : ir.Block } self.blocks = {} # { name: value } of global variables used by the bytecode self.used_globals = {} # { name: [definitions] } of local variables self.definitions = collections.defaultdict(list) # { ir.Block: { variable names (potentially) alive at start of block } } self.block_entry_vars = {} if self.bytecode.is_generator: self.generator_info = GeneratorInfo() else: self.generator_info = None # Temp states during interpretation self.current_block = None self.current_block_offset = None self.syntax_blocks = [] self.dfainfo = None
def _mk_stencil_parfor(self, label, in_args, out_arr, stencil_ir, index_offsets, target, return_type, stencil_func, arg_to_arr_dict): """ Converts a set of stencil kernel blocks to a parfor. """ gen_nodes = [] stencil_blocks = stencil_ir.blocks if config.DEBUG_ARRAY_OPT == 1: print("_mk_stencil_parfor", label, in_args, out_arr, index_offsets, return_type, stencil_func, stencil_blocks) ir_utils.dump_blocks(stencil_blocks) in_arr = in_args[0] # run copy propagate to replace in_args copies (e.g. a = A) in_arr_typ = self.typemap[in_arr.name] in_cps, out_cps = ir_utils.copy_propagate(stencil_blocks, self.typemap) name_var_table = ir_utils.get_name_var_table(stencil_blocks) ir_utils.apply_copy_propagate( stencil_blocks, in_cps, name_var_table, self.typemap, self.calltypes) if config.DEBUG_ARRAY_OPT == 1: print("stencil_blocks after copy_propagate") ir_utils.dump_blocks(stencil_blocks) ir_utils.remove_dead(stencil_blocks, self.func_ir.arg_names, stencil_ir, self.typemap) if config.DEBUG_ARRAY_OPT == 1: print("stencil_blocks after removing dead code") ir_utils.dump_blocks(stencil_blocks) # create parfor vars ndims = self.typemap[in_arr.name].ndim scope = in_arr.scope loc = in_arr.loc parfor_vars = [] for i in range(ndims): parfor_var = ir.Var(scope, mk_unique_var( "$parfor_index_var"), loc) self.typemap[parfor_var.name] = types.intp parfor_vars.append(parfor_var) start_lengths, end_lengths = self._replace_stencil_accesses( stencil_blocks, parfor_vars, in_args, index_offsets, stencil_func, arg_to_arr_dict) if config.DEBUG_ARRAY_OPT == 1: print("stencil_blocks after replace stencil accesses") ir_utils.dump_blocks(stencil_blocks) # create parfor loop nests loopnests = [] equiv_set = self.array_analysis.get_equiv_set(label) in_arr_dim_sizes = equiv_set.get_shape(in_arr) assert ndims == len(in_arr_dim_sizes) for i in range(ndims): last_ind = self._get_stencil_last_ind(in_arr_dim_sizes[i], end_lengths[i], gen_nodes, scope, loc) start_ind = self._get_stencil_start_ind( start_lengths[i], gen_nodes, scope, loc) # start from stencil size to avoid invalid array access loopnests.append(numba.parfor.LoopNest(parfor_vars[i], start_ind, last_ind, 1)) # We have to guarantee that the exit block has maximum label and that # there's only one exit block for the parfor body. # So, all return statements will change to jump to the parfor exit block. parfor_body_exit_label = max(stencil_blocks.keys()) + 1 stencil_blocks[parfor_body_exit_label] = ir.Block(scope, loc) exit_value_var = ir.Var(scope, mk_unique_var("$parfor_exit_value"), loc) self.typemap[exit_value_var.name] = return_type.dtype # create parfor index var for_replacing_ret = [] if ndims == 1: parfor_ind_var = parfor_vars[0] else: parfor_ind_var = ir.Var(scope, mk_unique_var( "$parfor_index_tuple_var"), loc) self.typemap[parfor_ind_var.name] = types.containers.UniTuple( types.intp, ndims) tuple_call = ir.Expr.build_tuple(parfor_vars, loc) tuple_assign = ir.Assign(tuple_call, parfor_ind_var, loc) for_replacing_ret.append(tuple_assign) if config.DEBUG_ARRAY_OPT == 1: print("stencil_blocks after creating parfor index var") ir_utils.dump_blocks(stencil_blocks) # empty init block init_block = ir.Block(scope, loc) if out_arr == None: in_arr_typ = self.typemap[in_arr.name] shape_name = ir_utils.mk_unique_var("in_arr_shape") shape_var = ir.Var(scope, shape_name, loc) shape_getattr = ir.Expr.getattr(in_arr, "shape", loc) self.typemap[shape_name] = types.containers.UniTuple(types.intp, in_arr_typ.ndim) init_block.body.extend([ir.Assign(shape_getattr, shape_var, loc)]) zero_name = ir_utils.mk_unique_var("zero_val") zero_var = ir.Var(scope, zero_name, loc) if "cval" in stencil_func.options: cval = stencil_func.options["cval"] # TODO: Loosen this restriction to adhere to casting rules. if return_type.dtype != typing.typeof.typeof(cval): raise ValueError("cval type does not match stencil return type.") temp2 = return_type.dtype(cval) else: temp2 = return_type.dtype(0) full_const = ir.Const(temp2, loc) self.typemap[zero_name] = return_type.dtype init_block.body.extend([ir.Assign(full_const, zero_var, loc)]) so_name = ir_utils.mk_unique_var("stencil_output") out_arr = ir.Var(scope, so_name, loc) self.typemap[out_arr.name] = numba.types.npytypes.Array( return_type.dtype, in_arr_typ.ndim, in_arr_typ.layout) dtype_g_np_var = ir.Var(scope, mk_unique_var("$np_g_var"), loc) self.typemap[dtype_g_np_var.name] = types.misc.Module(np) dtype_g_np = ir.Global('np', np, loc) dtype_g_np_assign = ir.Assign(dtype_g_np, dtype_g_np_var, loc) init_block.body.append(dtype_g_np_assign) dtype_np_attr_call = ir.Expr.getattr(dtype_g_np_var, return_type.dtype.name, loc) dtype_attr_var = ir.Var(scope, mk_unique_var("$np_attr_attr"), loc) self.typemap[dtype_attr_var.name] = types.functions.NumberClass(return_type.dtype) dtype_attr_assign = ir.Assign(dtype_np_attr_call, dtype_attr_var, loc) init_block.body.append(dtype_attr_assign) stmts = ir_utils.gen_np_call("full", np.full, out_arr, [shape_var, zero_var, dtype_attr_var], self.typingctx, self.typemap, self.calltypes) equiv_set.insert_equiv(out_arr, in_arr_dim_sizes) init_block.body.extend(stmts) self.replace_return_with_setitem(stencil_blocks, exit_value_var, parfor_body_exit_label) if config.DEBUG_ARRAY_OPT == 1: print("stencil_blocks after replacing return") ir_utils.dump_blocks(stencil_blocks) setitem_call = ir.SetItem(out_arr, parfor_ind_var, exit_value_var, loc) self.calltypes[setitem_call] = signature( types.none, self.typemap[out_arr.name], self.typemap[parfor_ind_var.name], self.typemap[out_arr.name].dtype ) stencil_blocks[parfor_body_exit_label].body.extend(for_replacing_ret) stencil_blocks[parfor_body_exit_label].body.append(setitem_call) # simplify CFG of parfor body (exit block could be simplified often) # add dummy return to enable CFG stencil_blocks[parfor_body_exit_label].body.append(ir.Return(0, ir.Loc("stencilparfor_dummy", -1))) stencil_blocks = ir_utils.simplify_CFG(stencil_blocks) stencil_blocks[max(stencil_blocks.keys())].body.pop() if config.DEBUG_ARRAY_OPT == 1: print("stencil_blocks after adding SetItem") ir_utils.dump_blocks(stencil_blocks) pattern = ('stencil', [start_lengths, end_lengths]) parfor = numba.parfor.Parfor(loopnests, init_block, stencil_blocks, loc, parfor_ind_var, equiv_set, pattern, self.flags) gen_nodes.append(parfor) gen_nodes.append(ir.Assign(out_arr, target, loc)) return gen_nodes