numba-cuda 0.17.0__py3-none-any.whl → 0.18.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of numba-cuda might be problematic. Click here for more details.
- numba_cuda/VERSION +1 -1
- numba_cuda/numba/cuda/__init__.py +0 -8
- numba_cuda/numba/cuda/_internal/cuda_fp16.py +14225 -0
- numba_cuda/numba/cuda/api_util.py +6 -0
- numba_cuda/numba/cuda/cgutils.py +1291 -0
- numba_cuda/numba/cuda/codegen.py +32 -14
- numba_cuda/numba/cuda/compiler.py +113 -10
- numba_cuda/numba/cuda/core/caching.py +741 -0
- numba_cuda/numba/cuda/core/callconv.py +338 -0
- numba_cuda/numba/cuda/core/codegen.py +168 -0
- numba_cuda/numba/cuda/core/compiler.py +205 -0
- numba_cuda/numba/cuda/core/typed_passes.py +139 -0
- numba_cuda/numba/cuda/cudadecl.py +0 -268
- numba_cuda/numba/cuda/cudadrv/devicearray.py +3 -0
- numba_cuda/numba/cuda/cudadrv/driver.py +2 -1
- numba_cuda/numba/cuda/cudadrv/nvvm.py +1 -1
- numba_cuda/numba/cuda/cudaimpl.py +4 -178
- numba_cuda/numba/cuda/debuginfo.py +469 -3
- numba_cuda/numba/cuda/device_init.py +0 -1
- numba_cuda/numba/cuda/dispatcher.py +310 -11
- numba_cuda/numba/cuda/extending.py +2 -1
- numba_cuda/numba/cuda/fp16.py +348 -0
- numba_cuda/numba/cuda/intrinsics.py +1 -1
- numba_cuda/numba/cuda/libdeviceimpl.py +2 -1
- numba_cuda/numba/cuda/lowering.py +1833 -8
- numba_cuda/numba/cuda/mathimpl.py +2 -90
- numba_cuda/numba/cuda/nvvmutils.py +2 -1
- numba_cuda/numba/cuda/printimpl.py +2 -1
- numba_cuda/numba/cuda/serialize.py +264 -0
- numba_cuda/numba/cuda/simulator/__init__.py +2 -0
- numba_cuda/numba/cuda/simulator/dispatcher.py +7 -0
- numba_cuda/numba/cuda/stubs.py +0 -308
- numba_cuda/numba/cuda/target.py +13 -5
- numba_cuda/numba/cuda/testing.py +156 -5
- numba_cuda/numba/cuda/tests/complex_usecases.py +113 -0
- numba_cuda/numba/cuda/tests/core/serialize_usecases.py +110 -0
- numba_cuda/numba/cuda/tests/core/test_serialize.py +359 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_context_stack.py +10 -4
- numba_cuda/numba/cuda/tests/cudadrv/test_cuda_ndarray.py +33 -0
- numba_cuda/numba/cuda/tests/cudadrv/test_runtime.py +2 -2
- numba_cuda/numba/cuda/tests/cudadrv/test_streams.py +1 -0
- numba_cuda/numba/cuda/tests/cudapy/extensions_usecases.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_caching.py +5 -10
- numba_cuda/numba/cuda/tests/cudapy/test_compiler.py +15 -0
- numba_cuda/numba/cuda/tests/cudapy/test_complex.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_debuginfo.py +381 -0
- numba_cuda/numba/cuda/tests/cudapy/test_enums.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_extending.py +1 -1
- numba_cuda/numba/cuda/tests/cudapy/test_inspect.py +108 -24
- numba_cuda/numba/cuda/tests/cudapy/test_intrinsics.py +37 -23
- numba_cuda/numba/cuda/tests/cudapy/test_operator.py +43 -27
- numba_cuda/numba/cuda/tests/cudapy/test_ufuncs.py +26 -9
- numba_cuda/numba/cuda/tests/cudapy/test_warning.py +27 -2
- numba_cuda/numba/cuda/tests/enum_usecases.py +56 -0
- numba_cuda/numba/cuda/tests/nocuda/test_library_lookup.py +1 -2
- numba_cuda/numba/cuda/tests/nocuda/test_nvvm.py +1 -1
- numba_cuda/numba/cuda/utils.py +785 -0
- numba_cuda/numba/cuda/vector_types.py +1 -1
- {numba_cuda-0.17.0.dist-info → numba_cuda-0.18.1.dist-info}/METADATA +18 -4
- {numba_cuda-0.17.0.dist-info → numba_cuda-0.18.1.dist-info}/RECORD +63 -50
- numba_cuda/numba/cuda/cpp_function_wrappers.cu +0 -46
- {numba_cuda-0.17.0.dist-info → numba_cuda-0.18.1.dist-info}/WHEEL +0 -0
- {numba_cuda-0.17.0.dist-info → numba_cuda-0.18.1.dist-info}/licenses/LICENSE +0 -0
- {numba_cuda-0.17.0.dist-info → numba_cuda-0.18.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
from numba.core import types
|
|
2
|
+
from numba.cuda import cgutils
|
|
3
|
+
from collections import namedtuple
|
|
4
|
+
|
|
5
|
+
from llvmlite import ir
|
|
6
|
+
|
|
7
|
+
int32_t = ir.IntType(32)
|
|
8
|
+
int64_t = ir.IntType(64)
|
|
9
|
+
errcode_t = int32_t
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
Status = namedtuple(
|
|
13
|
+
"Status",
|
|
14
|
+
(
|
|
15
|
+
"code",
|
|
16
|
+
# If the function returned ok (a value or None)
|
|
17
|
+
"is_ok",
|
|
18
|
+
# If the function returned None
|
|
19
|
+
"is_none",
|
|
20
|
+
# If the function errored out (== not is_ok)
|
|
21
|
+
"is_error",
|
|
22
|
+
# If the generator exited with StopIteration
|
|
23
|
+
"is_stop_iteration",
|
|
24
|
+
# If the function errored with an already set exception
|
|
25
|
+
"is_python_exc",
|
|
26
|
+
# If the function errored with a user exception
|
|
27
|
+
"is_user_exc",
|
|
28
|
+
# The pointer to the exception info structure (for user
|
|
29
|
+
# exceptions)
|
|
30
|
+
"excinfoptr",
|
|
31
|
+
),
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _const_int(code):
|
|
36
|
+
return ir.Constant(errcode_t, code)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
RETCODE_OK = _const_int(0)
|
|
40
|
+
RETCODE_EXC = _const_int(-1)
|
|
41
|
+
RETCODE_NONE = _const_int(-2)
|
|
42
|
+
# StopIteration
|
|
43
|
+
RETCODE_STOPIT = _const_int(-3)
|
|
44
|
+
|
|
45
|
+
FIRST_USEREXC = 1
|
|
46
|
+
|
|
47
|
+
RETCODE_USEREXC = _const_int(FIRST_USEREXC)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class BaseCallConv(object):
|
|
51
|
+
def __init__(self, context):
|
|
52
|
+
self.context = context
|
|
53
|
+
|
|
54
|
+
def return_optional_value(self, builder, retty, valty, value):
|
|
55
|
+
if valty == types.none:
|
|
56
|
+
# Value is none
|
|
57
|
+
self.return_native_none(builder)
|
|
58
|
+
|
|
59
|
+
elif retty == valty:
|
|
60
|
+
# Value is an optional, need a runtime switch
|
|
61
|
+
optval = self.context.make_helper(builder, retty, value=value)
|
|
62
|
+
|
|
63
|
+
validbit = cgutils.as_bool_bit(builder, optval.valid)
|
|
64
|
+
with builder.if_then(validbit):
|
|
65
|
+
retval = self.context.get_return_value(
|
|
66
|
+
builder, retty.type, optval.data
|
|
67
|
+
)
|
|
68
|
+
self.return_value(builder, retval)
|
|
69
|
+
|
|
70
|
+
self.return_native_none(builder)
|
|
71
|
+
|
|
72
|
+
elif not isinstance(valty, types.Optional):
|
|
73
|
+
# Value is not an optional, need a cast
|
|
74
|
+
if valty != retty.type:
|
|
75
|
+
value = self.context.cast(
|
|
76
|
+
builder, value, fromty=valty, toty=retty.type
|
|
77
|
+
)
|
|
78
|
+
retval = self.context.get_return_value(builder, retty.type, value)
|
|
79
|
+
self.return_value(builder, retval)
|
|
80
|
+
|
|
81
|
+
else:
|
|
82
|
+
raise NotImplementedError(
|
|
83
|
+
"returning {0} for {1}".format(valty, retty)
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
def return_native_none(self, builder):
|
|
87
|
+
self._return_errcode_raw(builder, RETCODE_NONE)
|
|
88
|
+
|
|
89
|
+
def return_exc(self, builder):
|
|
90
|
+
self._return_errcode_raw(builder, RETCODE_EXC)
|
|
91
|
+
|
|
92
|
+
def return_stop_iteration(self, builder):
|
|
93
|
+
self._return_errcode_raw(builder, RETCODE_STOPIT)
|
|
94
|
+
|
|
95
|
+
def get_return_type(self, ty):
|
|
96
|
+
"""
|
|
97
|
+
Get the actual type of the return argument for Numba type *ty*.
|
|
98
|
+
"""
|
|
99
|
+
restype = self.context.data_model_manager[ty].get_return_type()
|
|
100
|
+
return restype.as_pointer()
|
|
101
|
+
|
|
102
|
+
def init_call_helper(self, builder):
|
|
103
|
+
"""
|
|
104
|
+
Initialize and return a call helper object for the given builder.
|
|
105
|
+
"""
|
|
106
|
+
ch = self._make_call_helper(builder)
|
|
107
|
+
builder.__call_helper = ch
|
|
108
|
+
return ch
|
|
109
|
+
|
|
110
|
+
def _get_call_helper(self, builder):
|
|
111
|
+
return builder.__call_helper
|
|
112
|
+
|
|
113
|
+
def unpack_exception(self, builder, pyapi, status):
|
|
114
|
+
return pyapi.unserialize(status.excinfoptr)
|
|
115
|
+
|
|
116
|
+
def raise_error(self, builder, pyapi, status):
|
|
117
|
+
"""
|
|
118
|
+
Given a non-ok *status*, raise the corresponding Python exception.
|
|
119
|
+
"""
|
|
120
|
+
bbend = builder.function.append_basic_block()
|
|
121
|
+
|
|
122
|
+
with builder.if_then(status.is_user_exc):
|
|
123
|
+
# Unserialize user exception.
|
|
124
|
+
# Make sure another error may not interfere.
|
|
125
|
+
pyapi.err_clear()
|
|
126
|
+
exc = self.unpack_exception(builder, pyapi, status)
|
|
127
|
+
with cgutils.if_likely(builder, cgutils.is_not_null(builder, exc)):
|
|
128
|
+
pyapi.raise_object(exc) # steals ref
|
|
129
|
+
builder.branch(bbend)
|
|
130
|
+
|
|
131
|
+
with builder.if_then(status.is_stop_iteration):
|
|
132
|
+
pyapi.err_set_none("PyExc_StopIteration")
|
|
133
|
+
builder.branch(bbend)
|
|
134
|
+
|
|
135
|
+
with builder.if_then(status.is_python_exc):
|
|
136
|
+
# Error already raised => nothing to do
|
|
137
|
+
builder.branch(bbend)
|
|
138
|
+
|
|
139
|
+
pyapi.err_set_string(
|
|
140
|
+
"PyExc_SystemError", "unknown error when calling native function"
|
|
141
|
+
)
|
|
142
|
+
builder.branch(bbend)
|
|
143
|
+
|
|
144
|
+
builder.position_at_end(bbend)
|
|
145
|
+
|
|
146
|
+
def decode_arguments(self, builder, argtypes, func):
|
|
147
|
+
"""
|
|
148
|
+
Get the decoded (unpacked) Python arguments with *argtypes*
|
|
149
|
+
from LLVM function *func*. A tuple of LLVM values is returned.
|
|
150
|
+
"""
|
|
151
|
+
raw_args = self.get_arguments(func)
|
|
152
|
+
arginfo = self._get_arg_packer(argtypes)
|
|
153
|
+
return arginfo.from_arguments(builder, raw_args)
|
|
154
|
+
|
|
155
|
+
def _get_arg_packer(self, argtypes):
|
|
156
|
+
"""
|
|
157
|
+
Get an argument packer for the given argument types.
|
|
158
|
+
"""
|
|
159
|
+
return self.context.get_arg_packer(argtypes)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class MinimalCallConv(BaseCallConv):
|
|
163
|
+
"""
|
|
164
|
+
A minimal calling convention, suitable for e.g. GPU targets.
|
|
165
|
+
The implemented function signature is:
|
|
166
|
+
|
|
167
|
+
retcode_t (<Python return type>*, ... <Python arguments>)
|
|
168
|
+
|
|
169
|
+
The return code will be one of the RETCODE_* constants or a
|
|
170
|
+
function-specific user exception id (>= RETCODE_USEREXC).
|
|
171
|
+
|
|
172
|
+
Caller is responsible for allocating a slot for the return value
|
|
173
|
+
(passed as a pointer in the first argument).
|
|
174
|
+
"""
|
|
175
|
+
|
|
176
|
+
def _make_call_helper(self, builder):
|
|
177
|
+
return _MinimalCallHelper()
|
|
178
|
+
|
|
179
|
+
def return_value(self, builder, retval):
|
|
180
|
+
retptr = builder.function.args[0]
|
|
181
|
+
assert retval.type == retptr.type.pointee, (
|
|
182
|
+
str(retval.type),
|
|
183
|
+
str(retptr.type.pointee),
|
|
184
|
+
)
|
|
185
|
+
builder.store(retval, retptr)
|
|
186
|
+
self._return_errcode_raw(builder, RETCODE_OK)
|
|
187
|
+
|
|
188
|
+
def return_user_exc(
|
|
189
|
+
self, builder, exc, exc_args=None, loc=None, func_name=None
|
|
190
|
+
):
|
|
191
|
+
if exc is not None and not issubclass(exc, BaseException):
|
|
192
|
+
raise TypeError(
|
|
193
|
+
"exc should be None or exception class, got %r" % (exc,)
|
|
194
|
+
)
|
|
195
|
+
if exc_args is not None and not isinstance(exc_args, tuple):
|
|
196
|
+
raise TypeError(
|
|
197
|
+
"exc_args should be None or tuple, got %r" % (exc_args,)
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# Build excinfo struct
|
|
201
|
+
if loc is not None:
|
|
202
|
+
fname = loc._raw_function_name()
|
|
203
|
+
if fname is None:
|
|
204
|
+
# could be exec(<string>) or REPL, try func_name
|
|
205
|
+
fname = func_name
|
|
206
|
+
|
|
207
|
+
locinfo = (fname, loc.filename, loc.line)
|
|
208
|
+
if None in locinfo:
|
|
209
|
+
locinfo = None
|
|
210
|
+
else:
|
|
211
|
+
locinfo = None
|
|
212
|
+
|
|
213
|
+
call_helper = self._get_call_helper(builder)
|
|
214
|
+
exc_id = call_helper._add_exception(exc, exc_args, locinfo)
|
|
215
|
+
self._return_errcode_raw(builder, _const_int(exc_id))
|
|
216
|
+
|
|
217
|
+
def return_status_propagate(self, builder, status):
|
|
218
|
+
self._return_errcode_raw(builder, status.code)
|
|
219
|
+
|
|
220
|
+
def _return_errcode_raw(self, builder, code):
|
|
221
|
+
if isinstance(code, int):
|
|
222
|
+
code = _const_int(code)
|
|
223
|
+
builder.ret(code)
|
|
224
|
+
|
|
225
|
+
def _get_return_status(self, builder, code):
|
|
226
|
+
"""
|
|
227
|
+
Given a return *code*, get a Status instance.
|
|
228
|
+
"""
|
|
229
|
+
norm = builder.icmp_signed("==", code, RETCODE_OK)
|
|
230
|
+
none = builder.icmp_signed("==", code, RETCODE_NONE)
|
|
231
|
+
ok = builder.or_(norm, none)
|
|
232
|
+
err = builder.not_(ok)
|
|
233
|
+
exc = builder.icmp_signed("==", code, RETCODE_EXC)
|
|
234
|
+
is_stop_iteration = builder.icmp_signed("==", code, RETCODE_STOPIT)
|
|
235
|
+
is_user_exc = builder.icmp_signed(">=", code, RETCODE_USEREXC)
|
|
236
|
+
|
|
237
|
+
status = Status(
|
|
238
|
+
code=code,
|
|
239
|
+
is_ok=ok,
|
|
240
|
+
is_error=err,
|
|
241
|
+
is_python_exc=exc,
|
|
242
|
+
is_none=none,
|
|
243
|
+
is_user_exc=is_user_exc,
|
|
244
|
+
is_stop_iteration=is_stop_iteration,
|
|
245
|
+
excinfoptr=None,
|
|
246
|
+
)
|
|
247
|
+
return status
|
|
248
|
+
|
|
249
|
+
def get_function_type(self, restype, argtypes):
|
|
250
|
+
"""
|
|
251
|
+
Get the implemented Function type for *restype* and *argtypes*.
|
|
252
|
+
"""
|
|
253
|
+
arginfo = self._get_arg_packer(argtypes)
|
|
254
|
+
argtypes = list(arginfo.argument_types)
|
|
255
|
+
resptr = self.get_return_type(restype)
|
|
256
|
+
fnty = ir.FunctionType(errcode_t, [resptr] + argtypes)
|
|
257
|
+
return fnty
|
|
258
|
+
|
|
259
|
+
def decorate_function(self, fn, args, fe_argtypes, noalias=False):
|
|
260
|
+
"""
|
|
261
|
+
Set names and attributes of function arguments.
|
|
262
|
+
"""
|
|
263
|
+
assert not noalias
|
|
264
|
+
arginfo = self._get_arg_packer(fe_argtypes)
|
|
265
|
+
arginfo.assign_names(self.get_arguments(fn), ["arg." + a for a in args])
|
|
266
|
+
fn.args[0].name = ".ret"
|
|
267
|
+
|
|
268
|
+
def get_arguments(self, func):
|
|
269
|
+
"""
|
|
270
|
+
Get the Python-level arguments of LLVM *func*.
|
|
271
|
+
"""
|
|
272
|
+
return func.args[1:]
|
|
273
|
+
|
|
274
|
+
def call_function(self, builder, callee, resty, argtys, args):
|
|
275
|
+
"""
|
|
276
|
+
Call the Numba-compiled *callee*.
|
|
277
|
+
"""
|
|
278
|
+
retty = callee.args[0].type.pointee
|
|
279
|
+
retvaltmp = cgutils.alloca_once(builder, retty)
|
|
280
|
+
# initialize return value
|
|
281
|
+
builder.store(cgutils.get_null_value(retty), retvaltmp)
|
|
282
|
+
|
|
283
|
+
arginfo = self._get_arg_packer(argtys)
|
|
284
|
+
args = arginfo.as_arguments(builder, args)
|
|
285
|
+
realargs = [retvaltmp] + list(args)
|
|
286
|
+
code = builder.call(callee, realargs)
|
|
287
|
+
status = self._get_return_status(builder, code)
|
|
288
|
+
retval = builder.load(retvaltmp)
|
|
289
|
+
out = self.context.get_returned_value(builder, resty, retval)
|
|
290
|
+
return status, out
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class _MinimalCallHelper(object):
|
|
294
|
+
"""
|
|
295
|
+
A call helper object for the "minimal" calling convention.
|
|
296
|
+
User exceptions are represented as integer codes and stored in
|
|
297
|
+
a mapping for retrieval from the caller.
|
|
298
|
+
"""
|
|
299
|
+
|
|
300
|
+
def __init__(self):
|
|
301
|
+
self.exceptions = {}
|
|
302
|
+
|
|
303
|
+
def _add_exception(self, exc, exc_args, locinfo):
|
|
304
|
+
"""
|
|
305
|
+
Add a new user exception to this helper. Returns an integer that can be
|
|
306
|
+
used to refer to the added exception in future.
|
|
307
|
+
|
|
308
|
+
Parameters
|
|
309
|
+
----------
|
|
310
|
+
exc :
|
|
311
|
+
exception type
|
|
312
|
+
exc_args : None or tuple
|
|
313
|
+
exception args
|
|
314
|
+
locinfo : tuple
|
|
315
|
+
location information
|
|
316
|
+
"""
|
|
317
|
+
exc_id = len(self.exceptions) + FIRST_USEREXC
|
|
318
|
+
self.exceptions[exc_id] = exc, exc_args, locinfo
|
|
319
|
+
return exc_id
|
|
320
|
+
|
|
321
|
+
def get_exception(self, exc_id):
|
|
322
|
+
"""
|
|
323
|
+
Get information about a user exception. Returns a tuple of
|
|
324
|
+
(exception type, exception args, location information).
|
|
325
|
+
|
|
326
|
+
Parameters
|
|
327
|
+
----------
|
|
328
|
+
id : integer
|
|
329
|
+
The ID of the exception to look up
|
|
330
|
+
"""
|
|
331
|
+
try:
|
|
332
|
+
return self.exceptions[exc_id]
|
|
333
|
+
except KeyError:
|
|
334
|
+
msg = "unknown error %d in native function" % exc_id
|
|
335
|
+
exc = SystemError
|
|
336
|
+
exc_args = (msg,)
|
|
337
|
+
locinfo = None
|
|
338
|
+
return exc, exc_args, locinfo
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
from abc import abstractmethod, ABCMeta
|
|
2
|
+
from numba.misc.llvm_pass_timings import PassTimingsCollection
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CodeLibrary(metaclass=ABCMeta):
|
|
6
|
+
"""
|
|
7
|
+
An interface for bundling LLVM code together and compiling it.
|
|
8
|
+
It is tied to a *codegen* instance (e.g. JITCUDACodegen) that will
|
|
9
|
+
determine how the LLVM code is transformed and linked together.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
_finalized = False
|
|
13
|
+
_object_caching_enabled = False
|
|
14
|
+
_disable_inspection = False
|
|
15
|
+
|
|
16
|
+
def __init__(self, codegen: "Codegen", name: str):
|
|
17
|
+
self._codegen = codegen
|
|
18
|
+
self._name = name
|
|
19
|
+
ptc_name = f"{self.__class__.__name__}({self._name!r})"
|
|
20
|
+
self._recorded_timings = PassTimingsCollection(ptc_name)
|
|
21
|
+
# Track names of the dynamic globals
|
|
22
|
+
self._dynamic_globals = []
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def has_dynamic_globals(self):
|
|
26
|
+
self._ensure_finalized()
|
|
27
|
+
return len(self._dynamic_globals) > 0
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def recorded_timings(self):
|
|
31
|
+
return self._recorded_timings
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def codegen(self):
|
|
35
|
+
"""
|
|
36
|
+
The codegen object owning this library.
|
|
37
|
+
"""
|
|
38
|
+
return self._codegen
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def name(self):
|
|
42
|
+
return self._name
|
|
43
|
+
|
|
44
|
+
def __repr__(self):
|
|
45
|
+
return "<Library %r at 0x%x>" % (self.name, id(self))
|
|
46
|
+
|
|
47
|
+
def _raise_if_finalized(self):
|
|
48
|
+
if self._finalized:
|
|
49
|
+
raise RuntimeError(
|
|
50
|
+
"operation impossible on finalized object %r" % (self,)
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
def _ensure_finalized(self):
|
|
54
|
+
if not self._finalized:
|
|
55
|
+
self.finalize()
|
|
56
|
+
|
|
57
|
+
def create_ir_module(self, name):
|
|
58
|
+
"""
|
|
59
|
+
Create an LLVM IR module for use by this library.
|
|
60
|
+
"""
|
|
61
|
+
self._raise_if_finalized()
|
|
62
|
+
ir_module = self._codegen._create_empty_module(name)
|
|
63
|
+
return ir_module
|
|
64
|
+
|
|
65
|
+
@abstractmethod
|
|
66
|
+
def add_linking_library(self, library):
|
|
67
|
+
"""
|
|
68
|
+
Add a library for linking into this library, without losing
|
|
69
|
+
the original library.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
@abstractmethod
|
|
73
|
+
def add_ir_module(self, ir_module):
|
|
74
|
+
"""
|
|
75
|
+
Add an LLVM IR module's contents to this library.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
@abstractmethod
|
|
79
|
+
def finalize(self):
|
|
80
|
+
"""
|
|
81
|
+
Finalize the library. After this call, nothing can be added anymore.
|
|
82
|
+
Finalization involves various stages of code optimization and
|
|
83
|
+
linking.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
@abstractmethod
|
|
87
|
+
def get_function(self, name):
|
|
88
|
+
"""
|
|
89
|
+
Return the function named ``name``.
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
@abstractmethod
|
|
93
|
+
def get_llvm_str(self):
|
|
94
|
+
"""
|
|
95
|
+
Get the human-readable form of the LLVM module.
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
@abstractmethod
|
|
99
|
+
def get_asm_str(self):
|
|
100
|
+
"""
|
|
101
|
+
Get the human-readable assembly.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
#
|
|
105
|
+
# Object cache hooks and serialization
|
|
106
|
+
#
|
|
107
|
+
|
|
108
|
+
def enable_object_caching(self):
|
|
109
|
+
self._object_caching_enabled = True
|
|
110
|
+
self._compiled_object = None
|
|
111
|
+
self._compiled = False
|
|
112
|
+
|
|
113
|
+
def _get_compiled_object(self):
|
|
114
|
+
if not self._object_caching_enabled:
|
|
115
|
+
raise ValueError("object caching not enabled in %s" % (self,))
|
|
116
|
+
if self._compiled_object is None:
|
|
117
|
+
raise RuntimeError("no compiled object yet for %s" % (self,))
|
|
118
|
+
return self._compiled_object
|
|
119
|
+
|
|
120
|
+
def _set_compiled_object(self, value):
|
|
121
|
+
if not self._object_caching_enabled:
|
|
122
|
+
raise ValueError("object caching not enabled in %s" % (self,))
|
|
123
|
+
if self._compiled:
|
|
124
|
+
raise ValueError("library already compiled: %s" % (self,))
|
|
125
|
+
self._compiled_object = value
|
|
126
|
+
self._disable_inspection = True
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class Codegen(metaclass=ABCMeta):
|
|
130
|
+
"""
|
|
131
|
+
Base Codegen class. It is expected that subclasses set the class attribute
|
|
132
|
+
``_library_class``, indicating the CodeLibrary class for the target.
|
|
133
|
+
|
|
134
|
+
Subclasses should also initialize:
|
|
135
|
+
|
|
136
|
+
``self._data_layout``: the data layout for the target.
|
|
137
|
+
``self._target_data``: the binding layer ``TargetData`` for the target.
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
@abstractmethod
|
|
141
|
+
def _create_empty_module(self, name):
|
|
142
|
+
"""
|
|
143
|
+
Create a new empty module suitable for the target.
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
@abstractmethod
|
|
147
|
+
def _add_module(self, module):
|
|
148
|
+
"""
|
|
149
|
+
Add a module to the execution engine. Ownership of the module is
|
|
150
|
+
transferred to the engine.
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def target_data(self):
|
|
155
|
+
"""
|
|
156
|
+
The LLVM "target data" object for this codegen instance.
|
|
157
|
+
"""
|
|
158
|
+
return self._target_data
|
|
159
|
+
|
|
160
|
+
def create_library(self, name, **kwargs):
|
|
161
|
+
"""
|
|
162
|
+
Create a :class:`CodeLibrary` object for use with this codegen
|
|
163
|
+
instance.
|
|
164
|
+
"""
|
|
165
|
+
return self._library_class(self, name, **kwargs)
|
|
166
|
+
|
|
167
|
+
def unserialize_library(self, serialized):
|
|
168
|
+
return self._library_class._unserialize(self, serialized)
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
from numba.core.tracing import event
|
|
2
|
+
|
|
3
|
+
from numba.core import callconv, bytecode, config, errors
|
|
4
|
+
from numba.core.errors import CompilerError
|
|
5
|
+
from numba.parfors.parfor import ParforDiagnostics
|
|
6
|
+
|
|
7
|
+
from numba.core.untyped_passes import ExtractByteCode, FixupArgs
|
|
8
|
+
from numba.core.targetconfig import ConfigStack
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class _CompileStatus(object):
|
|
12
|
+
"""
|
|
13
|
+
Describes the state of compilation. Used like a C record.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
__slots__ = ["fail_reason", "can_fallback"]
|
|
17
|
+
|
|
18
|
+
def __init__(self, can_fallback):
|
|
19
|
+
self.fail_reason = None
|
|
20
|
+
self.can_fallback = can_fallback
|
|
21
|
+
|
|
22
|
+
def __repr__(self):
|
|
23
|
+
vals = []
|
|
24
|
+
for k in self.__slots__:
|
|
25
|
+
vals.append("{k}={v}".format(k=k, v=getattr(self, k)))
|
|
26
|
+
return ", ".join(vals)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class StateDict(dict):
|
|
30
|
+
"""
|
|
31
|
+
A dictionary that has an overloaded getattr and setattr to permit getting
|
|
32
|
+
and setting key/values through the use of attributes.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
def __getattr__(self, attr):
|
|
36
|
+
try:
|
|
37
|
+
return self[attr]
|
|
38
|
+
except KeyError:
|
|
39
|
+
raise AttributeError(attr)
|
|
40
|
+
|
|
41
|
+
def __setattr__(self, attr, value):
|
|
42
|
+
self[attr] = value
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class _EarlyPipelineCompletion(Exception):
|
|
46
|
+
"""
|
|
47
|
+
Raised to indicate that a pipeline has completed early
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(self, result):
|
|
51
|
+
self.result = result
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _make_subtarget(targetctx, flags):
|
|
55
|
+
"""
|
|
56
|
+
Make a new target context from the given target context and flags.
|
|
57
|
+
"""
|
|
58
|
+
subtargetoptions = {}
|
|
59
|
+
if flags.debuginfo:
|
|
60
|
+
subtargetoptions["enable_debuginfo"] = True
|
|
61
|
+
if flags.boundscheck:
|
|
62
|
+
subtargetoptions["enable_boundscheck"] = True
|
|
63
|
+
if flags.nrt:
|
|
64
|
+
subtargetoptions["enable_nrt"] = True
|
|
65
|
+
if flags.auto_parallel:
|
|
66
|
+
subtargetoptions["auto_parallel"] = flags.auto_parallel
|
|
67
|
+
if flags.fastmath:
|
|
68
|
+
subtargetoptions["fastmath"] = flags.fastmath
|
|
69
|
+
error_model = callconv.create_error_model(flags.error_model, targetctx)
|
|
70
|
+
subtargetoptions["error_model"] = error_model
|
|
71
|
+
|
|
72
|
+
return targetctx.subtarget(**subtargetoptions)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class CompilerBase(object):
|
|
76
|
+
"""
|
|
77
|
+
Stores and manages states for the compiler
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __init__(
|
|
81
|
+
self, typingctx, targetctx, library, args, return_type, flags, locals
|
|
82
|
+
):
|
|
83
|
+
# Make sure the environment is reloaded
|
|
84
|
+
config.reload_config()
|
|
85
|
+
typingctx.refresh()
|
|
86
|
+
targetctx.refresh()
|
|
87
|
+
|
|
88
|
+
self.state = StateDict()
|
|
89
|
+
|
|
90
|
+
self.state.typingctx = typingctx
|
|
91
|
+
self.state.targetctx = _make_subtarget(targetctx, flags)
|
|
92
|
+
self.state.library = library
|
|
93
|
+
self.state.args = args
|
|
94
|
+
self.state.return_type = return_type
|
|
95
|
+
self.state.flags = flags
|
|
96
|
+
self.state.locals = locals
|
|
97
|
+
|
|
98
|
+
# Results of various steps of the compilation pipeline
|
|
99
|
+
self.state.bc = None
|
|
100
|
+
self.state.func_id = None
|
|
101
|
+
self.state.func_ir = None
|
|
102
|
+
self.state.lifted = None
|
|
103
|
+
self.state.lifted_from = None
|
|
104
|
+
self.state.typemap = None
|
|
105
|
+
self.state.calltypes = None
|
|
106
|
+
self.state.type_annotation = None
|
|
107
|
+
# holds arbitrary inter-pipeline stage meta data
|
|
108
|
+
self.state.metadata = {}
|
|
109
|
+
self.state.reload_init = []
|
|
110
|
+
# hold this for e.g. with_lifting, null out on exit
|
|
111
|
+
self.state.pipeline = self
|
|
112
|
+
|
|
113
|
+
# parfor diagnostics info, add to metadata
|
|
114
|
+
self.state.parfor_diagnostics = ParforDiagnostics()
|
|
115
|
+
self.state.metadata["parfor_diagnostics"] = (
|
|
116
|
+
self.state.parfor_diagnostics
|
|
117
|
+
)
|
|
118
|
+
self.state.metadata["parfors"] = {}
|
|
119
|
+
|
|
120
|
+
self.state.status = _CompileStatus(
|
|
121
|
+
can_fallback=self.state.flags.enable_pyobject
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def compile_extra(self, func):
|
|
125
|
+
self.state.func_id = bytecode.FunctionIdentity.from_function(func)
|
|
126
|
+
ExtractByteCode().run_pass(self.state)
|
|
127
|
+
|
|
128
|
+
self.state.lifted = ()
|
|
129
|
+
self.state.lifted_from = None
|
|
130
|
+
return self._compile_bytecode()
|
|
131
|
+
|
|
132
|
+
def compile_ir(self, func_ir, lifted=(), lifted_from=None):
|
|
133
|
+
self.state.func_id = func_ir.func_id
|
|
134
|
+
self.state.lifted = lifted
|
|
135
|
+
self.state.lifted_from = lifted_from
|
|
136
|
+
self.state.func_ir = func_ir
|
|
137
|
+
self.state.nargs = self.state.func_ir.arg_count
|
|
138
|
+
|
|
139
|
+
FixupArgs().run_pass(self.state)
|
|
140
|
+
return self._compile_ir()
|
|
141
|
+
|
|
142
|
+
def define_pipelines(self):
|
|
143
|
+
"""Child classes override this to customize the pipelines in use."""
|
|
144
|
+
raise NotImplementedError()
|
|
145
|
+
|
|
146
|
+
def _compile_core(self):
|
|
147
|
+
"""
|
|
148
|
+
Populate and run compiler pipeline
|
|
149
|
+
"""
|
|
150
|
+
with ConfigStack().enter(self.state.flags.copy()):
|
|
151
|
+
pms = self.define_pipelines()
|
|
152
|
+
for pm in pms:
|
|
153
|
+
pipeline_name = pm.pipeline_name
|
|
154
|
+
func_name = "%s.%s" % (
|
|
155
|
+
self.state.func_id.modname,
|
|
156
|
+
self.state.func_id.func_qualname,
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
event("Pipeline: %s for %s" % (pipeline_name, func_name))
|
|
160
|
+
self.state.metadata["pipeline_times"] = {
|
|
161
|
+
pipeline_name: pm.exec_times
|
|
162
|
+
}
|
|
163
|
+
is_final_pipeline = pm == pms[-1]
|
|
164
|
+
res = None
|
|
165
|
+
try:
|
|
166
|
+
pm.run(self.state)
|
|
167
|
+
if self.state.cr is not None:
|
|
168
|
+
break
|
|
169
|
+
except _EarlyPipelineCompletion as e:
|
|
170
|
+
res = e.result
|
|
171
|
+
break
|
|
172
|
+
except Exception as e:
|
|
173
|
+
if not isinstance(e, errors.NumbaError):
|
|
174
|
+
raise e
|
|
175
|
+
self.state.status.fail_reason = e
|
|
176
|
+
if is_final_pipeline:
|
|
177
|
+
raise e
|
|
178
|
+
else:
|
|
179
|
+
raise CompilerError("All available pipelines exhausted")
|
|
180
|
+
|
|
181
|
+
# Pipeline is done, remove self reference to release refs to user
|
|
182
|
+
# code
|
|
183
|
+
self.state.pipeline = None
|
|
184
|
+
|
|
185
|
+
# organise a return
|
|
186
|
+
if res is not None:
|
|
187
|
+
# Early pipeline completion
|
|
188
|
+
return res
|
|
189
|
+
else:
|
|
190
|
+
assert self.state.cr is not None
|
|
191
|
+
return self.state.cr
|
|
192
|
+
|
|
193
|
+
def _compile_bytecode(self):
|
|
194
|
+
"""
|
|
195
|
+
Populate and run pipeline for bytecode input
|
|
196
|
+
"""
|
|
197
|
+
assert self.state.func_ir is None
|
|
198
|
+
return self._compile_core()
|
|
199
|
+
|
|
200
|
+
def _compile_ir(self):
|
|
201
|
+
"""
|
|
202
|
+
Populate and run pipeline for IR input
|
|
203
|
+
"""
|
|
204
|
+
assert self.state.func_ir is not None
|
|
205
|
+
return self._compile_core()
|