From 2acc0709845cdaa571c110c13e9cbaabe229e90f Mon Sep 17 00:00:00 2001 From: Siu Kwan Lam Date: Tue, 7 Aug 2012 16:55:33 -0700 Subject: [PATCH] Refactor. Modify the way CDefinition is specialized. --- llvm_cbuilder/builder.py | 92 +++++++++++++++++++++++--------- parallel_vectorize.py | 25 +++------ test_parallel_vectorize.py | 10 ++-- test_parallel_vectorize_numpy.py | 20 +++++-- 4 files changed, 96 insertions(+), 51 deletions(-) diff --git a/llvm_cbuilder/builder.py b/llvm_cbuilder/builder.py index 88516df..e591e9d 100644 --- a/llvm_cbuilder/builder.py +++ b/llvm_cbuilder/builder.py @@ -6,6 +6,7 @@ import contextlib import llvm.core as lc import llvm.ee as le from llvm import LLVMException +from . import shortnames as types def _is_int(ty): return isinstance(ty, lc.IntegerType) @@ -149,18 +150,12 @@ class CBuilder(object): func = mod.add_function(functype, name=name) return CBuilder(func) - def depends(self, cdef, **kwargs): - try: - func = cdef.define(self.function.module, **kwargs) - except NameError as e: - (func,) = e - return CFunc(self, func) + def depends(self, fndecl): + return CFunc(self, fndecl(self.function.module)) def printf(self, fmt, *args): mod = self.function.module - int_t = lc.Type.int() - char_ptr_t = lc.Type.pointer(lc.Type.int(8)) - functype = lc.Type.function(int_t, [char_ptr_t], True) + functype = lc.Type.function(types.int, [types.char_p], True) printf = mod.get_or_insert_function(functype, name='printf') ret = self.builder.call(printf, [fmt.value]+_list_values(args)) return CTemp(self, ret) @@ -224,7 +219,7 @@ class CBuilder(object): if isinstance(count, CValue): count = count.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) return CArray(self, ptr) @@ -236,7 +231,7 @@ class CBuilder(object): raise TypeError(errmsg) self.builder.ret(val.value) else: - if retty != lc.Type.void(): + if retty != types.void: errmsg = "Cannot return void" raise TypeError(errmsg) self.builder.ret_void() @@ -257,7 +252,7 @@ class CBuilder(object): def forever(self): with self.loop() as loop: with loop.condition() as setcond: - NULL = self.constant_null(lc.Type.int()) + NULL = self.constant_null(types.int) setcond( NULL == NULL ) with loop.body(): yield loop @@ -266,7 +261,7 @@ class CBuilder(object): def for_range(self, *args): def check_arg(x): if isinstance(x, int): - return self.constant(lc.Type.int(), x) + return self.constant(types.int, x) if not x.is_int: raise TypeError(x, "All args must be of integer type.") return x @@ -303,7 +298,7 @@ class CBuilder(object): def constant(self, ty, val): if isinstance(ty, lc.IntegerType): 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) else: raise TypeError("Cannot auto build constant " @@ -316,6 +311,8 @@ class CBuilder(object): def constant_string(self, string): mod = self.function.module + # TODO + # Is this a safe way for uniquing identifying string name = '.conststr.%x_%x' % (hash(string), len(string)) content = lc.Constant.stringz(string) try: @@ -323,10 +320,8 @@ class CBuilder(object): except LLVMException: globalstr = mod.add_global_variable(content.type, name=name) globalstr.initializer = content -# ptr = mod.add_global_variable(lc.Type.pointer(content.type.element), -# name=name+".ptr") return CTemp(self, globalstr.bitcast( - lc.Type.pointer(content.type.element))) + types.pointer(content.type.element))) def get_intrinsic(self, intrinsic_id, tys): @@ -423,18 +418,62 @@ class CBuilder(object): def unreachable(self): 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): ''' Inherit this class to for defining functions ''' - _name_ = 'unamed' # name of the function; should overide in subclass - _retty_ = lc.Type.void() # return type; can overide in subclass + _name_ = '' # name of the function; should overide in subclass + _retty_ = types.void # return 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 - def define(cls, module, **kws): + def define(cls, module): 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) if not func.is_declaration: # already defined? raise NameError(func) @@ -444,8 +483,9 @@ class CDefinition(CBuilder): func.args[i].name = name # Create builder and populate body - cbuilder = cls(func) - cbuilder.body(*cbuilder.args, **kws) + cbuilder = object.__new__(cls) + cbuilder.__init__(func) + cbuilder.body(*cbuilder.args) cbuilder.close() return func @@ -662,7 +702,7 @@ class CValue(object): def __getitem__(self, idx): self._ensure_is_pointer() if not isinstance(idx, CValue): - idx = self.parent.constant(lc.Type.int(), idx) + idx = self.parent.constant(types.int, idx) bldr = self.parent.builder ptr = bldr.gep(self.value, [idx.value]) return CVar(self.parent, ptr) @@ -827,7 +867,7 @@ class CArray(CValue): # if isinstance(idx, CValue): # idx = idx.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]) # return CVar(self.parent, ptr) @@ -838,7 +878,7 @@ class CStruct(CValue): def __init__(self, parent, ptr): 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 for i, (fd, _) in enumerate(self._fields_): gep = self.parent.builder.gep(ptr, [makeind(0), makeind(i)]) diff --git a/parallel_vectorize.py b/parallel_vectorize.py index b5a03f5..4c8daab 100644 --- a/parallel_vectorize.py +++ b/parallel_vectorize.py @@ -95,7 +95,7 @@ class ParallelUFunc(CDefinition): which should be implemented in subclass or mixin. ''' - #_name_ = 'parallel_ufunc_%(ThreadCount)d' + _argtys_ = [ ('func', C.void_p), ('worker', C.void_p), @@ -107,12 +107,9 @@ class ParallelUFunc(CDefinition): @classmethod def specialize(cls, num_thread): - name = 'parallel_ufunc_%d' % num_thread - newcls = type(name, (cls,), { - '_name_' : name, - 'ThreadCount': num_thread, - }) - return newcls + print num_thread + cls._name_ = 'parallel_ufunc_%d' % num_thread + cls.ThreadCount = num_thread def body(self, func, worker, args, dimensions, steps, data): # Setup variables @@ -346,16 +343,10 @@ class SpecializedParallelUFunc(CDefinition): @classmethod def specialize(cls, pufunc_def, core_def, func_def): - name = 'specialized_%s_%s_%s'% (pufunc_def._name_, - core_def._name_, - func_def._name_) - newcls = type(name, (cls,), { - '_name_' : name, - 'PUFuncDef': pufunc_def, - 'CoreDef' : core_def, - 'FuncDef' : func_def, - }) - return newcls + cls._name_ = 'specialized_%s_%s_%s'% (pufunc_def, core_def, func_def) + cls.PUFuncDef = pufunc_def + cls.CoreDef = core_def + cls.FuncDef = func_def class PThreadAPI(CExternal): pthread_t = C.void_p diff --git a/test_parallel_vectorize.py b/test_parallel_vectorize.py index ab28363..54d6b14 100644 --- a/test_parallel_vectorize.py +++ b/test_parallel_vectorize.py @@ -48,10 +48,12 @@ class Tester(CDefinition): ArgCount = 2 WorkCount = 10000 - PUfuncDef = ParallelUFuncPosix.specialize(num_thread=2) - SPUF = SpecializedParallelUFunc.specialize( - PUfuncDef, UFuncCore_D_D, Work_D_D) - sppufunc = self.depends(SPUF) + + spufdef = SpecializedParallelUFunc(ParallelUFuncPosix(num_thread=2), + UFuncCore_D_D(), + Work_D_D()) + + sppufunc = self.depends(spufdef) # real work NULL = self.constant_null(C.void_p) diff --git a/test_parallel_vectorize_numpy.py b/test_parallel_vectorize_numpy.py index 7b1ca51..392c81d 100644 --- a/test_parallel_vectorize_numpy.py +++ b/test_parallel_vectorize_numpy.py @@ -1,13 +1,20 @@ +''' +Test parallel-vectorize with numpy.fromfunc. +Uses the work load from test_parallel_vectorize. +''' + from test_parallel_vectorize import * import numpy as np def main(): module = Module.new(__name__) - PUfuncDef = ParallelUFuncPosix.specialize(num_thread=2) - SPUF = SpecializedParallelUFunc.specialize( - PUfuncDef, UFuncCore_D_D, Work_D_D) - sppufunc = SPUF.define(module) + + spufdef = SpecializedParallelUFunc(ParallelUFuncPosix(num_thread=2), + UFuncCore_D_D(), + Work_D_D()) + + sppufunc = spufdef(module) module.verify() @@ -27,6 +34,11 @@ def main(): 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 ufunc = np.fromfunc([ptr_t(funcptr)], [[typenum, typenum]], 1, 1, [None])