packages feed

apple-0.3.0.0: pyc/applemodule.c

#include <HsFFI.h>
#include <Python.h>
#include<sys/mman.h>
#include<numpy/arrayobject.h>
#include"../include/apple_abi.h"
#include"../c/ffi.c"

typedef void* U;typedef PyObject* PO;typedef PyArrayObject* NPA;typedef size_t S;

#define R return
#define Sw switch
#define C case
#define BR break;
#define CT(o,c,s) {PyArray_Descr *d=PyArray_DESCR(o);if(!(d->type==c)){PyErr_SetString(PyExc_RuntimeError,s);}}

// https://numpy.org/doc/stable/reference/c-api/array.html
U f_npy(const NPA o) {
    J rnk=PyArray_NDIM(o);
    npy_intp* dims=PyArray_DIMS(o);
    J n=PyArray_SIZE(o);
    J sz_i=1+rnk+n;
    CT(o,'d',"Error: expected an array of floats")
    S sz=sz_i*8;
    U x=malloc(sz);J* x_i=x; F* x_f=x;
    x_i[0]=rnk;
    DO(i,rnk,x_i[i+1]=(J)dims[i]);
    U data=PyArray_DATA(o);
    memcpy(x_f+rnk+1,data,n*8);
    R x;
}

U i_npy(NPA o) {
    J rnk=PyArray_NDIM(o);
    npy_intp* dims=PyArray_DIMS(o);
    J n=PyArray_SIZE(o);
    J sz_i=1+rnk+n;
    S sz=sz_i*8;
    CT(o,'l',"Error: expected an array of 64-bit integers")
    U x=malloc(sz);J* x_i=x;
    x_i[0]=rnk;
    DO(i,rnk,x_i[i+1]=(J)dims[i]);
    U data=PyArray_DATA(o);
    memcpy(x_i+rnk+1,data,n*8);
    R x;
}

PyObject* npy_i(U x) {
    J* i_p=x;
    J t=1;
    J rnk=i_p[0];
    npy_intp* dims=malloc(sizeof(npy_intp)*rnk);
    DO(i,rnk,t*=i_p[i+1];dims[i]=(npy_intp)i_p[i+1]);
    S sz=8*t;
    U data=malloc(sz);
    // TODO figure out how to do this without copying
    memcpy(data,i_p+rnk+1,sz);
    PyObject* res=PyArray_SimpleNewFromData(rnk,dims,NPY_INT64,data);
    PyArray_ENABLEFLAGS((PyArrayObject*)res,NPY_ARRAY_OWNDATA);
    free(x);R res;
}

PyObject* npy_f(U x) {
    J* i_p=x;
    J t=1;
    J rnk=i_p[0];
    npy_intp* dims=malloc(sizeof(npy_intp)*rnk);
    DO(i,rnk,t*=i_p[i+1];dims[i]=(npy_intp)i_p[i+1]);
    S sz=8*t;
    U data=malloc(sz);
    memcpy(data,i_p+rnk+1,sz);
    PyObject* res=PyArray_SimpleNewFromData(rnk,dims,NPY_FLOAT64,data);
    PyArray_ENABLEFLAGS((PyArrayObject*)res,NPY_ARRAY_OWNDATA);
    free(x);R res;
}

void freety(FnTy* x){free(x->args);free(x);}

static PyObject* apple_typeof(PyObject* self, PyObject *args) {
    const char* inp;
    PyArg_ParseTuple(args, "s", &inp);
    char* err;char** err_p = &err;
    char* res = apple_printty(inp,err_p);
    if (res == NULL) {
        PyErr_SetString(PyExc_RuntimeError, err);
        free(err);R NULL;
    }
    PyObject* pyres = PyUnicode_FromString(res);
    free(res); R pyres;
}

static PyObject* apple_asm(PyObject* self, PyObject *args) {
    const char* inp;
    PyArg_ParseTuple(args, "s", &inp);
    char* err;char** err_p = &err;
    char* res = apple_dumpasm(inp,err_p);
    if (res == NULL) {
        PyErr_SetString(PyExc_RuntimeError, err);
        free(err);R NULL;
    }
    PyObject* pyres = PyUnicode_FromString(res);
    free(res); R pyres;
}

static PyObject* apple_ir(PyObject* self, PyObject *args) {
    const char* inp;
    PyArg_ParseTuple(args, "s", &inp);
    char* err;char** err_p = &err;
    char* res = apple_dumpir(inp,err_p);
    if (res == NULL) {
        PyErr_SetString(PyExc_RuntimeError, err);
        free(err);R NULL;
    }
    PyObject* pyres = PyUnicode_FromString(res);
    free(res); R pyres;
}

