_box.c 7.67 KB
Newer Older
dugupeiwen's avatar
dugupeiwen committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/*
Implements jitclass Box type in python c-api level.
*/
#include "../../_pymodule.h"

typedef struct {
    PyObject_HEAD
    void *meminfoptr, *dataptr;
} BoxObject;


/* Store function defined in numba.core.runtime._nrt_python for use in box_dealloc.
 * It points to a function is code segment that does not need user deallocation
 * and does not disappear while the process is still running.
 */
static void (*MemInfo_release)(void*) = NULL;


/*
 * Box.__init__()
 * Takes no arguments.
 * meminfoptr and dataptr are set to NULL.
 */
static
int Box_init(BoxObject *self, PyObject *args, PyObject *kwds) {
    static char *keywords[] = {NULL};
    if (!PyArg_ParseTupleAndKeywords(args, kwds, "", keywords))
    {
        return -1;
    }
    /* Initialize attributes to NULL */
    self->meminfoptr = NULL;
    self->dataptr = NULL;
    return 0;
}

/*
 * Box destructor
 * Release MemInfo pointed by meminfoptr.
 * Free the instance.
 */
static
void box_dealloc(BoxObject *box)
{
    if (box->meminfoptr) MemInfo_release((void*)box->meminfoptr);
    Py_TYPE(box)->tp_free((PyObject *) box);
}


static const char Box_doc[] = "A box for numba created jit-class instance";


static PyTypeObject BoxType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    "_box.Box",                               /* tp_name */
    sizeof(BoxObject),                        /* tp_basicsize */
    0,                                        /* tp_itemsize */
    (destructor)box_dealloc,                  /* tp_dealloc */
    0,                                        /* tp_vectorcall_offset */
    0,                                        /* tp_getattr */
    0,                                        /* tp_setattr */
    0,                                        /* tp_as_async */
    0,                                        /* tp_repr */
    0,                                        /* tp_as_number */
    0,                                        /* tp_as_sequence */
    0,                                        /* tp_as_mapping */
    0,                                        /* tp_hash */
    0,                                        /* tp_call */
    0,                                        /* tp_str */
    0,                                        /* tp_getattro */
    0,                                        /* tp_setattro */
    0,                                        /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
    Box_doc,                                  /* tp_doc */
    0,                                        /* tp_traverse */
    0,                                        /* tp_clear */
    0,                                        /* tp_richcompare */
    0,                                        /* tp_weaklistoffset */
    0,                                        /* tp_iter */
    0,                                        /* tp_iternext */
    0,                                        /* tp_methods */
    0,                                        /* tp_members */
    0,                                        /* tp_getset */
    0,                                        /* tp_base */
    0,                                        /* tp_dict */
    0,                                        /* tp_descr_get */
    0,                                        /* tp_descr_set */
    0,                                        /* tp_dictoffset */
    (initproc)Box_init,                       /* tp_init */
    0,                                        /* tp_alloc */
    PyType_GenericNew,                        /* tp_new */
    0,                                        /* tp_free */
    0,                                        /* tp_is_gc */
    0,                                        /* tp_bases */
    0,                                        /* tp_mro */
    0,                                        /* tp_cache */
    0,                                        /* tp_subclasses */
    0,                                        /* tp_weaklist */
    0,                                        /* tp_del */
    0,                                        /* tp_version_tag */
    0,                                        /* tp_finalize */
/* The docs suggest Python 3.8 has no tp_vectorcall
 * https://github.com/python/cpython/blob/d917cfe4051d45b2b755c726c096ecfcc4869ceb/Doc/c-api/typeobj.rst?plain=1#L146
 * but the header has it:
 * https://github.com/python/cpython/blob/d917cfe4051d45b2b755c726c096ecfcc4869ceb/Include/cpython/object.h#L257
 */
    0,                                        /* tp_vectorcall */
