Skip to content

Commit

Permalink
initial commit - pass the first test (albeit requires setting up
Browse files Browse the repository at this point in the history
LD_LIBRARY_PATH path by hand)
  • Loading branch information
fijal committed Dec 1, 2014
1 parent 9044046 commit a6e1172
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
__pycache__/
*.py[cod]

# backups
*~

# C extensions
*.so

Expand Down
Empty file added jitpy/__init__.py
Empty file.
4 changes: 4 additions & 0 deletions jitpy/pypy.defs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

typedef struct pypy_defs {
void* (* basic_register)(char *, char *, char *);
} pypy_defs;
23 changes: 23 additions & 0 deletions jitpy/pypy_side.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

import cffi, new, os
ffi = cffi.FFI()
ffi.cdef(open(os.path.join(cur_dir, "pypy.defs")).read())

MAX_FUNCTIONS = 100
all_callbacks = []

mod = new.module('__global__')

@ffi.callback("void* (*)(char *, char *, char *)")
def basic_register(ll_source, ll_name, ll_tp):
source = ffi.string(ll_source)
name = ffi.string(ll_name)
tp = ffi.string(ll_tp)
exec source in mod.__dict__
func = mod.__dict__[name]
ll_callback = ffi.callback(tp)(func)
all_callbacks.append(ll_callback)
return ffi.cast('void *', ll_callback)

pypy_def = ffi.cast("struct pypy_defs*", c_argument)
pypy_def.basic_register = basic_register
72 changes: 72 additions & 0 deletions jitpy/wrapper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@

import cffi, os, inspect, py

def setup(pypy_home):
ffi = cffi.FFI()
ffi.cdef("""
void rpython_startup_code(void);
long pypy_setup_home(char* home, int verbose);
int pypy_execute_source(char* source);
int pypy_execute_source_ptr(char* source, void* ptr);
""")
os.environ['LD_LIBRARY_PATH'] = os.path.join(pypy_home, 'bin')

lib = ffi.verify("""
#include <include/PyPy.h>
""", libraries=["pypy-c"], include_dirs=[pypy_home], library_dirs=[os.path.join(pypy_home, 'bin')])
curdir = os.path.dirname(os.path.abspath(__file__))
defs = os.path.join(curdir, 'pypy.defs')
ffi.cdef(open(defs).read())
lib.rpython_startup_code()
res = lib.pypy_setup_home(os.path.join(os.path.abspath(pypy_home), 'bin'),
1)
pypy_side = os.path.join(curdir, 'pypy_side.py')
if res == -1:
raise Exception("cannot init pypy")
ptr = ffi.new("struct pypy_defs*")
res = lib.pypy_execute_source_ptr(str(py.code.Source("""
import sys, traceback
try:
import os
cur_dir = '%s'
execfile('%s')
except Exception, e:
traceback.print_tb(sys.exc_info()[2])
print "%%s:%%s" %% (e.__class__.__name__, e)
raise
""" % (curdir, pypy_side))), ptr)
if res:
raise Exception("error running pypy side")

converters = {
int: 'long',
float: 'double',
str: 'char*',
}

def jittify(argtypes, restype):
""" Wrap function into a callable visible from CPython, but run on
underlaying PyPy.
"""
ll_tp = converters[restype] + ' (*)(' + ', '.join(
[converters[arg] for arg in argtypes]) + ')'
def decorator(fn):
lines = inspect.getsource(fn).splitlines()
for i, line in enumerate(lines):
if line.strip(' ').startswith('@'):
continue
lines[i] = line.strip(' ')
break
source = "\n".join(lines[i:])
name = fn.__name__
handle = ptr.basic_register(source, name, ll_tp)
if not handle:
raise Exception("basic_register failed")
return ffi.cast(ll_tp, handle)
return decorator

globals()['jittify'] = jittify
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
cffi
Empty file added testing/__init__.py
Empty file.
22 changes: 22 additions & 0 deletions testing/test_jitpy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@

import os
from jitpy.wrapper import setup
if 'PYPY_HOME' not in os.environ:
raise Exception("please setup PYPY_HOME to point to your pypy installation")
setup(os.environ['PYPY_HOME'])
from jitpy.wrapper import jittify

class TestJitPy(object):
def test_float_func(self):
@jittify([float, float], float)
def func(a, b):
return a + b

assert func(1.2, 2.4) == 1.2 + 2.4

def test_int_func(self):
@jittify([int, int], int)
def func(a, b):
return a + b

assert func(2, 4) == 6

0 comments on commit a6e1172

Please sign in to comment.