def minmax(typesystem, args, op): if len(args) < 2: return res = args[0] for arg in args[1:]: lhs_type = get_type(res) rhs_type = get_type(arg) res_type = typesystem.promote(lhs_type, rhs_type) if lhs_type != res_type: res = nodes.CoercionNode(res, res_type) if rhs_type != res_type: arg = nodes.CoercionNode(arg, res_type) lhs_temp = nodes.TempNode(res_type) rhs_temp = nodes.TempNode(res_type) res_temp = nodes.TempNode(res_type) lhs = lhs_temp.load(invariant=True) rhs = rhs_temp.load(invariant=True) expr = ast.IfExp(ast.Compare(lhs, [op], [rhs]), lhs, rhs) body = [ ast.Assign([lhs_temp.store()], res), ast.Assign([rhs_temp.store()], arg), ast.Assign([res_temp.store()], expr), ] res = nodes.ExpressionNode(body, res_temp.load(invariant=True)) return res
def visit_ArrayNewNode(self, node): if self.nopython: raise error.NumbaError( node, "Cannot yet allocate new array in nopython context") PyArray_Type = nodes.ObjectInjectNode(np.ndarray) descr = nodes.ObjectInjectNode(node.type.dtype.get_dtype()).cloneable ndim = nodes.const(node.type.ndim, int_) flags = nodes.const(0, int_) args = [PyArray_Type, descr.clone, ndim, node.shape, node.strides, node.data, flags] incref_descr = nodes.IncrefNode(descr) incref_base = None setbase = None if node.base is None: args.append(nodes.NULL_obj) else: base = nodes.CloneableNode(node.base) incref_base = nodes.IncrefNode(base) args.append(base.clone) array = nodes.PyArray_NewFromDescr(args) array = nodes.ObjectTempNode(array).cloneable body = [incref_descr, incref_base, array, setbase] if node.base is not None: body.append(nodes.PyArray_SetBaseObject([array.clone, base.clone])) # TODO: PyArray_UpdateFlags() result = nodes.ExpressionNode(filter(None, body), array.clone) return self.visit(result)
def next(self, context, for_node, llvm_module): "Index element and update index" index = self.index.load value = nodes.CloneableNode(index(for_node.iter, index)) add = ast.BinOp(index, ast.Add(), nodes.const(1, Py_ssize_t)) return nodes.ExpressionNode( stmts=[value, assign(self.index.store, add)], expr=value.clone)
def visit_PyErr_OccurredNode(self, node): check_err = nodes.CheckErrorNode(nodes.ptrtoint( function_util.external_call(self.context, self.llvm_module, 'PyErr_Occurred')), goodval=nodes.ptrtoint(nodes.NULL)) result = nodes.CloneableNode(node.node) result = nodes.ExpressionNode(stmts=[result, check_err], expr=result.clone) return self.visit(result)
def visit_ArrayNewEmptyNode(self, node): if self.nopython: raise error.NumbaError( node, "Cannot yet allocate new empty array in nopython context") ndim = nodes.const(node.type.ndim, int_) dtype = nodes.const(node.type.dtype.get_dtype(), object_).cloneable is_fortran = nodes.const(node.is_fortran, int_) result = nodes.PyArray_Empty([ndim, node.shape, dtype, is_fortran]) result = nodes.ObjectTempNode(result) incref_descr = nodes.IncrefNode(dtype) return self.visit(nodes.ExpressionNode([incref_descr], result))
def visit_ListComp(self, node): """ Rewrite list comprehensions to the equivalent for loops. AST syntax: ListComp(expr elt, comprehension* generators) comprehension = (expr target, expr iter, expr* ifs) 'ifs' represent a chain of ANDs """ assert len(node.generators) > 0 # Create innermost body, i.e. list.append(expr) # TODO: size hint for PyList_New list_create = ast.List(elts=[], ctx=ast.Load()) list_create.type = typesystem.object_ # typesystem.list_() list_create = nodes.CloneableNode(list_create) list_value = nodes.CloneNode(list_create) list_append = ast.Attribute(list_value, "append", ast.Load()) append_call = ast.Call(func=list_append, args=[node.elt], keywords=[], starargs=None, kwargs=None) # Build up the loops from inwards to outwards body = append_call for comprehension in reversed(node.generators): # Hanlde the 'if' clause ifs = comprehension.ifs if len(ifs) > 1: make_boolop = lambda op1_op2: ast.BoolOp(op=ast.And(), values=op1_op2) if_test = reduce(make_boolop, ifs) elif len(ifs) == 1: if_test, = ifs else: if_test = None if if_test is not None: body = ast.If(test=if_test, body=[body], orelse=[]) # Wrap list.append() call or inner loops body = ast.For(target=comprehension.target, iter=comprehension.iter, body=[body], orelse=[]) expr = nodes.ExpressionNode(stmts=[list_create, body], expr=list_value) return self.visit(expr)
def visit_NativeCallNode(self, node): badval, goodval = node.badval, node.goodval if node not in self.visited_callnodes and (badval is not None or goodval is not None): self.visited_callnodes.add(node) result = node.cloneable body = nodes.CheckErrorNode(result, badval, goodval, node.exc_type, node.exc_msg, node.exc_args) node = nodes.ExpressionNode(stmts=[body], expr=result.clone) return self.visit(node) else: self.generic_visit(node) return node
def visit_InstantiateClosureScope(self, node): """ Instantiate a closure scope. After instantiation, assign the parent scope and all function arguments that belong in the scope to the scope. """ ctor = nodes.objconst(node.ext_type.__new__) ext_type_arg = nodes.objconst(node.ext_type) create_scope = nodes.ObjectCallNode(signature=node.scope_type(object_), func=ctor, args=[ext_type_arg]) create_scope = create_scope.cloneable scope = create_scope.clone stats = [create_scope] # Chain outer scope - if present - to current scope outer_scope = self.outer_scope if outer_scope: outer_scope_name, outer_scope_type = outer_scope_field(scope.type) dst = lookup_scope_attribute(scope, outer_scope_name, ctx=ast.Store()) assmt = ast.Assign(targets=[dst], value=outer_scope) stats.append(assmt) # Assign function arguments that are cellvars for arg in self.ast.args.args: name = arg.id if name in node.scope_type.unmangled_symtab: dst = lookup_scope_attribute(scope, name, ast.Store()) src = self._load_name(name) src.variable.assign_in_closure_scope = True assmt = ast.Assign(targets=[dst], value=src) stats.append(assmt) logger.debug("instantiating %s", scope.type) self.ast.cur_scope = scope return self.visit(nodes.ExpressionNode(stmts=stats, expr=scope))
def visit_ArrayNewNode(self, node): if self.nopython: # Give the codegen (subclass) a chance to handle this self.generic_visit(node) return node PyArray_Type = nodes.ObjectInjectNode(np.ndarray) descr = nodes.ObjectInjectNode(node.type.dtype.get_dtype()).cloneable ndim = nodes.const(node.type.ndim, int_) flags = nodes.const(0, int_) args = [ PyArray_Type, descr.clone, ndim, node.shape, node.strides, node.data, flags ] incref_descr = nodes.IncrefNode(descr) incref_base = None setbase = None if node.base is None: args.append(nodes.NULL_obj) else: base = nodes.CloneableNode(node.base) incref_base = nodes.IncrefNode(base) args.append(base.clone) array = nodes.PyArray_NewFromDescr(args) array = nodes.ObjectTempNode(array).cloneable body = [incref_descr, incref_base, array, setbase] if node.base is not None: body.append(nodes.PyArray_SetBaseObject([array.clone, base.clone])) # TODO: PyArray_UpdateFlags() result = nodes.ExpressionNode(filter(None, body), array.clone) return self.visit(result)
def register_array_expression(self, node, lhs=None): super(ArrayExpressionRewriteNative, self).register_array_expression( node, lhs) lhs_type = lhs.type if lhs else node.type is_expr = lhs is None if node.type.is_array and lhs_type.ndim < node.type.ndim: # TODO: this is valid in NumPy if the leading dimensions of the # TODO: RHS have extent 1 raise error.NumbaError( node, "Right hand side must have a " "dimensionality <= %d" % lhs_type.ndim) # Create ufunc scalar kernel ufunc_ast, signature, ufunc_builder = self.get_py_ufunc_ast(lhs, node) signature.struct_by_reference = True # Compile ufunc scalar kernel with numba ast.fix_missing_locations(ufunc_ast) func_env, (_, _, _) = pipeline.run_pipeline2( self.env, None, ufunc_ast, signature, function_globals={}, ) # Manual linking lfunc = func_env.lfunc # print lfunc operands = ufunc_builder.operands functions.keep_alive(self.func, lfunc) operands = [nodes.CloneableNode(operand) for operand in operands] if lhs is not None: lhs = nodes.CloneableNode(lhs) broadcast_operands = [lhs] + operands lhs = lhs.clone else: broadcast_operands = operands[:] shape = slicenodes.BroadcastNode(lhs_type, broadcast_operands) operands = [op.clone for op in operands] if lhs is None and self.nopython: raise error.NumbaError( node, "Cannot allocate new memory in nopython context") elif lhs is None: # TODO: determine best output order at runtime shape = shape.cloneable lhs = nodes.ArrayNewEmptyNode(lhs_type, shape.clone, lhs_type.is_f_contig).cloneable # Build minivect wrapper kernel context = NumbaproStaticArgsContext() context.llvm_module = self.env.llvm_context.module # context.debug = True context.optimize_broadcasting = False b = context.astbuilder variables = [b.variable(name_node.type, "op%d" % i) for i, name_node in enumerate([lhs] + operands)] miniargs = [b.funcarg(variable) for variable in variables] body = miniutils.build_kernel_call(lfunc.name, signature, miniargs, b) minikernel = b.function_from_numpy( templating.temp_name("array_expression"), body, miniargs) lminikernel, ctypes_kernel = context.run_simple( minikernel, specializers.StridedSpecializer) # Build call to minivect kernel operands.insert(0, lhs) args = [shape] scalar_args = [] for operand in operands: if operand.type.is_array: data_p = self.array_attr(operand, 'data') data_p = nodes.CoercionNode(data_p, operand.type.dtype.pointer()) if not isinstance(operand, nodes.CloneNode): operand = nodes.CloneNode(operand) strides_p = self.array_attr(operand, 'strides') args.extend((data_p, strides_p)) else: scalar_args.append(operand) args.extend(scalar_args) result = nodes.NativeCallNode(minikernel.type, args, lminikernel) # Use native slicing in array expressions slicenodes.mark_nopython(ast.Suite(body=result.args)) if not is_expr: # a[:] = b[:] * c[:] return result # b[:] * c[:], return new array as expression return nodes.ExpressionNode(stmts=[result], expr=lhs.clone)