#if (PY_MAJOR_VERSION == 3) && (PY_MINOR_VERSION == 8)
/* This is Python 3.8 only.
 * See: https://github.com/python/cpython/blob/3.8/Include/cpython/object.h
 * there's a tp_print preserved for backwards compatibility. xref:
 * https://github.com/python/cpython/blob/d917cfe4051d45b2b755c726c096ecfcc4869ceb/Include/cpython/object.h#L260
 */
    0,                                        /* tp_print */
#endif

/* WARNING: Do not remove this, only modify it! It is a version guard to
 * act as a reminder to update this struct on Python version update! */
#if (PY_MAJOR_VERSION == 3)
#if ! ((PY_MINOR_VERSION == 8) || (PY_MINOR_VERSION == 9) || (PY_MINOR_VERSION == 10) || (PY_MINOR_VERSION == 11))
#error "Python minor version is not supported."
#endif
#else
#error "Python major version is not supported."
#endif
/* END WARNING*/
};


/* Import MemInfo_Release from numba.core.runtime._nrt_python once for use in
 * Box_dealloc.
 */
static void *
import_meminfo_release(void) {
    PyObject *nrtmod = NULL;
    PyObject *helperdct = NULL;
    PyObject *mi_rel_fn = NULL;
    void *fnptr = NULL;
    /* from numba.core.runtime import _nrt_python */
    nrtmod = PyImport_ImportModule("numba.core.runtime._nrt_python");
    if (!nrtmod) goto cleanup;
    /* helperdct = _nrt_python.c_helpers */
    helperdct = PyObject_GetAttrString(nrtmod, "c_helpers");
    if (!helperdct) goto cleanup;
    /* helperdct['MemInfo_release'] */
    mi_rel_fn = PyDict_GetItemString(helperdct, "MemInfo_release");
    if (!mi_rel_fn) goto cleanup;
    fnptr = PyLong_AsVoidPtr(mi_rel_fn);

cleanup:
    Py_XDECREF(nrtmod);
    Py_XDECREF(helperdct);
    return fnptr;
}

/* Debug utils.
 * Get internal dataptr field from Box.
 */
static
PyObject* box_get_dataptr(PyObject *self, PyObject *args) {
    BoxObject *box;
    if (!PyArg_ParseTuple(args, "O!", &BoxType, (PyObject*)&box))
        return NULL;
    return PyLong_FromVoidPtr(box->dataptr);
}

/* Debug utils.
 * Get internal meminfoptr field from Box.
 */
static
PyObject* box_get_meminfoptr(PyObject *self, PyObject *args) {
    BoxObject *box;
    if (!PyArg_ParseTuple(args, "O!", &BoxType, (PyObject*)&box))
        return NULL;
    return PyLong_FromVoidPtr(box->meminfoptr);
}


static PyMethodDef ext_methods[] = {
#define declmethod(func) { #func , ( PyCFunction )func , METH_VARARGS , NULL }
    declmethod(box_get_dataptr),
    declmethod(box_get_meminfoptr),
    { NULL },
#undef declmethod
};


MOD_INIT(_box) {
    PyObject *m;

    MOD_DEF(m, "_box", "No docs", ext_methods)
    if (m == NULL)
        return MOD_ERROR_VAL;

    /* init BoxType */
    if (PyType_Ready(&BoxType))
        return MOD_ERROR_VAL;

    /* import and cache NRT_MemInfo_release function pointer */
    MemInfo_release = import_meminfo_release();
    if (!MemInfo_release) return MOD_ERROR_VAL;

    /* bind BoxType */
    Py_INCREF(&BoxType);
    PyModule_AddObject(m, "Box", (PyObject *) (&BoxType));

    /* bind address to direct access utils */;
    PyModule_AddObject(m, "box_meminfoptr_offset",
                       PyLong_FromSsize_t(offsetof(BoxObject, meminfoptr)));
    PyModule_AddObject(m, "box_dataptr_offset",
                       PyLong_FromSsize_t(offsetof(BoxObject, dataptr)));

    return MOD_SUCCESS_VAL(m);
}