typedef struct PyCacheObject {
    PyObject_HEAD
    U bc;S c_sz;FnTy* ty; U sa;ffi_cif* ffi;
} PyCacheObject;

static void cache_dealloc(PyCacheObject* self) {
    munmap(self->bc,self->c_sz);
    free(self->sa);freety(self->ty);free(self->ffi);
}

static PyTypeObject CacheType = {
    PyVarObject_HEAD_INIT(NULL, 0)
    .tp_name = "Cache",
    .tp_doc = PyDoc_STR("Cached JIT function"),
    .tp_basicsize = sizeof(PyCacheObject),
    .tp_itemsize = 0,
    .tp_flags = Py_TPFLAGS_DEFAULT,
    .tp_new = PyType_GenericNew,
    .tp_dealloc = (destructor)cache_dealloc,
};

static PyObject* apple_jit(PyObject *self, PyObject *args) {
    const char* inp;
    PyArg_ParseTuple(args, "s", &inp);
    char* err;char** err_p=&err;
    FnTy* ty=apple_ty(inp,err_p);
    if(ty == NULL){
        PyErr_SetString(PyExc_RuntimeError, err);
        free(err);R NULL;
    };
    U fp;S f_sz;U s;
    JC jc={(P)&malloc,(P)&free,(P)&lrand48,(P)&drand48,(P)&exp,(P)&log,(P)&pow};
    fp=apple_compile(&jc,inp,&f_sz,&s);
    PyCacheObject* cc=PyObject_New(PyCacheObject, &CacheType);
    ffi_cif* ffi=apple_ffi(ty);
    cc->bc=fp;cc->c_sz=f_sz;cc->ty=ty;cc->sa=s;cc->ffi=ffi;
    Py_INCREF(cc);
    R (PyObject*)cc;
}

// file:///usr/share/doc/libffi8/html/The-Basics.html
static PyObject* apple_f(PyObject* self, PyObject* args) {
    PyCacheObject* c;PO arg0=NULL;PO arg1=NULL;PO arg2=NULL;PO arg3=NULL;PO arg4=NULL;PO arg5=NULL;
    PyArg_ParseTuple(args, "O|OOOOOO", &c, &arg0, &arg1, &arg2, &arg3, &arg4, &arg5);
    FnTy* ty=c->ty;U fp=c->bc;
    PO r;
    ffi_cif* cif=c->ffi;
    int argc=ty->argc;
    U* vals=alloca(sizeof(U)*argc);
    U ret=alloca(8);
    PO pyarg;PO pyargs[]={arg0,arg1,arg2,arg3,arg4,arg5};
    for(int k=0;k<argc;k++){
        pyarg=pyargs[k];
        if(pyarg!=NULL){
            Sw(ty->args[k]){
                C IA: {U* x=alloca(sizeof(U));x[0]=i_npy((NPA)pyarg);vals[k]=x;};BR
                C FA: {U* x=alloca(sizeof(U));x[0]=f_npy((NPA)pyarg);vals[k]=x;};BR
                C I_t: {J* xi=alloca(sizeof(J));xi[0]=PyLong_AsLong(pyarg);vals[k]=xi;};BR
                C F_t: {F* xf=alloca(sizeof(F));xf[0]=PyFloat_AsDouble(pyarg);vals[k]=xf;};BR
            }
        }
    }
    ffi_call(cif,fp,ret,vals);
    Sw(ty->res){
        C IA: r=npy_i(*(U*)ret);BR
        C FA: r=npy_f(*(U*)ret);BR
        C F_t: r=PyFloat_FromDouble(*(F*)ret);BR
        C I_t: r=PyLong_FromLongLong(*(J*)ret);BR
    }
    R r;
};

static PyMethodDef AppleMethods[] = {
    {"f", apple_f, METH_VARARGS, "Run a JIT-compiled function"},
    {"jit", apple_jit, METH_VARARGS, "JIT a function"},
    {"typeof", apple_typeof, METH_VARARGS, "Display type of expression"},
    {"asm", apple_asm, METH_VARARGS, "Dump x86 assembly"},
    {"ir", apple_ir, METH_VARARGS, "Dump IR (debug)"},
    {NULL,NULL,0,NULL}
};

static struct PyModuleDef applemodule = { PyModuleDef_HEAD_INIT, "apple", NULL, -1, AppleMethods };

PyMODINIT_FUNC PyInit_apple(void) { hs_init(0,0); import_array(); R PyModule_Create(&applemodule); }