cleanup and doc
This commit is contained in:
parent
8b45094e20
commit
68e2359ad2
4 changed files with 406 additions and 56 deletions
|
|
@ -8,6 +8,10 @@ import llvm.ee as le
|
||||||
from llvm import LLVMException
|
from llvm import LLVMException
|
||||||
from . import shortnames as types
|
from . import shortnames as types
|
||||||
|
|
||||||
|
###
|
||||||
|
# Utilities
|
||||||
|
###
|
||||||
|
|
||||||
def _is_int(ty):
|
def _is_int(ty):
|
||||||
return isinstance(ty, lc.IntegerType)
|
return isinstance(ty, lc.IntegerType)
|
||||||
|
|
||||||
|
|
@ -47,6 +51,19 @@ def _change_block_temporarily_dummy(*args):
|
||||||
yield
|
yield
|
||||||
|
|
||||||
class _IfElse(object):
|
class _IfElse(object):
|
||||||
|
'''if-else construct.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
with cbuilder.ifelse(cond) as ifelse:
|
||||||
|
with ifelse.then():
|
||||||
|
# code when cond is true
|
||||||
|
# this block is mandatory
|
||||||
|
with ifelse.otherwise():
|
||||||
|
# code when cond is false
|
||||||
|
# this block is optional
|
||||||
|
'''
|
||||||
|
|
||||||
def __init__(self, parent, cond):
|
def __init__(self, parent, cond):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.cond = cond
|
self.cond = cond
|
||||||
|
|
@ -84,6 +101,22 @@ class _IfElse(object):
|
||||||
builder.position_at_end(bbend)
|
builder.position_at_end(bbend)
|
||||||
|
|
||||||
class _Loop(object):
|
class _Loop(object):
|
||||||
|
'''while...do loop.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
with cbuilder.loop() as loop:
|
||||||
|
with loop.condition() as setcond:
|
||||||
|
# Put the condition evaluation here
|
||||||
|
setcond( cond ) # set loop condition
|
||||||
|
# Do not put code after setcond(...)
|
||||||
|
with loop.body():
|
||||||
|
# Put the code of the loop body here
|
||||||
|
|
||||||
|
Use loop.break_loop() to break out of the loop.
|
||||||
|
Use loop.continue_loop() to jump the condition evaulation.
|
||||||
|
'''
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
|
||||||
|
|
@ -107,9 +140,8 @@ class _Loop(object):
|
||||||
def body(self):
|
def body(self):
|
||||||
builder = self.parent.builder
|
builder = self.parent.builder
|
||||||
builder.position_at_end(self._bbbody)
|
builder.position_at_end(self._bbbody)
|
||||||
|
|
||||||
yield self
|
yield self
|
||||||
|
# close last block
|
||||||
if not _is_block_terminated(builder.basic_block):
|
if not _is_block_terminated(builder.basic_block):
|
||||||
builder.branch(self._bbcond)
|
builder.branch(self._bbcond)
|
||||||
|
|
||||||
|
|
@ -121,9 +153,6 @@ class _Loop(object):
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
builder = self.parent.builder
|
builder = self.parent.builder
|
||||||
# if not _is_block_terminated(builder.basic_block):
|
|
||||||
# with _change_block_temporarily(builder, builder.basic_block):
|
|
||||||
# builder.branch(self._bbend)
|
|
||||||
builder.position_at_end(self._bbend)
|
builder.position_at_end(self._bbend)
|
||||||
|
|
||||||
class CBuilder(object):
|
class CBuilder(object):
|
||||||
|
|
@ -131,14 +160,18 @@ class CBuilder(object):
|
||||||
A wrapper class for features in llvm-py package
|
A wrapper class for features in llvm-py package
|
||||||
to allow user to use C-like high-level language contruct easily.
|
to allow user to use C-like high-level language contruct easily.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, function):
|
def __init__(self, function):
|
||||||
|
'''constructor
|
||||||
|
|
||||||
|
function : is an empty function to be populating.
|
||||||
|
'''
|
||||||
self.function = function
|
self.function = function
|
||||||
self.declare_block = self.function.append_basic_block('decl')
|
self.declare_block = self.function.append_basic_block('decl')
|
||||||
self.first_body_block = self.function.append_basic_block('body')
|
self.first_body_block = self.function.append_basic_block('body')
|
||||||
self.builder = lc.Builder.new(self.first_body_block)
|
self.builder = lc.Builder.new(self.first_body_block)
|
||||||
self.target_data = le.TargetData.new(self.function.module.data_layout)
|
self.target_data = le.TargetData.new(self.function.module.data_layout)
|
||||||
|
# Prepare arguments. Make all function arguments behave like variables.
|
||||||
# prepare arguments
|
|
||||||
self.args = []
|
self.args = []
|
||||||
for arg in function.args:
|
for arg in function.args:
|
||||||
var = self.var(arg.type, arg, name=arg.name)
|
var = self.var(arg.type, arg, name=arg.name)
|
||||||
|
|
@ -146,21 +179,40 @@ class CBuilder(object):
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def new_function(mod, name, ret, args):
|
def new_function(mod, name, ret, args):
|
||||||
|
'''factory method
|
||||||
|
|
||||||
|
Create a new function in the module and return a CBuilder instance.
|
||||||
|
'''
|
||||||
functype = lc.Type.function(ret, args)
|
functype = lc.Type.function(ret, args)
|
||||||
func = mod.add_function(functype, name=name)
|
func = mod.add_function(functype, name=name)
|
||||||
return CBuilder(func)
|
return CBuilder(func)
|
||||||
|
|
||||||
def depends(self, fndecl):
|
def depends(self, fndecl):
|
||||||
|
'''add function dependency
|
||||||
|
|
||||||
|
Returns a CFunc instance and define the function if it is not defined.
|
||||||
|
|
||||||
|
fndecl : is a callable that takes a `llvm.core.Module` and returns
|
||||||
|
a function pointer.
|
||||||
|
'''
|
||||||
return CFunc(self, fndecl(self.function.module))
|
return CFunc(self, fndecl(self.function.module))
|
||||||
|
|
||||||
def printf(self, fmt, *args):
|
def printf(self, fmt, *args):
|
||||||
mod = self.function.module
|
'''printf() from libc
|
||||||
functype = lc.Type.function(types.int, [types.char_p], True)
|
|
||||||
printf = mod.get_or_insert_function(functype, name='printf')
|
fmt : a character string holding printf format string.
|
||||||
ret = self.builder.call(printf, [fmt.value]+_list_values(args))
|
*args : additional variable arguments.
|
||||||
|
'''
|
||||||
|
from .libc import LibC
|
||||||
|
libc = LibC(self)
|
||||||
|
ret = libc.printf(fmt, *args)
|
||||||
return CTemp(self, ret)
|
return CTemp(self, ret)
|
||||||
|
|
||||||
def debug(self, *args):
|
def debug(self, *args):
|
||||||
|
'''debug print
|
||||||
|
|
||||||
|
Use printf to dump the values of all arguments.
|
||||||
|
'''
|
||||||
type_mapper = {
|
type_mapper = {
|
||||||
'i8' : '%c',
|
'i8' : '%c',
|
||||||
'i16': '%hd',
|
'i16': '%hd',
|
||||||
|
|
@ -174,17 +226,27 @@ class CBuilder(object):
|
||||||
if isinstance(i, str):
|
if isinstance(i, str):
|
||||||
itemsfmt.append(i.replace('%', '%%'))
|
itemsfmt.append(i.replace('%', '%%'))
|
||||||
else:
|
else:
|
||||||
ty = type_mapper[str(i.type)]
|
tyname = str(i.type)
|
||||||
|
if tyname == 'float':
|
||||||
|
# auto convert float to double
|
||||||
|
ty = '%e'
|
||||||
|
i = i.cast(types.double)
|
||||||
|
else:
|
||||||
|
ty = type_mapper[tyname]
|
||||||
itemsfmt.append(ty)
|
itemsfmt.append(ty)
|
||||||
items.append(i)
|
items.append(i)
|
||||||
fmt = ' '.join(itemsfmt) + '\n'
|
fmt = ' '.join(itemsfmt) + '\n'
|
||||||
return self.printf(self.constant_string(fmt), *items)
|
return self.printf(self.constant_string(fmt), *items)
|
||||||
|
|
||||||
def var(self, ty, value=None, name=''):
|
def var(self, ty, value=None, name=''):
|
||||||
'''
|
'''allocate variable on the stack
|
||||||
Only allocate in the first block
|
|
||||||
|
ty : variable type
|
||||||
|
value : [optional] initializer value
|
||||||
|
name : [optional] name used in LLVM IR
|
||||||
'''
|
'''
|
||||||
with _change_block_temporarily(self.builder, self.declare_block):
|
with _change_block_temporarily(self.builder, self.declare_block):
|
||||||
|
# goto the first block
|
||||||
is_cstruct = _is_cstruct(ty)
|
is_cstruct = _is_cstruct(ty)
|
||||||
if is_cstruct:
|
if is_cstruct:
|
||||||
cstruct = ty
|
cstruct = ty
|
||||||
|
|
@ -203,27 +265,45 @@ class CBuilder(object):
|
||||||
return CVar(self, ptr)
|
return CVar(self, ptr)
|
||||||
|
|
||||||
def var_copy(self, val, name=''):
|
def var_copy(self, val, name=''):
|
||||||
|
'''allocate a new variable by copying another value
|
||||||
|
|
||||||
|
The new variable has the same type and value of `val`.
|
||||||
|
'''
|
||||||
return self.var(val.type, val, name=name)
|
return self.var(val.type, val, name=name)
|
||||||
|
|
||||||
def array(self, ty, count, name=''):
|
def array(self, ty, count, name=''):
|
||||||
|
'''allocate an array on the stack
|
||||||
|
|
||||||
|
ty : array element type
|
||||||
|
count : array size; can be python int, llvm.core.Constant, or CValue
|
||||||
|
name : [optional] name used in LLVM IR
|
||||||
|
'''
|
||||||
if isinstance(count, int) or isinstance(count, lc.Constant):
|
if isinstance(count, int) or isinstance(count, lc.Constant):
|
||||||
|
# Only go to the first block if array size is fixed.
|
||||||
contexthelper = _change_block_temporarily
|
contexthelper = _change_block_temporarily
|
||||||
else:
|
else:
|
||||||
|
# Do not go to the first block if the array size is dynamic.
|
||||||
contexthelper = _change_block_temporarily_dummy
|
contexthelper = _change_block_temporarily_dummy
|
||||||
|
|
||||||
with contexthelper(self.builder, self.declare_block):
|
with contexthelper(self.builder, self.declare_block):
|
||||||
is_cstruct = _is_cstruct(ty)
|
if _is_cstruct(ty): # array of struct?
|
||||||
if is_cstruct:
|
|
||||||
cstruct = ty
|
cstruct = ty
|
||||||
ty = ty.llvm_type()
|
ty = ty.llvm_type()
|
||||||
|
|
||||||
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(types.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)
|
||||||
|
|
||||||
def ret(self, val=None):
|
def ret(self, val=None):
|
||||||
|
'''insert return statement
|
||||||
|
|
||||||
|
val : if is `None`, insert return-void
|
||||||
|
else, return `val`
|
||||||
|
'''
|
||||||
retty = self.function.type.pointee.return_type
|
retty = self.function.type.pointee.return_type
|
||||||
if val is not None:
|
if val is not None:
|
||||||
if val.type != retty:
|
if val.type != retty:
|
||||||
|
|
@ -238,18 +318,26 @@ class CBuilder(object):
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def ifelse(self, cond):
|
def ifelse(self, cond):
|
||||||
|
'''start a if-else block
|
||||||
|
|
||||||
|
cond : branch condition
|
||||||
|
'''
|
||||||
cb = _IfElse(self, cond)
|
cb = _IfElse(self, cond)
|
||||||
yield cb
|
yield cb
|
||||||
cb.close()
|
cb.close()
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def loop(self):
|
def loop(self):
|
||||||
|
'''start a loop block
|
||||||
|
'''
|
||||||
cb = _Loop(self)
|
cb = _Loop(self)
|
||||||
yield cb
|
yield cb
|
||||||
cb.close()
|
cb.close()
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def forever(self):
|
def forever(self):
|
||||||
|
'''start a forever loop block
|
||||||
|
'''
|
||||||
with self.loop() as loop:
|
with self.loop() as loop:
|
||||||
with loop.condition() as setcond:
|
with loop.condition() as setcond:
|
||||||
NULL = self.constant_null(types.int)
|
NULL = self.constant_null(types.int)
|
||||||
|
|
@ -259,6 +347,10 @@ class CBuilder(object):
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def for_range(self, *args):
|
def for_range(self, *args):
|
||||||
|
'''start a for-range block.
|
||||||
|
|
||||||
|
*args : same as arguments of builtin `range()`
|
||||||
|
'''
|
||||||
def check_arg(x):
|
def check_arg(x):
|
||||||
if isinstance(x, int):
|
if isinstance(x, int):
|
||||||
return self.constant(types.int, x)
|
return self.constant(types.int, x)
|
||||||
|
|
@ -287,15 +379,26 @@ class CBuilder(object):
|
||||||
idx += step
|
idx += step
|
||||||
|
|
||||||
def position_at_end(self, bb):
|
def position_at_end(self, bb):
|
||||||
|
'''reposition inserter to the end of basic-block
|
||||||
|
|
||||||
|
bb : a basic block
|
||||||
|
'''
|
||||||
self.basic_block = bb
|
self.basic_block = bb
|
||||||
self.builder.position_at_end(bb)
|
self.builder.position_at_end(bb)
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
|
'''end code generation
|
||||||
|
'''
|
||||||
# Close declaration block
|
# Close declaration block
|
||||||
with _change_block_temporarily(self.builder, self.declare_block):
|
with _change_block_temporarily(self.builder, self.declare_block):
|
||||||
self.builder.branch(self.first_body_block)
|
self.builder.branch(self.first_body_block)
|
||||||
|
|
||||||
def constant(self, ty, val):
|
def constant(self, ty, val):
|
||||||
|
'''create a constant
|
||||||
|
|
||||||
|
ty : data type
|
||||||
|
val : initializer
|
||||||
|
'''
|
||||||
if isinstance(ty, lc.IntegerType):
|
if isinstance(ty, lc.IntegerType):
|
||||||
res = lc.Constant.int(ty, val)
|
res = lc.Constant.int(ty, val)
|
||||||
elif ty == types.float or ty == types.double:
|
elif ty == types.float or ty == types.double:
|
||||||
|
|
@ -306,119 +409,239 @@ class CBuilder(object):
|
||||||
return CTemp(self, res)
|
return CTemp(self, res)
|
||||||
|
|
||||||
def constant_null(self, ty):
|
def constant_null(self, ty):
|
||||||
|
'''create a zero filled constant
|
||||||
|
|
||||||
|
ty : data type
|
||||||
|
'''
|
||||||
res = lc.Constant.null(ty)
|
res = lc.Constant.null(ty)
|
||||||
return CTemp(self, res)
|
return CTemp(self, res)
|
||||||
|
|
||||||
def constant_string(self, string):
|
def constant_string(self, string):
|
||||||
|
'''create a constant string
|
||||||
|
|
||||||
|
This will de-duplication string of same content to minimize memory use.
|
||||||
|
'''
|
||||||
mod = self.function.module
|
mod = self.function.module
|
||||||
# TODO
|
collision = 0
|
||||||
# Is this a safe way for uniquing identifying string
|
name_fmt = '.conststr.%x.%x'
|
||||||
name = '.conststr.%x_%x' % (hash(string), len(string))
|
|
||||||
content = lc.Constant.stringz(string)
|
content = lc.Constant.stringz(string)
|
||||||
|
while True:
|
||||||
|
name = name_fmt % (hash(string), collision)
|
||||||
try:
|
try:
|
||||||
|
# check if the name already exists
|
||||||
globalstr = mod.get_global_variable_named(name)
|
globalstr = mod.get_global_variable_named(name)
|
||||||
except LLVMException:
|
except LLVMException:
|
||||||
|
# new constant string
|
||||||
globalstr = mod.add_global_variable(content.type, name=name)
|
globalstr = mod.add_global_variable(content.type, name=name)
|
||||||
globalstr.initializer = content
|
globalstr.initializer = content
|
||||||
|
globalstr.global_constant = True
|
||||||
|
else:
|
||||||
|
# compare existing content
|
||||||
|
existed = str(globalstr.initializer)
|
||||||
|
if existed != str(content):
|
||||||
|
collision += 1
|
||||||
|
continue # loop until we resolve the name collision
|
||||||
|
|
||||||
return CTemp(self, globalstr.bitcast(
|
return CTemp(self, globalstr.bitcast(
|
||||||
types.pointer(content.type.element)))
|
types.pointer(content.type.element)))
|
||||||
|
|
||||||
|
|
||||||
def get_intrinsic(self, intrinsic_id, tys):
|
def get_intrinsic(self, intrinsic_id, tys):
|
||||||
|
'''get intrinsic function
|
||||||
|
|
||||||
|
intrinsic_id : numerical ID of target intrinsic
|
||||||
|
tys : type argument for the intrinsic
|
||||||
|
'''
|
||||||
lfunc = lc.Function.intrinsic(self.function.module, intrinsic_id, tys)
|
lfunc = lc.Function.intrinsic(self.function.module, intrinsic_id, tys)
|
||||||
return CFunc(self, lfunc)
|
return CFunc(self, lfunc)
|
||||||
|
|
||||||
def get_function_named(self, name):
|
def get_function_named(self, name):
|
||||||
|
'''get function by name
|
||||||
|
'''
|
||||||
m = self.function.module
|
m = self.function.module
|
||||||
func = m.get_function_named(name)
|
func = m.get_function_named(name)
|
||||||
return CFunc(self, func)
|
return CFunc(self, func)
|
||||||
|
|
||||||
def is_terminated(self):
|
def is_terminated(self):
|
||||||
'''
|
'''is the current basic-block terminated?
|
||||||
Is the current basic-block terminated?
|
|
||||||
'''
|
'''
|
||||||
return _is_block_terminated(self.builder.basic_block)
|
return _is_block_terminated(self.builder.basic_block)
|
||||||
|
|
||||||
def atomic_cmpxchg(self, ptr, old, val, ordering, crossthread=True):
|
def atomic_cmpxchg(self, ptr, old, val, ordering, crossthread=True):
|
||||||
|
'''atomic compare-exchange
|
||||||
|
|
||||||
|
ptr : pointer to data
|
||||||
|
old : old value to compare to
|
||||||
|
val : new value
|
||||||
|
ordering : memory ordering as a string
|
||||||
|
crossthread : set to `False` for single-thread code
|
||||||
|
|
||||||
|
Returns the old value on success.
|
||||||
|
'''
|
||||||
res = self.builder.atomic_cmpxchg(ptr.value, old.value, val.value,
|
res = self.builder.atomic_cmpxchg(ptr.value, old.value, val.value,
|
||||||
ordering, crossthread)
|
ordering, crossthread)
|
||||||
return CTemp(self, res)
|
return CTemp(self, res)
|
||||||
|
|
||||||
def atomic_xchg(self, ptr, val, ordering, crossthread=True):
|
def atomic_xchg(self, ptr, val, ordering, crossthread=True):
|
||||||
|
'''atomic exchange
|
||||||
|
|
||||||
|
ptr : pointer to data
|
||||||
|
val : new value
|
||||||
|
ordering : memory ordering as a string
|
||||||
|
crossthread : set to `False` for single-thread code
|
||||||
|
|
||||||
|
Returns the old value
|
||||||
|
'''
|
||||||
|
|
||||||
res = self.builder.atomic_xchg(ptr.value, val.value,
|
res = self.builder.atomic_xchg(ptr.value, val.value,
|
||||||
ordering, crossthread)
|
ordering, crossthread)
|
||||||
return CTemp(self, res)
|
return CTemp(self, res)
|
||||||
|
|
||||||
def atomic_add(self, ptr, val, ordering, crossthread=True):
|
def atomic_add(self, ptr, val, ordering, crossthread=True):
|
||||||
|
'''atomic add
|
||||||
|
|
||||||
|
ptr : pointer to data
|
||||||
|
val : new value
|
||||||
|
ordering : memory ordering as a string
|
||||||
|
crossthread : set to `False` for single-thread code
|
||||||
|
|
||||||
|
Returns the computation result of the operation
|
||||||
|
'''
|
||||||
|
|
||||||
res = self.builder.atomic_add(ptr.value, val.value,
|
res = self.builder.atomic_add(ptr.value, val.value,
|
||||||
ordering, crossthread)
|
ordering, crossthread)
|
||||||
return CTemp(self, res)
|
return CTemp(self, res)
|
||||||
|
|
||||||
def atomic_sub(self, ptr, val, ordering, crossthread=True):
|
def atomic_sub(self, ptr, val, ordering, crossthread=True):
|
||||||
|
'''atomic sub
|
||||||
|
|
||||||
|
See `atomic_add` for parameters documentation
|
||||||
|
'''
|
||||||
res = self.builder.atomic_sub(ptr.value, val.value,
|
res = self.builder.atomic_sub(ptr.value, val.value,
|
||||||
ordering, crossthread)
|
ordering, crossthread)
|
||||||
return CTemp(self, res)
|
return CTemp(self, res)
|
||||||
|
|
||||||
def atomic_and(self, ptr, val, ordering, crossthread=True):
|
def atomic_and(self, ptr, val, ordering, crossthread=True):
|
||||||
|
'''atomic bitwise and
|
||||||
|
|
||||||
|
See `atomic_add` for parameters documentation
|
||||||
|
'''
|
||||||
res = self.builder.atomic_and(ptr.value, val.value,
|
res = self.builder.atomic_and(ptr.value, val.value,
|
||||||
ordering, crossthread)
|
ordering, crossthread)
|
||||||
return CTemp(self, res)
|
return CTemp(self, res)
|
||||||
|
|
||||||
def atomic_nand(self, ptr, val, ordering, crossthread=True):
|
def atomic_nand(self, ptr, val, ordering, crossthread=True):
|
||||||
|
'''atomic bitwise nand
|
||||||
|
|
||||||
|
See `atomic_add` for parameters documentation
|
||||||
|
'''
|
||||||
res = self.builder.atomic_nand(ptr.value, val.value,
|
res = self.builder.atomic_nand(ptr.value, val.value,
|
||||||
ordering, crossthread)
|
ordering, crossthread)
|
||||||
return CTemp(self, res)
|
return CTemp(self, res)
|
||||||
|
|
||||||
def atomic_or(self, ptr, val, ordering, crossthread=True):
|
def atomic_or(self, ptr, val, ordering, crossthread=True):
|
||||||
|
'''atomic bitwise or
|
||||||
|
|
||||||
|
See `atomic_add` for parameters documentation
|
||||||
|
'''
|
||||||
res = self.builder.atomic_or(ptr.value, val.value,
|
res = self.builder.atomic_or(ptr.value, val.value,
|
||||||
ordering, crossthread)
|
ordering, crossthread)
|
||||||
return CTemp(self, res)
|
return CTemp(self, res)
|
||||||
|
|
||||||
def atomic_xor(self, ptr, val, ordering, crossthread=True):
|
def atomic_xor(self, ptr, val, ordering, crossthread=True):
|
||||||
|
'''atomic bitwise xor
|
||||||
|
|
||||||
|
See `atomic_add` for parameters documentation
|
||||||
|
'''
|
||||||
|
|
||||||
res = self.builder.atomic_xor(ptr.value, val.value,
|
res = self.builder.atomic_xor(ptr.value, val.value,
|
||||||
ordering, crossthread)
|
ordering, crossthread)
|
||||||
return CTemp(self, res)
|
return CTemp(self, res)
|
||||||
|
|
||||||
def atomic_max(self, ptr, val, ordering, crossthread=True):
|
def atomic_max(self, ptr, val, ordering, crossthread=True):
|
||||||
|
'''atomic signed maximum between value at `ptr` and `val`
|
||||||
|
|
||||||
|
See `atomic_add` for parameters documentation
|
||||||
|
'''
|
||||||
res = self.builder.atomic_max(ptr.value, val.value,
|
res = self.builder.atomic_max(ptr.value, val.value,
|
||||||
ordering, crossthread)
|
ordering, crossthread)
|
||||||
return CTemp(self, res)
|
return CTemp(self, res)
|
||||||
|
|
||||||
def atomic_min(self, ptr, val, ordering, crossthread=True):
|
def atomic_min(self, ptr, val, ordering, crossthread=True):
|
||||||
|
'''atomic signed minimum between value at `ptr` and `val`
|
||||||
|
|
||||||
|
See `atomic_add` for parameters documentation
|
||||||
|
'''
|
||||||
res = self.builder.atomic_min(ptr.value, val.value,
|
res = self.builder.atomic_min(ptr.value, val.value,
|
||||||
ordering, crossthread)
|
ordering, crossthread)
|
||||||
return CTemp(self, res)
|
return CTemp(self, res)
|
||||||
|
|
||||||
def atomic_umax(self, ptr, val, ordering, crossthread=True):
|
def atomic_umax(self, ptr, val, ordering, crossthread=True):
|
||||||
|
'''atomic unsigned maximum between value at `ptr` and `val`
|
||||||
|
|
||||||
|
See `atomic_add` for parameters documentation
|
||||||
|
'''
|
||||||
res = self.builder.atomic_umax(ptr.value, val.value,
|
res = self.builder.atomic_umax(ptr.value, val.value,
|
||||||
ordering, crossthread)
|
ordering, crossthread)
|
||||||
return CTemp(self, res)
|
return CTemp(self, res)
|
||||||
|
|
||||||
def atomic_umin(self, ptr, val, ordering, crossthread=True):
|
def atomic_umin(self, ptr, val, ordering, crossthread=True):
|
||||||
|
'''atomic unsigned minimum between value at `ptr` and `val`
|
||||||
|
|
||||||
|
See `atomic_add` for parameters documentation
|
||||||
|
'''
|
||||||
res = self.builder.atomic_umin(ptr.value, val.value,
|
res = self.builder.atomic_umin(ptr.value, val.value,
|
||||||
ordering, crossthread)
|
ordering, crossthread)
|
||||||
return CTemp(self, res)
|
return CTemp(self, res)
|
||||||
|
|
||||||
def atomic_load(self, ptr, ordering, align=1, crossthread=True):
|
def atomic_load(self, ptr, ordering, align=1, crossthread=True):
|
||||||
|
'''atomic load
|
||||||
|
|
||||||
|
ptr : pointer to the value to load
|
||||||
|
align : memory alignment in bytes
|
||||||
|
See `atomic_add` for other documentation of other parameters
|
||||||
|
'''
|
||||||
res = self.builder.atomic_load(ptr.value, ordering, align, crossthread)
|
res = self.builder.atomic_load(ptr.value, ordering, align, crossthread)
|
||||||
return CTemp(self, res)
|
return CTemp(self, res)
|
||||||
|
|
||||||
def atomic_store(self, val, ptr, ordering, align=1, crossthread=True):
|
def atomic_store(self, val, ptr, ordering, align=1, crossthread=True):
|
||||||
|
'''atomic store
|
||||||
|
|
||||||
|
ptr : pointer to where to store
|
||||||
|
val : value to store
|
||||||
|
align : memory alignment in bytes
|
||||||
|
See `atomic_add` for other documentation of other parameters
|
||||||
|
'''
|
||||||
|
|
||||||
res = self.builder.atomic_store(val.value, ptr.value, ordering,
|
res = self.builder.atomic_store(val.value, ptr.value, ordering,
|
||||||
align, crossthread)
|
align, crossthread)
|
||||||
return CTemp(self, res)
|
return CTemp(self, res)
|
||||||
|
|
||||||
def fence(self, ordering, crossthread=True):
|
def fence(self, ordering, crossthread=True):
|
||||||
|
'''insert memory fence
|
||||||
|
'''
|
||||||
res = self.builder.fence(ordering, crossthread)
|
res = self.builder.fence(ordering, crossthread)
|
||||||
return CTemp(self, res)
|
return CTemp(self, res)
|
||||||
|
|
||||||
def alignment(self, ty):
|
def alignment(self, ty):
|
||||||
|
'''get minimum alignment of `ty`
|
||||||
|
'''
|
||||||
return self.target_data.abi_alignment(ty)
|
return self.target_data.abi_alignment(ty)
|
||||||
|
|
||||||
def unreachable(self):
|
def unreachable(self):
|
||||||
|
'''insert instruction that causes segfault some platform (Intel),
|
||||||
|
or no-op on others.
|
||||||
|
|
||||||
|
It has no defined semantic.
|
||||||
|
'''
|
||||||
self.builder.unreachable()
|
self.builder.unreachable()
|
||||||
|
|
||||||
class _DeclareCDef(object):
|
class _DeclareCDef(object):
|
||||||
|
'''create a function a CDefinition to use with `CBuilder.depends`
|
||||||
|
|
||||||
|
An instance of this class is created by the constructor of CDefinition.
|
||||||
|
Do not use directly.
|
||||||
|
'''
|
||||||
def __init__(self, cdef):
|
def __init__(self, cdef):
|
||||||
self.cdef = cdef
|
self.cdef = cdef
|
||||||
|
|
||||||
|
|
@ -433,6 +656,9 @@ class _DeclareCDef(object):
|
||||||
return func
|
return func
|
||||||
|
|
||||||
class CDeclare(object):
|
class CDeclare(object):
|
||||||
|
'''create a function from name, type and pointer to use with
|
||||||
|
`CBuilder.depends`
|
||||||
|
'''
|
||||||
def __init__(self, name, ty, ptr):
|
def __init__(self, name, ty, ptr):
|
||||||
self._name = name
|
self._name = name
|
||||||
self._type = ty
|
self._type = ty
|
||||||
|
|
@ -448,33 +674,51 @@ class CDeclare(object):
|
||||||
return self._name
|
return self._name
|
||||||
|
|
||||||
class CDefinition(CBuilder):
|
class CDefinition(CBuilder):
|
||||||
'''
|
'''represents function definition
|
||||||
Inherit this class to for defining functions
|
|
||||||
|
Inherit from this class to create a new function definition.
|
||||||
|
|
||||||
|
Class Members
|
||||||
|
-------------
|
||||||
|
_name_ : name of the function
|
||||||
|
_retty_ : return type
|
||||||
|
_argtys_ : argument names and types as list of tuples;
|
||||||
|
e.g. [ ( 'myarg', lc.Type.int() ), ... ]
|
||||||
'''
|
'''
|
||||||
_name_ = '' # name of the function; should overide in subclass
|
_name_ = '' # name of the function; should overide in subclass
|
||||||
_retty_ = types.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):
|
def __new__(cls, *args, **kws):
|
||||||
try:
|
if cls.is_generic():
|
||||||
fnsp = getattr(cls, 'specialize')
|
# Call specialize if it is defined.
|
||||||
except AttributeError:
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
cls = type('%s_Specialized' % cls.__name__, (cls,), {})
|
cls = type('%s_Specialized' % cls.__name__, (cls,), {})
|
||||||
fnsp(*args, **kws)
|
cls.specialize(*args, **kws)
|
||||||
|
|
||||||
obj = object.__new__(_DeclareCDef)
|
obj = object.__new__(_DeclareCDef)
|
||||||
obj.__init__(cls)
|
obj.__init__(cls)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def is_generic(cls):
|
||||||
|
'''Is this a generic definition?
|
||||||
|
'''
|
||||||
|
return hasattr(cls, 'specialize')
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def define(cls, module):
|
def define(cls, module):
|
||||||
|
'''define the function in the module.
|
||||||
|
|
||||||
|
Raises NameError if a function of the same name has already been
|
||||||
|
defined.
|
||||||
|
'''
|
||||||
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_
|
name = cls._name_
|
||||||
if not name:
|
if not name:
|
||||||
raise NameError("Function name cannot be empty.")
|
raise AttributeError("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)
|
||||||
|
|
||||||
|
|
@ -490,25 +734,24 @@ class CDefinition(CBuilder):
|
||||||
return func
|
return func
|
||||||
|
|
||||||
def body(self):
|
def body(self):
|
||||||
'''
|
'''overide this function to define the body.
|
||||||
Overide this function to define the body.
|
|
||||||
'''
|
'''
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
class CValue(object):
|
class CValue(object):
|
||||||
'''
|
'''
|
||||||
= Signess =
|
Signedness
|
||||||
Since LLVM type does not provide signess attribute. This information
|
----------
|
||||||
|
Since LLVM type does not provide signedness attribute. This information
|
||||||
is provided in the CValue.unsigned attribute. The default value is
|
is provided in the CValue.unsigned attribute. The default value is
|
||||||
`None`, meaning that this attribute is not set.
|
`None`, meaning that this attribute is not set.
|
||||||
|
|
||||||
In casting operation, signess information is passed as an optional arg.
|
In casting operation, signednss information is passed as an optional arg.
|
||||||
|
|
||||||
In binary operation, signess of the left operand is used.
|
In binary operation, signedness of the left operand is used.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# Attribute for for integer values.
|
unsigned = None # attribute for for integer values.
|
||||||
unsigned = None
|
|
||||||
|
|
||||||
_BINOP_MAP = {
|
_BINOP_MAP = {
|
||||||
# op-name : (signed int, unsigned int, real)
|
# op-name : (signed int, unsigned int, real)
|
||||||
|
|
@ -542,6 +785,8 @@ class CValue(object):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
|
||||||
def _use_binop(self, op):
|
def _use_binop(self, op):
|
||||||
|
'''implements binary operations
|
||||||
|
'''
|
||||||
def wrapped(rhs):
|
def wrapped(rhs):
|
||||||
self._ensure_same_type(rhs)
|
self._ensure_same_type(rhs)
|
||||||
binop = self._BINOP_MAP[op]
|
binop = self._BINOP_MAP[op]
|
||||||
|
|
@ -560,6 +805,8 @@ class CValue(object):
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
def _use_bitwise(self, op):
|
def _use_bitwise(self, op):
|
||||||
|
'''implements bitwise operations
|
||||||
|
'''
|
||||||
def wrapped(rhs):
|
def wrapped(rhs):
|
||||||
self._ensure_same_type(rhs)
|
self._ensure_same_type(rhs)
|
||||||
if not self.is_int:
|
if not self.is_int:
|
||||||
|
|
@ -608,6 +855,10 @@ class CValue(object):
|
||||||
return self._use_bitwise('xor')(rhs)
|
return self._use_bitwise('xor')(rhs)
|
||||||
|
|
||||||
def _ensure_same_type(self, val):
|
def _ensure_same_type(self, val):
|
||||||
|
'''ensure that this instance has the same type as `val`
|
||||||
|
|
||||||
|
Raises TypeError if `self.type != val.type`
|
||||||
|
'''
|
||||||
if self.type != val.type:
|
if self.type != val.type:
|
||||||
errmsg = "Type mismatch: %s != %s"
|
errmsg = "Type mismatch: %s != %s"
|
||||||
raise TypeError(errmsg % (self.type, val.type))
|
raise TypeError(errmsg % (self.type, val.type))
|
||||||
|
|
@ -621,6 +872,10 @@ class CValue(object):
|
||||||
return _is_real(self.type)
|
return _is_real(self.type)
|
||||||
|
|
||||||
def cast(self, ty, unsigned=False):
|
def cast(self, ty, unsigned=False):
|
||||||
|
'''cast to another type
|
||||||
|
|
||||||
|
If `ty == self.type`, then pass thru
|
||||||
|
'''
|
||||||
make = lambda X: CTemp(self.parent, X)
|
make = lambda X: CTemp(self.parent, X)
|
||||||
if self.type == ty:
|
if self.type == ty:
|
||||||
return self # pass thru
|
return self # pass thru
|
||||||
|
|
@ -651,6 +906,8 @@ class CValue(object):
|
||||||
raise TypeError(errmsg % (self.type, ty))
|
raise TypeError(errmsg % (self.type, ty))
|
||||||
|
|
||||||
def _cmp_op(self, name):
|
def _cmp_op(self, name):
|
||||||
|
'''implements comparison operations
|
||||||
|
'''
|
||||||
def wrapped(rhs):
|
def wrapped(rhs):
|
||||||
make = lambda X: CTemp(self.parent, X)
|
make = lambda X: CTemp(self.parent, X)
|
||||||
self._ensure_same_type(rhs)
|
self._ensure_same_type(rhs)
|
||||||
|
|
@ -700,6 +957,10 @@ class CValue(object):
|
||||||
raise TypeError("Must be a pointer; got %s" % self.type)
|
raise TypeError("Must be a pointer; got %s" % self.type)
|
||||||
|
|
||||||
def __getitem__(self, idx):
|
def __getitem__(self, idx):
|
||||||
|
'''implement access indexing
|
||||||
|
|
||||||
|
Uses GEP.
|
||||||
|
'''
|
||||||
self._ensure_is_pointer()
|
self._ensure_is_pointer()
|
||||||
if not isinstance(idx, CValue):
|
if not isinstance(idx, CValue):
|
||||||
idx = self.parent.constant(types.int, idx)
|
idx = self.parent.constant(types.int, idx)
|
||||||
|
|
@ -708,15 +969,26 @@ class CValue(object):
|
||||||
return CVar(self.parent, ptr)
|
return CVar(self.parent, ptr)
|
||||||
|
|
||||||
def load(self, volatile=False):
|
def load(self, volatile=False):
|
||||||
|
'''memory load for pointer types
|
||||||
|
'''
|
||||||
self._ensure_is_pointer()
|
self._ensure_is_pointer()
|
||||||
loaded = self.parent.builder.load(self.value, volatile=volatile)
|
loaded = self.parent.builder.load(self.value, volatile=volatile)
|
||||||
return CTemp(self.parent, loaded)
|
return CTemp(self.parent, loaded)
|
||||||
|
|
||||||
def store(self, val, volatile=False):
|
def store(self, val, volatile=False):
|
||||||
|
'''memory store for pointer types
|
||||||
|
'''
|
||||||
self._ensure_is_pointer()
|
self._ensure_is_pointer()
|
||||||
self.parent.builder.store(val.value, self.value, volatile=volatile)
|
self.parent.builder.store(val.value, self.value, volatile=volatile)
|
||||||
|
|
||||||
def atomic_load(self, ordering, align=None, crossthread=True):
|
def atomic_load(self, ordering, align=None, crossthread=True):
|
||||||
|
'''atomic load memory for pointer types
|
||||||
|
|
||||||
|
align : overide to control memory alignment; otherwise the default
|
||||||
|
alignment of the type is used.
|
||||||
|
|
||||||
|
Other parameters are the same as `CBuilder.atomic_load`
|
||||||
|
'''
|
||||||
self._ensure_is_pointer()
|
self._ensure_is_pointer()
|
||||||
if align is None:
|
if align is None:
|
||||||
align = self.parent.alignment(self.type.pointee)
|
align = self.parent.alignment(self.type.pointee)
|
||||||
|
|
@ -725,6 +997,13 @@ class CValue(object):
|
||||||
return CTemp(self.parent, inst)
|
return CTemp(self.parent, inst)
|
||||||
|
|
||||||
def atomic_store(self, value, ordering, align=None, crossthread=True):
|
def atomic_store(self, value, ordering, align=None, crossthread=True):
|
||||||
|
'''atomic memory store for pointer types
|
||||||
|
|
||||||
|
align : overide to control memory alignment; otherwise the default
|
||||||
|
alignment of the type is used.
|
||||||
|
|
||||||
|
Other parameters are the same as `CBuilder.atomic_store`
|
||||||
|
'''
|
||||||
self._ensure_is_pointer()
|
self._ensure_is_pointer()
|
||||||
if align is None:
|
if align is None:
|
||||||
align = self.parent.alignment(self.type.pointee)
|
align = self.parent.alignment(self.type.pointee)
|
||||||
|
|
@ -732,6 +1011,10 @@ class CValue(object):
|
||||||
align=align, crossthread=crossthread)
|
align=align, crossthread=crossthread)
|
||||||
|
|
||||||
def atomic_cmpxchg(self, old, new, ordering, crossthread=True):
|
def atomic_cmpxchg(self, old, new, ordering, crossthread=True):
|
||||||
|
'''atomic compare-exchange for pointer types
|
||||||
|
|
||||||
|
Other parameters are the same as `CBuilder.atomic_cmpxchg`
|
||||||
|
'''
|
||||||
self._ensure_is_pointer()
|
self._ensure_is_pointer()
|
||||||
inst = self.parent.builder.atomic_cmpxchg(self.value, old.value,
|
inst = self.parent.builder.atomic_cmpxchg(self.value, old.value,
|
||||||
new.value, ordering,
|
new.value, ordering,
|
||||||
|
|
@ -740,11 +1023,17 @@ class CValue(object):
|
||||||
|
|
||||||
|
|
||||||
class CFunc(CValue):
|
class CFunc(CValue):
|
||||||
|
'''Wraps function pointer
|
||||||
|
'''
|
||||||
def __init__(self, parent, func):
|
def __init__(self, parent, func):
|
||||||
super(CFunc, self).__init__(parent)
|
super(CFunc, self).__init__(parent)
|
||||||
self.function = func
|
self.function = func
|
||||||
|
|
||||||
def __call__(self, *args):
|
def __call__(self, *args):
|
||||||
|
'''Call the function with the given arguments
|
||||||
|
|
||||||
|
*args : variable arguments of CValue instances
|
||||||
|
'''
|
||||||
arg_values = _list_values(args)
|
arg_values = _list_values(args)
|
||||||
ftype = self.function.type.pointee
|
ftype = self.function.type.pointee
|
||||||
for i, (exp, got) in enumerate(zip(ftype.args, arg_values)):
|
for i, (exp, got) in enumerate(zip(ftype.args, arg_values)):
|
||||||
|
|
@ -764,6 +1053,8 @@ class CFunc(CValue):
|
||||||
return self.function.type
|
return self.function.type
|
||||||
|
|
||||||
class CTemp(CValue):
|
class CTemp(CValue):
|
||||||
|
'''Wraps temporary values
|
||||||
|
'''
|
||||||
def __init__(self, parent, value):
|
def __init__(self, parent, value):
|
||||||
super(CTemp, self).__init__(parent)
|
super(CTemp, self).__init__(parent)
|
||||||
self.value = value
|
self.value = value
|
||||||
|
|
@ -773,6 +1064,10 @@ class CTemp(CValue):
|
||||||
return self.value.type
|
return self.value.type
|
||||||
|
|
||||||
class CVar(CValue):
|
class CVar(CValue):
|
||||||
|
'''Wraps variables
|
||||||
|
|
||||||
|
Similar to C variables.
|
||||||
|
'''
|
||||||
|
|
||||||
def __init__(self, parent, ptr):
|
def __init__(self, parent, ptr):
|
||||||
super(CVar, self).__init__(parent)
|
super(CVar, self).__init__(parent)
|
||||||
|
|
@ -827,6 +1122,8 @@ class CVar(CValue):
|
||||||
return self.parent.builder.load(self.ptr)
|
return self.parent.builder.load(self.ptr)
|
||||||
|
|
||||||
def assign(self, val):
|
def assign(self, val):
|
||||||
|
'''assign new value to the variable
|
||||||
|
'''
|
||||||
self._ensure_same_type(val)
|
self._ensure_same_type(val)
|
||||||
self.parent.builder.store(val.value, self.ptr)
|
self.parent.builder.store(val.value, self.ptr)
|
||||||
|
|
||||||
|
|
@ -835,9 +1132,13 @@ class CVar(CValue):
|
||||||
return self.ptr.type.pointee
|
return self.ptr.type.pointee
|
||||||
|
|
||||||
def reference(self):
|
def reference(self):
|
||||||
|
'''get a pointer reference of the variable
|
||||||
|
'''
|
||||||
return CTemp(self.parent, self.ptr)
|
return CTemp(self.parent, self.ptr)
|
||||||
|
|
||||||
def as_struct(self, cstruct_class, volatile=False):
|
def as_struct(self, cstruct_class, volatile=False):
|
||||||
|
'''load a pointer to a structure and assume a structure interface
|
||||||
|
'''
|
||||||
if _is_pointer(self.type):
|
if _is_pointer(self.type):
|
||||||
ptr = self.parent.builder.load(self.ptr, volatile=volatile)
|
ptr = self.parent.builder.load(self.ptr, volatile=volatile)
|
||||||
return cstruct_class(self.parent, ptr)
|
return cstruct_class(self.parent, ptr)
|
||||||
|
|
@ -845,6 +1146,10 @@ class CVar(CValue):
|
||||||
return cstruct_class(self.parent, self.ptr)
|
return cstruct_class(self.parent, self.ptr)
|
||||||
|
|
||||||
class CArray(CValue):
|
class CArray(CValue):
|
||||||
|
'''wraps a array
|
||||||
|
|
||||||
|
Similar to C arrays
|
||||||
|
'''
|
||||||
def __init__(self, parent, base):
|
def __init__(self, parent, base):
|
||||||
super(CArray, self).__init__(parent)
|
super(CArray, self).__init__(parent)
|
||||||
self.base_ptr = base
|
self.base_ptr = base
|
||||||
|
|
@ -860,18 +1165,17 @@ class CArray(CValue):
|
||||||
def type(self):
|
def type(self):
|
||||||
return self.base_ptr.type
|
return self.base_ptr.type
|
||||||
|
|
||||||
## Moved to CValue
|
|
||||||
# def __getitem__(self, idx):
|
|
||||||
# self._ensure_is_pointer()
|
|
||||||
# builder = self.parent.builder
|
|
||||||
# if isinstance(idx, CValue):
|
|
||||||
# idx = idx.value
|
|
||||||
# elif not isinstance(idx, lc.Value):
|
|
||||||
# idx = self.parent.constant(types.int, idx).value
|
|
||||||
# ptr = builder.gep(self.value, [idx])
|
|
||||||
# return CVar(self.parent, ptr)
|
|
||||||
|
|
||||||
class CStruct(CValue):
|
class CStruct(CValue):
|
||||||
|
'''Wraps a structure
|
||||||
|
|
||||||
|
Structure in LLVM can be identified by name of layout.
|
||||||
|
|
||||||
|
Subclass to define a new structure. All fields are defined in the
|
||||||
|
`_fields_` class attribute as a list of tuple (name, type).
|
||||||
|
|
||||||
|
Can define new methods which gets inlined to the parent CBuilder.
|
||||||
|
'''
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def llvm_type(cls):
|
def llvm_type(cls):
|
||||||
return lc.Type.struct([v for k, v in cls._fields_])
|
return lc.Type.struct([v for k, v in cls._fields_])
|
||||||
|
|
@ -883,12 +1187,19 @@ class CStruct(CValue):
|
||||||
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)])
|
||||||
gep.name = "%s.%s" % (type(self).__name__, fd)
|
gep.name = "%s.%s" % (type(self).__name__, fd)
|
||||||
|
if hasattr(self, fd):
|
||||||
|
raise AttributeError("Field name shadows another attribute")
|
||||||
setattr(self, fd, CVar(self.parent, gep))
|
setattr(self, fd, CVar(self.parent, gep))
|
||||||
|
|
||||||
def reference(self):
|
def reference(self):
|
||||||
return CTemp(self.parent, self.ptr)
|
return CTemp(self.parent, self.ptr)
|
||||||
|
|
||||||
class CExternal(object):
|
class CExternal(object):
|
||||||
|
'''subclass to define external interface
|
||||||
|
|
||||||
|
All class attributes that are `llvm.core.FunctionType` are converted
|
||||||
|
to `CFunc` instance during instantiation.
|
||||||
|
'''
|
||||||
def __init__(self, cbuilder):
|
def __init__(self, cbuilder):
|
||||||
is_func = lambda x: isinstance(x, lc.FunctionType)
|
is_func = lambda x: isinstance(x, lc.FunctionType)
|
||||||
non_magic = lambda s: not ( s.startswith('__') and s.endswith('__') )
|
non_magic = lambda s: not ( s.startswith('__') and s.endswith('__') )
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
'''
|
||||||
|
This is mostly a convenience module for testing with ctypes.
|
||||||
|
'''
|
||||||
|
|
||||||
from llvm.core import Type, Module
|
from llvm.core import Type, Module
|
||||||
import llvm.ee as le
|
import llvm.ee as le
|
||||||
import ctypes as ct
|
import ctypes as ct
|
||||||
|
|
@ -29,6 +33,8 @@ MAP_CTYPES = {
|
||||||
}
|
}
|
||||||
|
|
||||||
class CExecutor(object):
|
class CExecutor(object):
|
||||||
|
'''a convenient class for creating ctype functions from LLVM modules
|
||||||
|
'''
|
||||||
def __init__(self, mod_or_engine):
|
def __init__(self, mod_or_engine):
|
||||||
if isinstance(mod_or_engine, Module):
|
if isinstance(mod_or_engine, Module):
|
||||||
self.engine = le.EngineBuilder.new(mod_or_engine).opt(3).create()
|
self.engine = le.EngineBuilder.new(mod_or_engine).opt(3).create()
|
||||||
|
|
@ -36,6 +42,14 @@ class CExecutor(object):
|
||||||
self.engine = mod_or_engine
|
self.engine = mod_or_engine
|
||||||
|
|
||||||
def get_ctype_function(self, fn, *typeinfo):
|
def get_ctype_function(self, fn, *typeinfo):
|
||||||
|
'''create a ctype function from a LLVM function
|
||||||
|
|
||||||
|
typeinfo : string of types (see `MAP_CTYPES`) or
|
||||||
|
list of ctypes datatype.
|
||||||
|
First value is the return type.
|
||||||
|
A function that takes no argument and return nothing
|
||||||
|
should use `"void"` or `None`.
|
||||||
|
'''
|
||||||
if len(typeinfo)==1 and isinstance(typeinfo[0], str):
|
if len(typeinfo)==1 and isinstance(typeinfo[0], str):
|
||||||
types = [ MAP_CTYPES[s.strip()] for s in typeinfo[0].split(',') ]
|
types = [ MAP_CTYPES[s.strip()] for s in typeinfo[0].split(',') ]
|
||||||
if not types:
|
if not types:
|
||||||
|
|
|
||||||
8
llvm_cbuilder/libc.py
Normal file
8
llvm_cbuilder/libc.py
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
from .builder import CExternal
|
||||||
|
import llvm.core as lc
|
||||||
|
from . import shortnames as types
|
||||||
|
|
||||||
|
class LibC(CExternal):
|
||||||
|
printf = lc.Type.function(types.int, [types.char_p], True)
|
||||||
|
# TODO a lot more to add
|
||||||
|
|
||||||
17
tests/test_cstr_collide.py
Normal file
17
tests/test_cstr_collide.py
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
from llvm.core import *
|
||||||
|
from llvm_cbuilder import *
|
||||||
|
from llvm_cbuilder import shortnames as C
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
class TestCstrCollide(unittest.TestCase):
|
||||||
|
def test_same_string(self):
|
||||||
|
mod = Module.new(__name__)
|
||||||
|
cb = CBuilder.new_function(mod, 'test_cstr_collide', C.void, [])
|
||||||
|
|
||||||
|
a = cb.constant_string("hello")
|
||||||
|
b = cb.constant_string("hello")
|
||||||
|
self.assertEqual(a.value, b.value)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue