Refactor.
Modify the way CDefinition is specialized.
This commit is contained in:
parent
01054c726f
commit
2acc070984
4 changed files with 96 additions and 51 deletions
|
|
@ -6,6 +6,7 @@ import contextlib
|
||||||
import llvm.core as lc
|
import llvm.core as lc
|
||||||
import llvm.ee as le
|
import llvm.ee as le
|
||||||
from llvm import LLVMException
|
from llvm import LLVMException
|
||||||
|
from . import shortnames as types
|
||||||
|
|
||||||
def _is_int(ty):
|
def _is_int(ty):
|
||||||
return isinstance(ty, lc.IntegerType)
|
return isinstance(ty, lc.IntegerType)
|
||||||
|
|
@ -149,18 +150,12 @@ class CBuilder(object):
|
||||||
func = mod.add_function(functype, name=name)
|
func = mod.add_function(functype, name=name)
|
||||||
return CBuilder(func)
|
return CBuilder(func)
|
||||||
|
|
||||||
def depends(self, cdef, **kwargs):
|
def depends(self, fndecl):
|
||||||
try:
|
return CFunc(self, fndecl(self.function.module))
|
||||||
func = cdef.define(self.function.module, **kwargs)
|
|
||||||
except NameError as e:
|
|
||||||
(func,) = e
|
|
||||||
return CFunc(self, func)
|
|
||||||
|
|
||||||
def printf(self, fmt, *args):
|
def printf(self, fmt, *args):
|
||||||
mod = self.function.module
|
mod = self.function.module
|
||||||
int_t = lc.Type.int()
|
functype = lc.Type.function(types.int, [types.char_p], True)
|
||||||
char_ptr_t = lc.Type.pointer(lc.Type.int(8))
|
|
||||||
functype = lc.Type.function(int_t, [char_ptr_t], True)
|
|
||||||
printf = mod.get_or_insert_function(functype, name='printf')
|
printf = mod.get_or_insert_function(functype, name='printf')
|
||||||
ret = self.builder.call(printf, [fmt.value]+_list_values(args))
|
ret = self.builder.call(printf, [fmt.value]+_list_values(args))
|
||||||
return CTemp(self, ret)
|
return CTemp(self, ret)
|
||||||
|
|
@ -224,7 +219,7 @@ class CBuilder(object):
|
||||||
if isinstance(count, CValue):
|
if isinstance(count, CValue):
|
||||||
count = count.value
|
count = count.value
|
||||||
elif not isinstance(count, lc.Value):
|
elif not isinstance(count, lc.Value):
|
||||||
count = self.constant(lc.Type.int(), count).value
|
count = self.constant(types.int, count).value
|
||||||
ptr = self.builder.alloca_array(ty, count, name=name)
|
ptr = self.builder.alloca_array(ty, count, name=name)
|
||||||
return CArray(self, ptr)
|
return CArray(self, ptr)
|
||||||
|
|
||||||
|
|
@ -236,7 +231,7 @@ class CBuilder(object):
|
||||||
raise TypeError(errmsg)
|
raise TypeError(errmsg)
|
||||||
self.builder.ret(val.value)
|
self.builder.ret(val.value)
|
||||||
else:
|
else:
|
||||||
if retty != lc.Type.void():
|
if retty != types.void:
|
||||||
errmsg = "Cannot return void"
|
errmsg = "Cannot return void"
|
||||||
raise TypeError(errmsg)
|
raise TypeError(errmsg)
|
||||||
self.builder.ret_void()
|
self.builder.ret_void()
|
||||||
|
|
@ -257,7 +252,7 @@ class CBuilder(object):
|
||||||
def forever(self):
|
def forever(self):
|
||||||
with self.loop() as loop:
|
with self.loop() as loop:
|
||||||
with loop.condition() as setcond:
|
with loop.condition() as setcond:
|
||||||
NULL = self.constant_null(lc.Type.int())
|
NULL = self.constant_null(types.int)
|
||||||
setcond( NULL == NULL )
|
setcond( NULL == NULL )
|
||||||
with loop.body():
|
with loop.body():
|
||||||
yield loop
|
yield loop
|
||||||
|
|
@ -266,7 +261,7 @@ class CBuilder(object):
|
||||||
def for_range(self, *args):
|
def for_range(self, *args):
|
||||||
def check_arg(x):
|
def check_arg(x):
|
||||||
if isinstance(x, int):
|
if isinstance(x, int):
|
||||||
return self.constant(lc.Type.int(), x)
|
return self.constant(types.int, x)
|
||||||
if not x.is_int:
|
if not x.is_int:
|
||||||
raise TypeError(x, "All args must be of integer type.")
|
raise TypeError(x, "All args must be of integer type.")
|
||||||
return x
|
return x
|
||||||
|
|
@ -303,7 +298,7 @@ class CBuilder(object):
|
||||||
def constant(self, ty, val):
|
def constant(self, ty, val):
|
||||||
if isinstance(ty, lc.IntegerType):
|
if isinstance(ty, lc.IntegerType):
|
||||||
res = lc.Constant.int(ty, val)
|
res = lc.Constant.int(ty, val)
|
||||||
elif ty==lc.Type.float() or ty==lc.Type.double():
|
elif ty == types.float or ty == types.double:
|
||||||
res = lc.Constant.real(ty, val)
|
res = lc.Constant.real(ty, val)
|
||||||
else:
|
else:
|
||||||
raise TypeError("Cannot auto build constant "
|
raise TypeError("Cannot auto build constant "
|
||||||
|
|
@ -316,6 +311,8 @@ class CBuilder(object):
|
||||||
|
|
||||||
def constant_string(self, string):
|
def constant_string(self, string):
|
||||||
mod = self.function.module
|
mod = self.function.module
|
||||||
|
# TODO
|
||||||
|
# Is this a safe way for uniquing identifying string
|
||||||
name = '.conststr.%x_%x' % (hash(string), len(string))
|
name = '.conststr.%x_%x' % (hash(string), len(string))
|
||||||
content = lc.Constant.stringz(string)
|
content = lc.Constant.stringz(string)
|
||||||
try:
|
try:
|
||||||
|
|
@ -323,10 +320,8 @@ class CBuilder(object):
|
||||||
except LLVMException:
|
except LLVMException:
|
||||||
globalstr = mod.add_global_variable(content.type, name=name)
|
globalstr = mod.add_global_variable(content.type, name=name)
|
||||||
globalstr.initializer = content
|
globalstr.initializer = content
|
||||||
# ptr = mod.add_global_variable(lc.Type.pointer(content.type.element),
|
|
||||||
# name=name+".ptr")
|
|
||||||
return CTemp(self, globalstr.bitcast(
|
return CTemp(self, globalstr.bitcast(
|
||||||
lc.Type.pointer(content.type.element)))
|
types.pointer(content.type.element)))
|
||||||
|
|
||||||
|
|
||||||
def get_intrinsic(self, intrinsic_id, tys):
|
def get_intrinsic(self, intrinsic_id, tys):
|
||||||
|
|
@ -423,18 +418,62 @@ class CBuilder(object):
|
||||||
def unreachable(self):
|
def unreachable(self):
|
||||||
self.builder.unreachable()
|
self.builder.unreachable()
|
||||||
|
|
||||||
|
class _DeclareCDef(object):
|
||||||
|
def __init__(self, cdef):
|
||||||
|
self.cdef = cdef
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.cdef._name_
|
||||||
|
|
||||||
|
def __call__(self, module):
|
||||||
|
try:
|
||||||
|
func = self.cdef.define(module)
|
||||||
|
except NameError as e:
|
||||||
|
(func,) = e
|
||||||
|
return func
|
||||||
|
|
||||||
|
class CDeclare(object):
|
||||||
|
def __init__(self, name, ty, ptr):
|
||||||
|
self._name = name
|
||||||
|
self._type = ty
|
||||||
|
self._ptr = ptr
|
||||||
|
|
||||||
|
def __call__(self, module):
|
||||||
|
fnptr = types.pointer(self._type)
|
||||||
|
ptr = lc.Constant.int(types.intp, self._ptr)
|
||||||
|
ptr = ptr.inttoptr(fnptr)
|
||||||
|
return ptr
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self._name
|
||||||
|
|
||||||
class CDefinition(CBuilder):
|
class CDefinition(CBuilder):
|
||||||
'''
|
'''
|
||||||
Inherit this class to for defining functions
|
Inherit this class to for defining functions
|
||||||
'''
|
'''
|
||||||
_name_ = 'unamed' # name of the function; should overide in subclass
|
_name_ = '' # name of the function; should overide in subclass
|
||||||
_retty_ = lc.Type.void() # return type; can overide in subclass
|
_retty_ = types.void # return type; can overide in subclass
|
||||||
_argtys_ = [] # a list of tuple(name, type); can overide in subclass
|
_argtys_ = [] # a list of tuple(name, type); can overide in subclass
|
||||||
|
|
||||||
|
def __new__(cls, *args, **kws):
|
||||||
|
try:
|
||||||
|
fnsp = getattr(cls, 'specialize')
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
cls = type('%s_Specialized' % cls.__name__, (cls,), {})
|
||||||
|
fnsp(*args, **kws)
|
||||||
|
|
||||||
|
obj = object.__new__(_DeclareCDef)
|
||||||
|
obj.__init__(cls)
|
||||||
|
return obj
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def define(cls, module, **kws):
|
def define(cls, module):
|
||||||
functype = lc.Type.function(cls._retty_, [v for k, v in cls._argtys_])
|
functype = lc.Type.function(cls._retty_, [v for k, v in cls._argtys_])
|
||||||
name = cls._name_ % kws
|
name = cls._name_
|
||||||
|
if not name:
|
||||||
|
raise NameError("Function name cannot be empty.")
|
||||||
func = module.get_or_insert_function(functype, name=name)
|
func = module.get_or_insert_function(functype, name=name)
|
||||||
if not func.is_declaration: # already defined?
|
if not func.is_declaration: # already defined?
|
||||||
raise NameError(func)
|
raise NameError(func)
|
||||||
|
|
@ -444,8 +483,9 @@ class CDefinition(CBuilder):
|
||||||
func.args[i].name = name
|
func.args[i].name = name
|
||||||
|
|
||||||
# Create builder and populate body
|
# Create builder and populate body
|
||||||
cbuilder = cls(func)
|
cbuilder = object.__new__(cls)
|
||||||
cbuilder.body(*cbuilder.args, **kws)
|
cbuilder.__init__(func)
|
||||||
|
cbuilder.body(*cbuilder.args)
|
||||||
cbuilder.close()
|
cbuilder.close()
|
||||||
return func
|
return func
|
||||||
|
|
||||||
|
|
@ -662,7 +702,7 @@ class CValue(object):
|
||||||
def __getitem__(self, idx):
|
def __getitem__(self, idx):
|
||||||
self._ensure_is_pointer()
|
self._ensure_is_pointer()
|
||||||
if not isinstance(idx, CValue):
|
if not isinstance(idx, CValue):
|
||||||
idx = self.parent.constant(lc.Type.int(), idx)
|
idx = self.parent.constant(types.int, idx)
|
||||||
bldr = self.parent.builder
|
bldr = self.parent.builder
|
||||||
ptr = bldr.gep(self.value, [idx.value])
|
ptr = bldr.gep(self.value, [idx.value])
|
||||||
return CVar(self.parent, ptr)
|
return CVar(self.parent, ptr)
|
||||||
|
|
@ -827,7 +867,7 @@ class CArray(CValue):
|
||||||
# if isinstance(idx, CValue):
|
# if isinstance(idx, CValue):
|
||||||
# idx = idx.value
|
# idx = idx.value
|
||||||
# elif not isinstance(idx, lc.Value):
|
# elif not isinstance(idx, lc.Value):
|
||||||
# idx = self.parent.constant(lc.Type.int(), idx).value
|
# idx = self.parent.constant(types.int, idx).value
|
||||||
# ptr = builder.gep(self.value, [idx])
|
# ptr = builder.gep(self.value, [idx])
|
||||||
# return CVar(self.parent, ptr)
|
# return CVar(self.parent, ptr)
|
||||||
|
|
||||||
|
|
@ -838,7 +878,7 @@ class CStruct(CValue):
|
||||||
|
|
||||||
def __init__(self, parent, ptr):
|
def __init__(self, parent, ptr):
|
||||||
super(CStruct, self).__init__(parent)
|
super(CStruct, self).__init__(parent)
|
||||||
makeind = lambda x: self.parent.constant(lc.Type.int(), x).value
|
makeind = lambda x: self.parent.constant(types.int, x).value
|
||||||
self.ptr = ptr
|
self.ptr = ptr
|
||||||
for i, (fd, _) in enumerate(self._fields_):
|
for i, (fd, _) in enumerate(self._fields_):
|
||||||
gep = self.parent.builder.gep(ptr, [makeind(0), makeind(i)])
|
gep = self.parent.builder.gep(ptr, [makeind(0), makeind(i)])
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,7 @@ class ParallelUFunc(CDefinition):
|
||||||
|
|
||||||
which should be implemented in subclass or mixin.
|
which should be implemented in subclass or mixin.
|
||||||
'''
|
'''
|
||||||
#_name_ = 'parallel_ufunc_%(ThreadCount)d'
|
|
||||||
_argtys_ = [
|
_argtys_ = [
|
||||||
('func', C.void_p),
|
('func', C.void_p),
|
||||||
('worker', C.void_p),
|
('worker', C.void_p),
|
||||||
|
|
@ -107,12 +107,9 @@ class ParallelUFunc(CDefinition):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def specialize(cls, num_thread):
|
def specialize(cls, num_thread):
|
||||||
name = 'parallel_ufunc_%d' % num_thread
|
print num_thread
|
||||||
newcls = type(name, (cls,), {
|
cls._name_ = 'parallel_ufunc_%d' % num_thread
|
||||||
'_name_' : name,
|
cls.ThreadCount = num_thread
|
||||||
'ThreadCount': num_thread,
|
|
||||||
})
|
|
||||||
return newcls
|
|
||||||
|
|
||||||
def body(self, func, worker, args, dimensions, steps, data):
|
def body(self, func, worker, args, dimensions, steps, data):
|
||||||
# Setup variables
|
# Setup variables
|
||||||
|
|
@ -346,16 +343,10 @@ class SpecializedParallelUFunc(CDefinition):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def specialize(cls, pufunc_def, core_def, func_def):
|
def specialize(cls, pufunc_def, core_def, func_def):
|
||||||
name = 'specialized_%s_%s_%s'% (pufunc_def._name_,
|
cls._name_ = 'specialized_%s_%s_%s'% (pufunc_def, core_def, func_def)
|
||||||
core_def._name_,
|
cls.PUFuncDef = pufunc_def
|
||||||
func_def._name_)
|
cls.CoreDef = core_def
|
||||||
newcls = type(name, (cls,), {
|
cls.FuncDef = func_def
|
||||||
'_name_' : name,
|
|
||||||
'PUFuncDef': pufunc_def,
|
|
||||||
'CoreDef' : core_def,
|
|
||||||
'FuncDef' : func_def,
|
|
||||||
})
|
|
||||||
return newcls
|
|
||||||
|
|
||||||
class PThreadAPI(CExternal):
|
class PThreadAPI(CExternal):
|
||||||
pthread_t = C.void_p
|
pthread_t = C.void_p
|
||||||
|
|
|
||||||
|
|
@ -48,10 +48,12 @@ class Tester(CDefinition):
|
||||||
ArgCount = 2
|
ArgCount = 2
|
||||||
WorkCount = 10000
|
WorkCount = 10000
|
||||||
|
|
||||||
PUfuncDef = ParallelUFuncPosix.specialize(num_thread=2)
|
|
||||||
SPUF = SpecializedParallelUFunc.specialize(
|
spufdef = SpecializedParallelUFunc(ParallelUFuncPosix(num_thread=2),
|
||||||
PUfuncDef, UFuncCore_D_D, Work_D_D)
|
UFuncCore_D_D(),
|
||||||
sppufunc = self.depends(SPUF)
|
Work_D_D())
|
||||||
|
|
||||||
|
sppufunc = self.depends(spufdef)
|
||||||
|
|
||||||
# real work
|
# real work
|
||||||
NULL = self.constant_null(C.void_p)
|
NULL = self.constant_null(C.void_p)
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,20 @@
|
||||||
|
'''
|
||||||
|
Test parallel-vectorize with numpy.fromfunc.
|
||||||
|
Uses the work load from test_parallel_vectorize.
|
||||||
|
'''
|
||||||
|
|
||||||
from test_parallel_vectorize import *
|
from test_parallel_vectorize import *
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
module = Module.new(__name__)
|
module = Module.new(__name__)
|
||||||
PUfuncDef = ParallelUFuncPosix.specialize(num_thread=2)
|
|
||||||
SPUF = SpecializedParallelUFunc.specialize(
|
spufdef = SpecializedParallelUFunc(ParallelUFuncPosix(num_thread=2),
|
||||||
PUfuncDef, UFuncCore_D_D, Work_D_D)
|
UFuncCore_D_D(),
|
||||||
sppufunc = SPUF.define(module)
|
Work_D_D())
|
||||||
|
|
||||||
|
sppufunc = spufdef(module)
|
||||||
|
|
||||||
module.verify()
|
module.verify()
|
||||||
|
|
||||||
|
|
@ -27,6 +34,11 @@ def main():
|
||||||
|
|
||||||
ptr_t = long # py2 only
|
ptr_t = long # py2 only
|
||||||
|
|
||||||
|
# Becareful that fromfunc does not provide full error checking yet.
|
||||||
|
# If typenum is out-of-bound, we have nasty memory corruptions.
|
||||||
|
# For instance, -1 for typenum will cause segfault.
|
||||||
|
# If elements of type-list (2nd arg) is tuple instead,
|
||||||
|
# there will also memory corruption. (Seems like code rewrite.)
|
||||||
typenum = np.dtype(np.double).num
|
typenum = np.dtype(np.double).num
|
||||||
ufunc = np.fromfunc([ptr_t(funcptr)], [[typenum, typenum]], 1, 1, [None])
|
ufunc = np.fromfunc([ptr_t(funcptr)], [[typenum, typenum]], 1, 1, [None])
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue