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 . import shortnames as types
|
||||
|
||||
###
|
||||
# Utilities
|
||||
###
|
||||
|
||||
def _is_int(ty):
|
||||
return isinstance(ty, lc.IntegerType)
|
||||
|
||||
|
|
@ -47,6 +51,19 @@ def _change_block_temporarily_dummy(*args):
|
|||
yield
|
||||
|
||||
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):
|
||||
self.parent = parent
|
||||
self.cond = cond
|
||||
|
|
@ -84,6 +101,22 @@ class _IfElse(object):
|
|||
builder.position_at_end(bbend)
|
||||
|
||||
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):
|
||||
self.parent = parent
|
||||
|
||||
|
|
@ -107,9 +140,8 @@ class _Loop(object):
|
|||
def body(self):
|
||||
builder = self.parent.builder
|
||||
builder.position_at_end(self._bbbody)
|
||||
|
||||
yield self
|
||||
|
||||
# close last block
|
||||
if not _is_block_terminated(builder.basic_block):
|
||||
builder.branch(self._bbcond)
|
||||
|
||||
|
|
@ -121,9 +153,6 @@ class _Loop(object):
|
|||
|
||||
def close(self):
|
||||
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)
|
||||
|
||||
class CBuilder(object):
|
||||
|
|
@ -131,14 +160,18 @@ class CBuilder(object):
|
|||
A wrapper class for features in llvm-py package
|
||||
to allow user to use C-like high-level language contruct easily.
|
||||
'''
|
||||
|
||||
def __init__(self, function):
|
||||
'''constructor
|
||||
|
||||
function : is an empty function to be populating.
|
||||
'''
|
||||
self.function = function
|
||||
self.declare_block = self.function.append_basic_block('decl')
|
||||
self.first_body_block = self.function.append_basic_block('body')
|
||||
self.builder = lc.Builder.new(self.first_body_block)
|
||||
self.target_data = le.TargetData.new(self.function.module.data_layout)
|
||||
|
||||
# prepare arguments
|
||||
# Prepare arguments. Make all function arguments behave like variables.
|
||||
self.args = []
|
||||
for arg in function.args:
|
||||
var = self.var(arg.type, arg, name=arg.name)
|
||||
|
|
@ -146,21 +179,40 @@ class CBuilder(object):
|
|||
|
||||
@staticmethod
|
||||
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)
|
||||
func = mod.add_function(functype, name=name)
|
||||
return CBuilder(func)
|
||||
|
||||
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))
|
||||
|
||||
def printf(self, fmt, *args):
|
||||
mod = self.function.module
|
||||
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))
|
||||
'''printf() from libc
|
||||
|
||||
fmt : a character string holding printf format string.
|
||||
*args : additional variable arguments.
|
||||
'''
|
||||
from .libc import LibC
|
||||
libc = LibC(self)
|
||||
ret = libc.printf(fmt, *args)
|
||||
return CTemp(self, ret)
|
||||
|
||||
def debug(self, *args):
|
||||
'''debug print
|
||||
|
||||
Use printf to dump the values of all arguments.
|
||||
'''
|
||||
type_mapper = {
|
||||
'i8' : '%c',
|
||||
'i16': '%hd',
|
||||
|
|
@ -174,17 +226,27 @@ class CBuilder(object):
|
|||
if isinstance(i, str):
|
||||
itemsfmt.append(i.replace('%', '%%'))
|
||||
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)
|
||||
items.append(i)
|
||||
fmt = ' '.join(itemsfmt) + '\n'
|
||||
return self.printf(self.constant_string(fmt), *items)
|
||||
|
||||
def var(self, ty, value=None, name=''):
|
||||
'''
|
||||
Only allocate in the first block
|
||||
'''allocate variable on the stack
|
||||
|
||||
ty : variable type
|
||||
value : [optional] initializer value
|
||||
name : [optional] name used in LLVM IR
|
||||
'''
|
||||
with _change_block_temporarily(self.builder, self.declare_block):
|
||||
# goto the first block
|
||||
is_cstruct = _is_cstruct(ty)
|
||||
if is_cstruct:
|
||||
cstruct = ty
|
||||
|
|
@ -203,27 +265,45 @@ class CBuilder(object):
|
|||
return CVar(self, ptr)
|
||||
|
||||
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)
|
||||
|
||||
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):
|
||||
# Only go to the first block if array size is fixed.
|
||||
contexthelper = _change_block_temporarily
|
||||
else:
|
||||
# Do not go to the first block if the array size is dynamic.
|
||||
contexthelper = _change_block_temporarily_dummy
|
||||
|
||||
with contexthelper(self.builder, self.declare_block):
|
||||
is_cstruct = _is_cstruct(ty)
|
||||
if is_cstruct:
|
||||
if _is_cstruct(ty): # array of struct?
|
||||
cstruct = ty
|
||||
ty = ty.llvm_type()
|
||||
|
||||
if isinstance(count, CValue):
|
||||
count = count.value
|
||||
elif not isinstance(count, lc.Value):
|
||||
count = self.constant(types.int, count).value
|
||||
|
||||
ptr = self.builder.alloca_array(ty, count, name=name)
|
||||
return CArray(self, ptr)
|
||||
|
||||
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
|
||||
if val is not None:
|
||||
if val.type != retty:
|
||||
|
|
@ -238,18 +318,26 @@ class CBuilder(object):
|
|||
|
||||
@contextlib.contextmanager
|
||||
def ifelse(self, cond):
|
||||
'''start a if-else block
|
||||
|
||||
cond : branch condition
|
||||
'''
|
||||
cb = _IfElse(self, cond)
|
||||
yield cb
|
||||
cb.close()
|
||||
|
||||
@contextlib.contextmanager
|
||||
def loop(self):
|
||||
'''start a loop block
|
||||
'''
|
||||
cb = _Loop(self)
|
||||
yield cb
|
||||
cb.close()
|
||||
|
||||
@contextlib.contextmanager
|
||||
def forever(self):
|
||||
'''start a forever loop block
|
||||
'''
|
||||
with self.loop() as loop:
|
||||
with loop.condition() as setcond:
|
||||
NULL = self.constant_null(types.int)
|
||||
|
|
@ -259,6 +347,10 @@ class CBuilder(object):
|
|||
|
||||
@contextlib.contextmanager
|
||||
def for_range(self, *args):
|
||||
'''start a for-range block.
|
||||
|
||||
*args : same as arguments of builtin `range()`
|
||||
'''
|
||||
def check_arg(x):
|
||||
if isinstance(x, int):
|
||||
return self.constant(types.int, x)
|
||||
|
|
@ -287,15 +379,26 @@ class CBuilder(object):
|
|||
idx += step
|
||||
|
||||
def position_at_end(self, bb):
|
||||
'''reposition inserter to the end of basic-block
|
||||
|
||||
bb : a basic block
|
||||
'''
|
||||
self.basic_block = bb
|
||||
self.builder.position_at_end(bb)
|
||||
|
||||
def close(self):
|
||||
'''end code generation
|
||||
'''
|
||||
# Close declaration block
|
||||
with _change_block_temporarily(self.builder, self.declare_block):
|
||||
self.builder.branch(self.first_body_block)
|
||||
|
||||
def constant(self, ty, val):
|
||||
'''create a constant
|
||||
|
||||
ty : data type
|
||||
val : initializer
|
||||
'''
|
||||
if isinstance(ty, lc.IntegerType):
|
||||
res = lc.Constant.int(ty, val)
|
||||
elif ty == types.float or ty == types.double:
|
||||
|
|
@ -306,119 +409,239 @@ class CBuilder(object):
|
|||
return CTemp(self, res)
|
||||
|
||||
def constant_null(self, ty):
|
||||
'''create a zero filled constant
|
||||
|
||||
ty : data type
|
||||
'''
|
||||
res = lc.Constant.null(ty)
|
||||
return CTemp(self, res)
|
||||
|
||||
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
|
||||
# TODO
|
||||
# Is this a safe way for uniquing identifying string
|
||||
name = '.conststr.%x_%x' % (hash(string), len(string))
|
||||
collision = 0
|
||||
name_fmt = '.conststr.%x.%x'
|
||||
content = lc.Constant.stringz(string)
|
||||
try:
|
||||
globalstr = mod.get_global_variable_named(name)
|
||||
except LLVMException:
|
||||
globalstr = mod.add_global_variable(content.type, name=name)
|
||||
globalstr.initializer = content
|
||||
return CTemp(self, globalstr.bitcast(
|
||||
types.pointer(content.type.element)))
|
||||
while True:
|
||||
name = name_fmt % (hash(string), collision)
|
||||
try:
|
||||
# check if the name already exists
|
||||
globalstr = mod.get_global_variable_named(name)
|
||||
except LLVMException:
|
||||
# new constant string
|
||||
globalstr = mod.add_global_variable(content.type, name=name)
|
||||
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(
|
||||
types.pointer(content.type.element)))
|
||||
|
||||
|
||||
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)
|
||||
return CFunc(self, lfunc)
|
||||
|
||||
def get_function_named(self, name):
|
||||
'''get function by name
|
||||
'''
|
||||
m = self.function.module
|
||||
func = m.get_function_named(name)
|
||||
return CFunc(self, func)
|
||||
|
||||
def is_terminated(self):
|
||||
'''
|
||||
Is the current basic-block terminated?
|
||||
'''is the current basic-block terminated?
|
||||
'''
|
||||
return _is_block_terminated(self.builder.basic_block)
|
||||
|
||||
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,
|
||||
ordering, crossthread)
|
||||
return CTemp(self, res)
|
||||
|
||||
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,
|
||||
ordering, crossthread)
|
||||
return CTemp(self, res)
|
||||
|
||||
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,
|
||||
ordering, crossthread)
|
||||
return CTemp(self, res)
|
||||
|
||||
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,
|
||||
ordering, crossthread)
|
||||
return CTemp(self, res)
|
||||
|
||||
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,
|
||||
ordering, crossthread)
|
||||
return CTemp(self, res)
|
||||
|
||||
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,
|
||||
ordering, crossthread)
|
||||
return CTemp(self, res)
|
||||
|
||||
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,
|
||||
ordering, crossthread)
|
||||
return CTemp(self, res)
|
||||
|
||||
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,
|
||||
ordering, crossthread)
|
||||
return CTemp(self, res)
|
||||
|
||||
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,
|
||||
ordering, crossthread)
|
||||
return CTemp(self, res)
|
||||
|
||||
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,
|
||||
ordering, crossthread)
|
||||
return CTemp(self, res)
|
||||
|
||||
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,
|
||||
ordering, crossthread)
|
||||
return CTemp(self, res)
|
||||
|
||||
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,
|
||||
ordering, crossthread)
|
||||
return CTemp(self, res)
|
||||
|
||||
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)
|
||||
return CTemp(self, res)
|
||||
|
||||
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,
|
||||
align, crossthread)
|
||||
return CTemp(self, res)
|
||||
|
||||
def fence(self, ordering, crossthread=True):
|
||||
'''insert memory fence
|
||||
'''
|
||||
res = self.builder.fence(ordering, crossthread)
|
||||
return CTemp(self, res)
|
||||
|
||||
def alignment(self, ty):
|
||||
'''get minimum alignment of `ty`
|
||||
'''
|
||||
return self.target_data.abi_alignment(ty)
|
||||
|
||||
def unreachable(self):
|
||||
'''insert instruction that causes segfault some platform (Intel),
|
||||
or no-op on others.
|
||||
|
||||
It has no defined semantic.
|
||||
'''
|
||||
self.builder.unreachable()
|
||||
|
||||
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):
|
||||
self.cdef = cdef
|
||||
|
||||
|
|
@ -433,6 +656,9 @@ class _DeclareCDef(object):
|
|||
return func
|
||||
|
||||
class CDeclare(object):
|
||||
'''create a function from name, type and pointer to use with
|
||||
`CBuilder.depends`
|
||||
'''
|
||||
def __init__(self, name, ty, ptr):
|
||||
self._name = name
|
||||
self._type = ty
|
||||
|
|
@ -448,33 +674,51 @@ class CDeclare(object):
|
|||
return self._name
|
||||
|
||||
class CDefinition(CBuilder):
|
||||
'''
|
||||
Inherit this class to for defining functions
|
||||
'''represents function definition
|
||||
|
||||
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
|
||||
_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:
|
||||
if cls.is_generic():
|
||||
# Call specialize if it is defined.
|
||||
cls = type('%s_Specialized' % cls.__name__, (cls,), {})
|
||||
fnsp(*args, **kws)
|
||||
cls.specialize(*args, **kws)
|
||||
|
||||
obj = object.__new__(_DeclareCDef)
|
||||
obj.__init__(cls)
|
||||
return obj
|
||||
|
||||
@classmethod
|
||||
def is_generic(cls):
|
||||
'''Is this a generic definition?
|
||||
'''
|
||||
return hasattr(cls, 'specialize')
|
||||
|
||||
@classmethod
|
||||
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_])
|
||||
name = cls._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)
|
||||
|
||||
if not func.is_declaration: # already defined?
|
||||
raise NameError(func)
|
||||
|
||||
|
|
@ -490,25 +734,24 @@ class CDefinition(CBuilder):
|
|||
return func
|
||||
|
||||
def body(self):
|
||||
'''
|
||||
Overide this function to define the body.
|
||||
'''overide this function to define the body.
|
||||
'''
|
||||
raise NotImplementedError
|
||||
|
||||
class CValue(object):
|
||||
'''
|
||||
= Signess =
|
||||
Since LLVM type does not provide signess attribute. This information
|
||||
Signedness
|
||||
----------
|
||||
Since LLVM type does not provide signedness attribute. This information
|
||||
is provided in the CValue.unsigned attribute. The default value is
|
||||
`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
|
||||
unsigned = None # attribute for for integer values.
|
||||
|
||||
_BINOP_MAP = {
|
||||
# op-name : (signed int, unsigned int, real)
|
||||
|
|
@ -542,6 +785,8 @@ class CValue(object):
|
|||
self.parent = parent
|
||||
|
||||
def _use_binop(self, op):
|
||||
'''implements binary operations
|
||||
'''
|
||||
def wrapped(rhs):
|
||||
self._ensure_same_type(rhs)
|
||||
binop = self._BINOP_MAP[op]
|
||||
|
|
@ -560,6 +805,8 @@ class CValue(object):
|
|||
return wrapped
|
||||
|
||||
def _use_bitwise(self, op):
|
||||
'''implements bitwise operations
|
||||
'''
|
||||
def wrapped(rhs):
|
||||
self._ensure_same_type(rhs)
|
||||
if not self.is_int:
|
||||
|
|
@ -608,6 +855,10 @@ class CValue(object):
|
|||
return self._use_bitwise('xor')(rhs)
|
||||
|
||||
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:
|
||||
errmsg = "Type mismatch: %s != %s"
|
||||
raise TypeError(errmsg % (self.type, val.type))
|
||||
|
|
@ -621,6 +872,10 @@ class CValue(object):
|
|||
return _is_real(self.type)
|
||||
|
||||
def cast(self, ty, unsigned=False):
|
||||
'''cast to another type
|
||||
|
||||
If `ty == self.type`, then pass thru
|
||||
'''
|
||||
make = lambda X: CTemp(self.parent, X)
|
||||
if self.type == ty:
|
||||
return self # pass thru
|
||||
|
|
@ -651,6 +906,8 @@ class CValue(object):
|
|||
raise TypeError(errmsg % (self.type, ty))
|
||||
|
||||
def _cmp_op(self, name):
|
||||
'''implements comparison operations
|
||||
'''
|
||||
def wrapped(rhs):
|
||||
make = lambda X: CTemp(self.parent, X)
|
||||
self._ensure_same_type(rhs)
|
||||
|
|
@ -700,6 +957,10 @@ class CValue(object):
|
|||
raise TypeError("Must be a pointer; got %s" % self.type)
|
||||
|
||||
def __getitem__(self, idx):
|
||||
'''implement access indexing
|
||||
|
||||
Uses GEP.
|
||||
'''
|
||||
self._ensure_is_pointer()
|
||||
if not isinstance(idx, CValue):
|
||||
idx = self.parent.constant(types.int, idx)
|
||||
|
|
@ -708,15 +969,26 @@ class CValue(object):
|
|||
return CVar(self.parent, ptr)
|
||||
|
||||
def load(self, volatile=False):
|
||||
'''memory load for pointer types
|
||||
'''
|
||||
self._ensure_is_pointer()
|
||||
loaded = self.parent.builder.load(self.value, volatile=volatile)
|
||||
return CTemp(self.parent, loaded)
|
||||
|
||||
def store(self, val, volatile=False):
|
||||
'''memory store for pointer types
|
||||
'''
|
||||
self._ensure_is_pointer()
|
||||
self.parent.builder.store(val.value, self.value, volatile=volatile)
|
||||
|
||||
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()
|
||||
if align is None:
|
||||
align = self.parent.alignment(self.type.pointee)
|
||||
|
|
@ -725,6 +997,13 @@ class CValue(object):
|
|||
return CTemp(self.parent, inst)
|
||||
|
||||
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()
|
||||
if align is None:
|
||||
align = self.parent.alignment(self.type.pointee)
|
||||
|
|
@ -732,6 +1011,10 @@ class CValue(object):
|
|||
align=align, crossthread=crossthread)
|
||||
|
||||
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()
|
||||
inst = self.parent.builder.atomic_cmpxchg(self.value, old.value,
|
||||
new.value, ordering,
|
||||
|
|
@ -740,11 +1023,17 @@ class CValue(object):
|
|||
|
||||
|
||||
class CFunc(CValue):
|
||||
'''Wraps function pointer
|
||||
'''
|
||||
def __init__(self, parent, func):
|
||||
super(CFunc, self).__init__(parent)
|
||||
self.function = func
|
||||
|
||||
def __call__(self, *args):
|
||||
'''Call the function with the given arguments
|
||||
|
||||
*args : variable arguments of CValue instances
|
||||
'''
|
||||
arg_values = _list_values(args)
|
||||
ftype = self.function.type.pointee
|
||||
for i, (exp, got) in enumerate(zip(ftype.args, arg_values)):
|
||||
|
|
@ -764,6 +1053,8 @@ class CFunc(CValue):
|
|||
return self.function.type
|
||||
|
||||
class CTemp(CValue):
|
||||
'''Wraps temporary values
|
||||
'''
|
||||
def __init__(self, parent, value):
|
||||
super(CTemp, self).__init__(parent)
|
||||
self.value = value
|
||||
|
|
@ -773,6 +1064,10 @@ class CTemp(CValue):
|
|||
return self.value.type
|
||||
|
||||
class CVar(CValue):
|
||||
'''Wraps variables
|
||||
|
||||
Similar to C variables.
|
||||
'''
|
||||
|
||||
def __init__(self, parent, ptr):
|
||||
super(CVar, self).__init__(parent)
|
||||
|
|
@ -827,6 +1122,8 @@ class CVar(CValue):
|
|||
return self.parent.builder.load(self.ptr)
|
||||
|
||||
def assign(self, val):
|
||||
'''assign new value to the variable
|
||||
'''
|
||||
self._ensure_same_type(val)
|
||||
self.parent.builder.store(val.value, self.ptr)
|
||||
|
||||
|
|
@ -835,9 +1132,13 @@ class CVar(CValue):
|
|||
return self.ptr.type.pointee
|
||||
|
||||
def reference(self):
|
||||
'''get a pointer reference of the variable
|
||||
'''
|
||||
return CTemp(self.parent, self.ptr)
|
||||
|
||||
def as_struct(self, cstruct_class, volatile=False):
|
||||
'''load a pointer to a structure and assume a structure interface
|
||||
'''
|
||||
if _is_pointer(self.type):
|
||||
ptr = self.parent.builder.load(self.ptr, volatile=volatile)
|
||||
return cstruct_class(self.parent, ptr)
|
||||
|
|
@ -845,6 +1146,10 @@ class CVar(CValue):
|
|||
return cstruct_class(self.parent, self.ptr)
|
||||
|
||||
class CArray(CValue):
|
||||
'''wraps a array
|
||||
|
||||
Similar to C arrays
|
||||
'''
|
||||
def __init__(self, parent, base):
|
||||
super(CArray, self).__init__(parent)
|
||||
self.base_ptr = base
|
||||
|
|
@ -860,18 +1165,17 @@ class CArray(CValue):
|
|||
def type(self):
|
||||
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):
|
||||
'''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
|
||||
def llvm_type(cls):
|
||||
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_):
|
||||
gep = self.parent.builder.gep(ptr, [makeind(0), makeind(i)])
|
||||
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))
|
||||
|
||||
def reference(self):
|
||||
return CTemp(self.parent, self.ptr)
|
||||
|
||||
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):
|
||||
is_func = lambda x: isinstance(x, lc.FunctionType)
|
||||
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
|
||||
import llvm.ee as le
|
||||
import ctypes as ct
|
||||
|
|
@ -29,6 +33,8 @@ MAP_CTYPES = {
|
|||
}
|
||||
|
||||
class CExecutor(object):
|
||||
'''a convenient class for creating ctype functions from LLVM modules
|
||||
'''
|
||||
def __init__(self, mod_or_engine):
|
||||
if isinstance(mod_or_engine, Module):
|
||||
self.engine = le.EngineBuilder.new(mod_or_engine).opt(3).create()
|
||||
|
|
@ -36,6 +42,14 @@ class CExecutor(object):
|
|||
self.engine = mod_or_engine
|
||||
|
||||
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):
|
||||
types = [ MAP_CTYPES[s.strip()] for s in typeinfo[0].split(',') ]
|
||||
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