1from collections import namedtuple 2 3import numpy as np 4 5from llvmlite.llvmpy.core import Type, Builder, ICMP_EQ, Constant 6 7from numba.core import types, cgutils 8from numba.core.compiler_lock import global_compiler_lock 9from numba.core.caching import make_library_cache, NullCache 10 11 12_wrapper_info = namedtuple('_wrapper_info', ['library', 'env', 'name']) 13 14 15def _build_ufunc_loop_body(load, store, context, func, builder, arrays, out, 16 offsets, store_offset, signature, pyapi, env): 17 elems = load() 18 19 # Compute 20 status, retval = context.call_conv.call_function(builder, func, 21 signature.return_type, 22 signature.args, elems) 23 24 # Store 25 with builder.if_else(status.is_ok, likely=True) as (if_ok, if_error): 26 with if_ok: 27 store(retval) 28 with if_error: 29 gil = pyapi.gil_ensure() 30 context.call_conv.raise_error(builder, pyapi, status) 31 pyapi.gil_release(gil) 32 33 # increment indices 34 for off, ary in zip(offsets, arrays): 35 builder.store(builder.add(builder.load(off), ary.step), off) 36 37 builder.store(builder.add(builder.load(store_offset), out.step), 38 store_offset) 39 40 return status.code 41 42 43def _build_ufunc_loop_body_objmode(load, store, context, func, builder, 44 arrays, out, offsets, store_offset, 45 signature, env, pyapi): 46 elems = load() 47 48 # Compute 49 _objargs = [types.pyobject] * len(signature.args) 50 # We need to push the error indicator to avoid it messing with 51 # the ufunc's execution. We restore it unless the ufunc raised 52 # a new error. 53 with pyapi.err_push(keep_new=True): 54 status, retval = context.call_conv.call_function(builder, func, 55 types.pyobject, 56 _objargs, elems) 57 # Release owned reference to arguments 58 for elem in elems: 59 pyapi.decref(elem) 60 # NOTE: if an error occurred, it will be caught by the Numpy machinery 61 62 # Store 63 store(retval) 64 65 # increment indices 66 for off, ary in zip(offsets, arrays): 67 builder.store(builder.add(builder.load(off), ary.step), off) 68 69 builder.store(builder.add(builder.load(store_offset), out.step), 70 store_offset) 71 72 return status.code 73 74 75def build_slow_loop_body(context, func, builder, arrays, out, offsets, 76 store_offset, signature, pyapi, env): 77 def load(): 78 elems = [ary.load_direct(builder.load(off)) 79 for off, ary in zip(offsets, arrays)] 80 return elems 81 82 def store(retval): 83 out.store_direct(retval, builder.load(store_offset)) 84 85 return _build_ufunc_loop_body(load, store, context, func, builder, arrays, 86 out, offsets, store_offset, signature, pyapi, 87 env=env) 88 89 90def build_obj_loop_body(context, func, builder, arrays, out, offsets, 91 store_offset, signature, pyapi, envptr, env): 92 env_body = context.get_env_body(builder, envptr) 93 env_manager = pyapi.get_env_manager(env, env_body, envptr) 94 95 def load(): 96 # Load 97 elems = [ary.load_direct(builder.load(off)) 98 for off, ary in zip(offsets, arrays)] 99 # Box 100 elems = [pyapi.from_native_value(t, v, env_manager) 101 for v, t in zip(elems, signature.args)] 102 return elems 103 104 def store(retval): 105 is_ok = cgutils.is_not_null(builder, retval) 106 # If an error is raised by the object mode ufunc, it will 107 # simply get caught by the Numpy ufunc machinery. 108 with builder.if_then(is_ok, likely=True): 109 # Unbox 110 native = pyapi.to_native_value(signature.return_type, retval) 111 assert native.cleanup is None 112 # Store 113 out.store_direct(native.value, builder.load(store_offset)) 114 # Release owned reference 115 pyapi.decref(retval) 116 117 return _build_ufunc_loop_body_objmode(load, store, context, func, builder, 118 arrays, out, offsets, store_offset, 119 signature, envptr, pyapi) 120 121 122def build_fast_loop_body(context, func, builder, arrays, out, offsets, 123 store_offset, signature, ind, pyapi, env): 124 def load(): 125 elems = [ary.load_aligned(ind) 126 for ary in arrays] 127 return elems 128 129 def store(retval): 130 out.store_aligned(retval, ind) 131 132 return _build_ufunc_loop_body(load, store, context, func, builder, arrays, 133 out, offsets, store_offset, signature, pyapi, 134 env=env) 135 136 137def build_ufunc_wrapper(library, context, fname, signature, objmode, cres): 138 """ 139 Wrap the scalar function with a loop that iterates over the arguments 140 141 Returns 142 ------- 143 (library, env, name) 144 """ 145 assert isinstance(fname, str) 146 byte_t = Type.int(8) 147 byte_ptr_t = Type.pointer(byte_t) 148 byte_ptr_ptr_t = Type.pointer(byte_ptr_t) 149 intp_t = context.get_value_type(types.intp) 150 intp_ptr_t = Type.pointer(intp_t) 151 152 fnty = Type.function(Type.void(), [byte_ptr_ptr_t, intp_ptr_t, 153 intp_ptr_t, byte_ptr_t]) 154 155 wrapperlib = context.codegen().create_library('ufunc_wrapper') 156 wrapper_module = wrapperlib.create_ir_module('') 157 if objmode: 158 func_type = context.call_conv.get_function_type( 159 types.pyobject, [types.pyobject] * len(signature.args)) 160 else: 161 func_type = context.call_conv.get_function_type( 162 signature.return_type, signature.args) 163 164 func = wrapper_module.add_function(func_type, name=fname) 165 func.attributes.add("alwaysinline") 166 167 wrapper = wrapper_module.add_function(fnty, "__ufunc__." + func.name) 168 arg_args, arg_dims, arg_steps, arg_data = wrapper.args 169 arg_args.name = "args" 170 arg_dims.name = "dims" 171 arg_steps.name = "steps" 172 arg_data.name = "data" 173 174 builder = Builder(wrapper.append_basic_block("entry")) 175 176 # Prepare Environment 177 envname = context.get_env_name(cres.fndesc) 178 env = cres.environment 179 envptr = builder.load(context.declare_env_global(builder.module, envname)) 180 181 # Emit loop 182 loopcount = builder.load(arg_dims, name="loopcount") 183 184 # Prepare inputs 185 arrays = [] 186 for i, typ in enumerate(signature.args): 187 arrays.append(UArrayArg(context, builder, arg_args, arg_steps, i, typ)) 188 189 # Prepare output 190 out = UArrayArg(context, builder, arg_args, arg_steps, len(arrays), 191 signature.return_type) 192 193 # Setup indices 194 offsets = [] 195 zero = context.get_constant(types.intp, 0) 196 for _ in arrays: 197 p = cgutils.alloca_once(builder, intp_t) 198 offsets.append(p) 199 builder.store(zero, p) 200 201 store_offset = cgutils.alloca_once(builder, intp_t) 202 builder.store(zero, store_offset) 203 204 unit_strided = cgutils.true_bit 205 for ary in arrays: 206 unit_strided = builder.and_(unit_strided, ary.is_unit_strided) 207 208 pyapi = context.get_python_api(builder) 209 if objmode: 210 # General loop 211 gil = pyapi.gil_ensure() 212 with cgutils.for_range(builder, loopcount, intp=intp_t): 213 build_obj_loop_body( 214 context, func, builder, arrays, out, offsets, 215 store_offset, signature, pyapi, envptr, env, 216 ) 217 pyapi.gil_release(gil) 218 builder.ret_void() 219 220 else: 221 with builder.if_else(unit_strided) as (is_unit_strided, is_strided): 222 with is_unit_strided: 223 with cgutils.for_range(builder, loopcount, intp=intp_t) as loop: 224 build_fast_loop_body( 225 context, func, builder, arrays, out, offsets, 226 store_offset, signature, loop.index, pyapi, 227 env=envptr, 228 ) 229 230 with is_strided: 231 # General loop 232 with cgutils.for_range(builder, loopcount, intp=intp_t): 233 build_slow_loop_body( 234 context, func, builder, arrays, out, offsets, 235 store_offset, signature, pyapi, 236 env=envptr, 237 ) 238 239 builder.ret_void() 240 del builder 241 242 # Link and finalize 243 wrapperlib.add_ir_module(wrapper_module) 244 wrapperlib.add_linking_library(library) 245 return _wrapper_info(library=wrapperlib, env=env, name=wrapper.name) 246 247 248class UArrayArg(object): 249 def __init__(self, context, builder, args, steps, i, fe_type): 250 self.context = context 251 self.builder = builder 252 self.fe_type = fe_type 253 offset = self.context.get_constant(types.intp, i) 254 offseted_args = self.builder.load(builder.gep(args, [offset])) 255 data_type = context.get_data_type(fe_type) 256 self.dataptr = self.builder.bitcast(offseted_args, 257 data_type.as_pointer()) 258 sizeof = self.context.get_abi_sizeof(data_type) 259 self.abisize = self.context.get_constant(types.intp, sizeof) 260 offseted_step = self.builder.gep(steps, [offset]) 261 self.step = self.builder.load(offseted_step) 262 self.is_unit_strided = builder.icmp(ICMP_EQ, self.abisize, self.step) 263 self.builder = builder 264 265 def load_direct(self, byteoffset): 266 """ 267 Generic load from the given *byteoffset*. load_aligned() is 268 preferred if possible. 269 """ 270 ptr = cgutils.pointer_add(self.builder, self.dataptr, byteoffset) 271 return self.context.unpack_value(self.builder, self.fe_type, ptr) 272 273 def load_aligned(self, ind): 274 # Using gep() instead of explicit pointer addition helps LLVM 275 # vectorize the loop. 276 ptr = self.builder.gep(self.dataptr, [ind]) 277 return self.context.unpack_value(self.builder, self.fe_type, ptr) 278 279 def store_direct(self, value, byteoffset): 280 ptr = cgutils.pointer_add(self.builder, self.dataptr, byteoffset) 281 self.context.pack_value(self.builder, self.fe_type, value, ptr) 282 283 def store_aligned(self, value, ind): 284 ptr = self.builder.gep(self.dataptr, [ind]) 285 self.context.pack_value(self.builder, self.fe_type, value, ptr) 286 287 288GufWrapperCache = make_library_cache('guf') 289 290 291class _GufuncWrapper(object): 292 def __init__(self, py_func, cres, sin, sout, cache, is_parfors): 293 """ 294 The *is_parfors* argument is a boolean that indicates if the GUfunc 295 being built is to be used as a ParFors kernel. If True, it disables 296 the caching on the wrapper as a separate unit because it will be linked 297 into the caller function and cached along with it. 298 """ 299 self.py_func = py_func 300 self.cres = cres 301 self.sin = sin 302 self.sout = sout 303 self.is_objectmode = self.signature.return_type == types.pyobject 304 self.cache = (GufWrapperCache(py_func=self.py_func) 305 if cache else NullCache()) 306 self.is_parfors = bool(is_parfors) 307 308 @property 309 def library(self): 310 return self.cres.library 311 312 @property 313 def context(self): 314 return self.cres.target_context 315 316 @property 317 def call_conv(self): 318 return self.context.call_conv 319 320 @property 321 def signature(self): 322 return self.cres.signature 323 324 @property 325 def fndesc(self): 326 return self.cres.fndesc 327 328 @property 329 def env(self): 330 return self.cres.environment 331 332 def _wrapper_function_type(self): 333 byte_t = Type.int(8) 334 byte_ptr_t = Type.pointer(byte_t) 335 byte_ptr_ptr_t = Type.pointer(byte_ptr_t) 336 intp_t = self.context.get_value_type(types.intp) 337 intp_ptr_t = Type.pointer(intp_t) 338 339 fnty = Type.function(Type.void(), [byte_ptr_ptr_t, intp_ptr_t, 340 intp_ptr_t, byte_ptr_t]) 341 return fnty 342 343 def _build_wrapper(self, library, name): 344 """ 345 The LLVM IRBuilder code to create the gufunc wrapper. 346 The *library* arg is the CodeLibrary to which the wrapper should 347 be added. The *name* arg is the name of the wrapper function being 348 created. 349 """ 350 intp_t = self.context.get_value_type(types.intp) 351 fnty = self._wrapper_function_type() 352 353 wrapper_module = library.create_ir_module('_gufunc_wrapper') 354 func_type = self.call_conv.get_function_type(self.fndesc.restype, 355 self.fndesc.argtypes) 356 fname = self.fndesc.llvm_func_name 357 func = wrapper_module.add_function(func_type, name=fname) 358 359 func.attributes.add("alwaysinline") 360 wrapper = wrapper_module.add_function(fnty, name) 361 # The use of weak_odr linkage avoids the function being dropped due 362 # to the order in which the wrappers and the user function are linked. 363 wrapper.linkage = 'weak_odr' 364 arg_args, arg_dims, arg_steps, arg_data = wrapper.args 365 arg_args.name = "args" 366 arg_dims.name = "dims" 367 arg_steps.name = "steps" 368 arg_data.name = "data" 369 370 builder = Builder(wrapper.append_basic_block("entry")) 371 loopcount = builder.load(arg_dims, name="loopcount") 372 pyapi = self.context.get_python_api(builder) 373 374 # Unpack shapes 375 unique_syms = set() 376 for grp in (self.sin, self.sout): 377 for syms in grp: 378 unique_syms |= set(syms) 379 380 sym_map = {} 381 for syms in self.sin: 382 for s in syms: 383 if s not in sym_map: 384 sym_map[s] = len(sym_map) 385 386 sym_dim = {} 387 for s, i in sym_map.items(): 388 sym_dim[s] = builder.load(builder.gep(arg_dims, 389 [self.context.get_constant( 390 types.intp, 391 i + 1)])) 392 393 # Prepare inputs 394 arrays = [] 395 step_offset = len(self.sin) + len(self.sout) 396 for i, (typ, sym) in enumerate(zip(self.signature.args, 397 self.sin + self.sout)): 398 ary = GUArrayArg(self.context, builder, arg_args, 399 arg_steps, i, step_offset, typ, sym, sym_dim) 400 step_offset += len(sym) 401 arrays.append(ary) 402 403 bbreturn = builder.append_basic_block('.return') 404 405 # Prologue 406 self.gen_prologue(builder, pyapi) 407 408 # Loop 409 with cgutils.for_range(builder, loopcount, intp=intp_t) as loop: 410 args = [a.get_array_at_offset(loop.index) for a in arrays] 411 innercall, error = self.gen_loop_body(builder, pyapi, func, args) 412 # If error, escape 413 cgutils.cbranch_or_continue(builder, error, bbreturn) 414 415 builder.branch(bbreturn) 416 builder.position_at_end(bbreturn) 417 418 # Epilogue 419 self.gen_epilogue(builder, pyapi) 420 421 builder.ret_void() 422 423 # Link 424 library.add_ir_module(wrapper_module) 425 library.add_linking_library(self.library) 426 427 def _compile_wrapper(self, wrapper_name): 428 # Gufunc created by Parfors? 429 if self.is_parfors: 430 # No wrapper caching for parfors 431 wrapperlib = self.context.codegen().create_library(str(self)) 432 # Build wrapper 433 self._build_wrapper(wrapperlib, wrapper_name) 434 # Non-parfors? 435 else: 436 # Use cache and compiler in a critical section 437 wrapperlib = self.cache.load_overload( 438 self.cres.signature, self.cres.target_context, 439 ) 440 if wrapperlib is None: 441 # Create library and enable caching 442 wrapperlib = self.context.codegen().create_library(str(self)) 443 wrapperlib.enable_object_caching() 444 # Build wrapper 445 self._build_wrapper(wrapperlib, wrapper_name) 446 # Cache 447 self.cache.save_overload(self.cres.signature, wrapperlib) 448 449 return wrapperlib 450 451 @global_compiler_lock 452 def build(self): 453 wrapper_name = "__gufunc__." + self.fndesc.mangled_name 454 wrapperlib = self._compile_wrapper(wrapper_name) 455 return _wrapper_info( 456 library=wrapperlib, env=self.env, name=wrapper_name, 457 ) 458 459 def gen_loop_body(self, builder, pyapi, func, args): 460 status, retval = self.call_conv.call_function( 461 builder, func, self.signature.return_type, self.signature.args, 462 args) 463 464 with builder.if_then(status.is_error, likely=False): 465 gil = pyapi.gil_ensure() 466 self.context.call_conv.raise_error(builder, pyapi, status) 467 pyapi.gil_release(gil) 468 469 return status.code, status.is_error 470 471 def gen_prologue(self, builder, pyapi): 472 pass # Do nothing 473 474 def gen_epilogue(self, builder, pyapi): 475 pass # Do nothing 476 477 478class _GufuncObjectWrapper(_GufuncWrapper): 479 def gen_loop_body(self, builder, pyapi, func, args): 480 innercall, error = _prepare_call_to_object_mode(self.context, 481 builder, pyapi, func, 482 self.signature, 483 args) 484 return innercall, error 485 486 def gen_prologue(self, builder, pyapi): 487 # Acquire the GIL 488 self.gil = pyapi.gil_ensure() 489 490 def gen_epilogue(self, builder, pyapi): 491 # Release GIL 492 pyapi.gil_release(self.gil) 493 494 495def build_gufunc_wrapper(py_func, cres, sin, sout, cache, is_parfors): 496 signature = cres.signature 497 wrapcls = (_GufuncObjectWrapper 498 if signature.return_type == types.pyobject 499 else _GufuncWrapper) 500 return wrapcls( 501 py_func, cres, sin, sout, cache, is_parfors=is_parfors, 502 ).build() 503 504 505def _prepare_call_to_object_mode(context, builder, pyapi, func, 506 signature, args): 507 mod = builder.module 508 509 bb_core_return = builder.append_basic_block('ufunc.core.return') 510 511 # Call to 512 # PyObject* ndarray_new(int nd, 513 # npy_intp *dims, /* shape */ 514 # npy_intp *strides, 515 # void* data, 516 # int type_num, 517 # int itemsize) 518 519 ll_int = context.get_value_type(types.int32) 520 ll_intp = context.get_value_type(types.intp) 521 ll_intp_ptr = Type.pointer(ll_intp) 522 ll_voidptr = context.get_value_type(types.voidptr) 523 ll_pyobj = context.get_value_type(types.pyobject) 524 fnty = Type.function(ll_pyobj, [ll_int, ll_intp_ptr, 525 ll_intp_ptr, ll_voidptr, 526 ll_int, ll_int]) 527 528 fn_array_new = mod.get_or_insert_function(fnty, name="numba_ndarray_new") 529 530 # Convert each llarray into pyobject 531 error_pointer = cgutils.alloca_once(builder, Type.int(1), name='error') 532 builder.store(cgutils.true_bit, error_pointer) 533 534 # The PyObject* arguments to the kernel function 535 object_args = [] 536 object_pointers = [] 537 538 for i, (arg, argty) in enumerate(zip(args, signature.args)): 539 # Allocate NULL-initialized slot for this argument 540 objptr = cgutils.alloca_once(builder, ll_pyobj, zfill=True) 541 object_pointers.append(objptr) 542 543 if isinstance(argty, types.Array): 544 # Special case arrays: we don't need full-blown NRT reflection 545 # since the argument will be gone at the end of the kernel 546 arycls = context.make_array(argty) 547 array = arycls(context, builder, value=arg) 548 549 zero = Constant.int(ll_int, 0) 550 551 # Extract members of the llarray 552 nd = Constant.int(ll_int, argty.ndim) 553 dims = builder.gep(array._get_ptr_by_name('shape'), [zero, zero]) 554 strides = builder.gep(array._get_ptr_by_name('strides'), 555 [zero, zero]) 556 data = builder.bitcast(array.data, ll_voidptr) 557 dtype = np.dtype(str(argty.dtype)) 558 559 # Prepare other info for reconstruction of the PyArray 560 type_num = Constant.int(ll_int, dtype.num) 561 itemsize = Constant.int(ll_int, dtype.itemsize) 562 563 # Call helper to reconstruct PyArray objects 564 obj = builder.call(fn_array_new, [nd, dims, strides, data, 565 type_num, itemsize]) 566 else: 567 # Other argument types => use generic boxing 568 obj = pyapi.from_native_value(argty, arg) 569 570 builder.store(obj, objptr) 571 object_args.append(obj) 572 573 obj_is_null = cgutils.is_null(builder, obj) 574 builder.store(obj_is_null, error_pointer) 575 cgutils.cbranch_or_continue(builder, obj_is_null, bb_core_return) 576 577 # Call ufunc core function 578 object_sig = [types.pyobject] * len(object_args) 579 580 status, retval = context.call_conv.call_function( 581 builder, func, types.pyobject, object_sig, 582 object_args) 583 builder.store(status.is_error, error_pointer) 584 585 # Release returned object 586 pyapi.decref(retval) 587 588 builder.branch(bb_core_return) 589 # At return block 590 builder.position_at_end(bb_core_return) 591 592 # Release argument objects 593 for objptr in object_pointers: 594 pyapi.decref(builder.load(objptr)) 595 596 innercall = status.code 597 return innercall, builder.load(error_pointer) 598 599 600class GUArrayArg(object): 601 def __init__(self, context, builder, args, steps, i, step_offset, 602 typ, syms, sym_dim): 603 604 self.context = context 605 self.builder = builder 606 607 offset = context.get_constant(types.intp, i) 608 609 data = builder.load(builder.gep(args, [offset], name="data.ptr"), 610 name="data") 611 self.data = data 612 613 core_step_ptr = builder.gep(steps, [offset], name="core.step.ptr") 614 core_step = builder.load(core_step_ptr) 615 616 if isinstance(typ, types.Array): 617 as_scalar = not syms 618 619 # number of symbol in the shape spec should match the dimension 620 # of the array type. 621 if len(syms) != typ.ndim: 622 if len(syms) == 0 and typ.ndim == 1: 623 # This is an exception for handling scalar argument. 624 # The type can be 1D array for scalar. 625 # In the future, we may deprecate this exception. 626 pass 627 else: 628 raise TypeError("type and shape signature mismatch for arg " 629 "#{0}".format(i + 1)) 630 631 ndim = typ.ndim 632 shape = [sym_dim[s] for s in syms] 633 strides = [] 634 635 for j in range(ndim): 636 stepptr = builder.gep(steps, 637 [context.get_constant(types.intp, 638 step_offset + j)], 639 name="step.ptr") 640 step = builder.load(stepptr) 641 strides.append(step) 642 643 ldcls = (_ArrayAsScalarArgLoader 644 if as_scalar 645 else _ArrayArgLoader) 646 647 self._loader = ldcls(dtype=typ.dtype, 648 ndim=ndim, 649 core_step=core_step, 650 as_scalar=as_scalar, 651 shape=shape, 652 strides=strides) 653 else: 654 # If typ is not an array 655 if syms: 656 raise TypeError("scalar type {0} given for non scalar " 657 "argument #{1}".format(typ, i + 1)) 658 self._loader = _ScalarArgLoader(dtype=typ, stride=core_step) 659 660 def get_array_at_offset(self, ind): 661 return self._loader.load(context=self.context, builder=self.builder, 662 data=self.data, ind=ind) 663 664 665class _ScalarArgLoader(object): 666 """ 667 Handle GFunc argument loading where a scalar type is used in the core 668 function. 669 Note: It still has a stride because the input to the gufunc can be an array 670 for this argument. 671 """ 672 673 def __init__(self, dtype, stride): 674 self.dtype = dtype 675 self.stride = stride 676 677 def load(self, context, builder, data, ind): 678 # Load at base + ind * stride 679 data = builder.gep(data, [builder.mul(ind, self.stride)]) 680 dptr = builder.bitcast(data, 681 context.get_data_type(self.dtype).as_pointer()) 682 return builder.load(dptr) 683 684 685class _ArrayArgLoader(object): 686 """ 687 Handle GUFunc argument loading where an array is expected. 688 """ 689 690 def __init__(self, dtype, ndim, core_step, as_scalar, shape, strides): 691 self.dtype = dtype 692 self.ndim = ndim 693 self.core_step = core_step 694 self.as_scalar = as_scalar 695 self.shape = shape 696 self.strides = strides 697 698 def load(self, context, builder, data, ind): 699 arytyp = types.Array(dtype=self.dtype, ndim=self.ndim, layout="A") 700 arycls = context.make_array(arytyp) 701 702 array = arycls(context, builder) 703 offseted_data = cgutils.pointer_add(builder, 704 data, 705 builder.mul(self.core_step, 706 ind)) 707 708 shape, strides = self._shape_and_strides(context, builder) 709 710 itemsize = context.get_abi_sizeof(context.get_data_type(self.dtype)) 711 context.populate_array(array, 712 data=builder.bitcast(offseted_data, 713 array.data.type), 714 shape=shape, 715 strides=strides, 716 itemsize=context.get_constant(types.intp, 717 itemsize), 718 meminfo=None) 719 720 return array._getvalue() 721 722 def _shape_and_strides(self, context, builder): 723 shape = cgutils.pack_array(builder, self.shape) 724 strides = cgutils.pack_array(builder, self.strides) 725 return shape, strides 726 727 728class _ArrayAsScalarArgLoader(_ArrayArgLoader): 729 """ 730 Handle GUFunc argument loading where the shape signature specifies 731 a scalar "()" but a 1D array is used for the type of the core function. 732 """ 733 734 def _shape_and_strides(self, context, builder): 735 # Set shape and strides for a 1D size 1 array 736 one = context.get_constant(types.intp, 1) 737 zero = context.get_constant(types.intp, 0) 738 shape = cgutils.pack_array(builder, [one]) 739 strides = cgutils.pack_array(builder, [zero]) 740 return shape, strides 741