Test ABI for structures and reorganize tests a bit.
This commit is contained in:
parent
cc4e4631bd
commit
69a78d0a54
5 changed files with 392 additions and 24 deletions
|
|
@ -37,7 +37,7 @@ def test(verbosity=1):
|
||||||
|
|
||||||
Run self-test, and return the number of failures + errors
|
Run self-test, and return the number of failures + errors
|
||||||
"""
|
"""
|
||||||
from llvm.test_llvmpy import run
|
from llvm.tests import run
|
||||||
|
|
||||||
result = run(verbosity=verbosity)
|
result = run(verbosity=verbosity)
|
||||||
|
|
||||||
|
|
|
||||||
32
llvm/tests/__init__.py
Normal file
32
llvm/tests/__init__.py
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
from __future__ import print_function
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import unittest
|
||||||
|
import llvm
|
||||||
|
|
||||||
|
tests = []
|
||||||
|
|
||||||
|
def run(verbosity=1):
|
||||||
|
print('llvmpy is installed in: ' + os.path.dirname(__file__))
|
||||||
|
print('llvmpy version: ' + llvm.__version__)
|
||||||
|
print(sys.version)
|
||||||
|
|
||||||
|
files = filter(lambda s: s.startswith('test_') and s.endswith('.py'),
|
||||||
|
os.listdir(os.path.dirname(__file__)))
|
||||||
|
|
||||||
|
for f in files:
|
||||||
|
fname = f.split('.', 1)[0]
|
||||||
|
__import__('.'.join([__name__, fname]))
|
||||||
|
|
||||||
|
suite = unittest.TestSuite()
|
||||||
|
for cls in tests:
|
||||||
|
suite.addTest(unittest.makeSuite(cls))
|
||||||
|
|
||||||
|
# The default stream fails in IPython qtconsole on Windows,
|
||||||
|
# so just using sys.stdout
|
||||||
|
runner = unittest.TextTestRunner(verbosity=verbosity, stream=sys.stdout)
|
||||||
|
return runner.run(suite)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
|
|
||||||
|
|
@ -10,6 +10,7 @@ import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
import contextlib
|
import contextlib
|
||||||
from distutils.spawn import find_executable
|
from distutils.spawn import find_executable
|
||||||
|
from . import tests
|
||||||
|
|
||||||
is_py3k = sys.version_info[0] >= 3
|
is_py3k = sys.version_info[0] >= 3
|
||||||
BITS = tuple.__itemsize__ * 8
|
BITS = tuple.__itemsize__ * 8
|
||||||
|
|
@ -30,8 +31,6 @@ import llvm.ee as le
|
||||||
import llvmpy
|
import llvmpy
|
||||||
|
|
||||||
|
|
||||||
tests = []
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
if sys.version_info[:2] <= (2, 6):
|
if sys.version_info[:2] <= (2, 6):
|
||||||
|
|
@ -1582,23 +1581,3 @@ class TestExact(TestCase):
|
||||||
tests.append(TestExact)
|
tests.append(TestExact)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
def run(verbosity=1):
|
|
||||||
print('llvmpy is installed in: ' + os.path.dirname(__file__))
|
|
||||||
print('llvmpy version: ' + llvm.__version__)
|
|
||||||
print(sys.version)
|
|
||||||
|
|
||||||
suite = unittest.TestSuite()
|
|
||||||
for cls in tests:
|
|
||||||
suite.addTest(unittest.makeSuite(cls))
|
|
||||||
|
|
||||||
# The default stream fails in IPython qtconsole on Windows,
|
|
||||||
# so just using sys.stdout
|
|
||||||
runner = unittest.TextTestRunner(verbosity=verbosity, stream=sys.stdout)
|
|
||||||
return runner.run(suite)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
unittest.main()
|
|
||||||
356
llvm/tests/test_struct_args.py
Normal file
356
llvm/tests/test_struct_args.py
Normal file
|
|
@ -0,0 +1,356 @@
|
||||||
|
from __future__ import print_function
|
||||||
|
from . import tests
|
||||||
|
import unittest
|
||||||
|
from ctypes import Structure, c_float, c_double, c_uint8, CFUNCTYPE
|
||||||
|
from llvm import core as lc
|
||||||
|
from llvm import ee as le
|
||||||
|
|
||||||
|
class TwoDoubleOneByte(Structure):
|
||||||
|
_fields_ = ('x', c_double), ('y', c_double), ('z', c_uint8)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<x=%f y=%f z=%d>' % (self.x, self.y, self.z)
|
||||||
|
|
||||||
|
class TwoDouble(Structure):
|
||||||
|
_fields_ = ('x', c_double), ('y', c_double)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<x=%f y=%f>' % (self.x, self.y)
|
||||||
|
|
||||||
|
class TwoFloat(Structure):
|
||||||
|
_fields_ = ('x', c_float), ('y', c_float)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<x=%f y=%f>' % (self.x, self.y)
|
||||||
|
|
||||||
|
class OneByte(Structure):
|
||||||
|
_fields_ = [('x', c_uint8)]
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<x=%d>' % (self.x,)
|
||||||
|
|
||||||
|
TM = le.TargetMachine.new()
|
||||||
|
POINTER_BITSIZE = TM.target_data.pointer_size * 8
|
||||||
|
|
||||||
|
def skip_if_not_64bits(fn):
|
||||||
|
if POINTER_BITSIZE == 64:
|
||||||
|
return fn
|
||||||
|
|
||||||
|
def skip_if_not_32bits(fn):
|
||||||
|
if POINTER_BITSIZE == 32:
|
||||||
|
return fn
|
||||||
|
|
||||||
|
class FloatTestMixin(object):
|
||||||
|
def assertClose(self, got, expect):
|
||||||
|
rel = abs(got - expect) / float(expect)
|
||||||
|
self.assertTrue(rel < 1e-6, 'relative error = %f' % rel)
|
||||||
|
|
||||||
|
class TestStructABI(unittest.TestCase, FloatTestMixin):
|
||||||
|
@skip_if_not_64bits
|
||||||
|
def test_bigger_than_two_words_64(self):
|
||||||
|
m = lc.Module.new('test_struct_arg')
|
||||||
|
|
||||||
|
double_type = lc.Type.double()
|
||||||
|
uint8_type = lc.Type.int(8)
|
||||||
|
struct_type = lc.Type.struct([double_type, double_type, uint8_type])
|
||||||
|
struct_ptr_type = lc.Type.pointer(struct_type)
|
||||||
|
func_type = lc.Type.function(lc.Type.void(),
|
||||||
|
[struct_ptr_type, struct_ptr_type])
|
||||||
|
func = m.add_function(func_type, name='foo')
|
||||||
|
|
||||||
|
# return value pointer
|
||||||
|
func.args[0].add_attribute(lc.ATTR_STRUCT_RET)
|
||||||
|
|
||||||
|
# pass structure by value
|
||||||
|
func.args[1].add_attribute(lc.ATTR_BY_VAL)
|
||||||
|
|
||||||
|
# define function body
|
||||||
|
builder = lc.Builder.new(func.append_basic_block(''))
|
||||||
|
|
||||||
|
arg = builder.load(func.args[1])
|
||||||
|
e1, e2, e3 = [builder.extract_value(arg, i) for i in range(3)]
|
||||||
|
se1 = builder.fmul(e1, e2)
|
||||||
|
se2 = builder.fdiv(e1, e2)
|
||||||
|
ret = builder.insert_value(lc.Constant.undef(struct_type), se1, 0)
|
||||||
|
ret = builder.insert_value(ret, se2, 1)
|
||||||
|
ret = builder.insert_value(ret, e3, 2)
|
||||||
|
builder.store(ret, func.args[0])
|
||||||
|
builder.ret_void()
|
||||||
|
|
||||||
|
del builder
|
||||||
|
|
||||||
|
# verify
|
||||||
|
m.verify()
|
||||||
|
|
||||||
|
print(m)
|
||||||
|
# use with ctypes
|
||||||
|
engine = le.EngineBuilder.new(m).create()
|
||||||
|
ptr = engine.get_pointer_to_function(func)
|
||||||
|
|
||||||
|
cfunctype = CFUNCTYPE(TwoDoubleOneByte, TwoDoubleOneByte)
|
||||||
|
cfunc = cfunctype(ptr)
|
||||||
|
|
||||||
|
arg = TwoDoubleOneByte(x=1.321321, y=6.54352, z=128)
|
||||||
|
ret = cfunc(arg)
|
||||||
|
print(arg)
|
||||||
|
print(ret)
|
||||||
|
|
||||||
|
self.assertClose(arg.x * arg.y, ret.x)
|
||||||
|
self.assertClose(arg.x / arg.y, ret.y)
|
||||||
|
self.assertEqual(arg.z, ret.z)
|
||||||
|
|
||||||
|
@skip_if_not_64bits
|
||||||
|
def test_just_two_words_64(self):
|
||||||
|
m = lc.Module.new('test_struct_arg')
|
||||||
|
|
||||||
|
double_type = lc.Type.double()
|
||||||
|
struct_type = lc.Type.struct([double_type, double_type])
|
||||||
|
func_type = lc.Type.function(struct_type, [struct_type])
|
||||||
|
func = m.add_function(func_type, name='foo')
|
||||||
|
|
||||||
|
# define function body
|
||||||
|
builder = lc.Builder.new(func.append_basic_block(''))
|
||||||
|
|
||||||
|
arg = func.args[0]
|
||||||
|
e1, e2 = [builder.extract_value(arg, i) for i in range(2)]
|
||||||
|
se1 = builder.fmul(e1, e2)
|
||||||
|
se2 = builder.fdiv(e1, e2)
|
||||||
|
ret = builder.insert_value(lc.Constant.undef(struct_type), se1, 0)
|
||||||
|
ret = builder.insert_value(ret, se2, 1)
|
||||||
|
builder.ret(ret)
|
||||||
|
|
||||||
|
del builder
|
||||||
|
|
||||||
|
# verify
|
||||||
|
m.verify()
|
||||||
|
|
||||||
|
print(m)
|
||||||
|
# use with ctypes
|
||||||
|
engine = le.EngineBuilder.new(m).create()
|
||||||
|
ptr = engine.get_pointer_to_function(func)
|
||||||
|
|
||||||
|
cfunctype = CFUNCTYPE(TwoDouble, TwoDouble)
|
||||||
|
cfunc = cfunctype(ptr)
|
||||||
|
|
||||||
|
arg = TwoDouble(x=1.321321, y=6.54352)
|
||||||
|
ret = cfunc(arg)
|
||||||
|
print(arg)
|
||||||
|
print(ret)
|
||||||
|
|
||||||
|
self.assertClose(arg.x * arg.y, ret.x)
|
||||||
|
self.assertClose(arg.x / arg.y, ret.y)
|
||||||
|
|
||||||
|
@skip_if_not_64bits
|
||||||
|
def test_two_halfwords(self):
|
||||||
|
'''Arguments smaller or equal to a word is packed into a word.
|
||||||
|
|
||||||
|
Passing as struct { float, float } occupies two XMM registers instead
|
||||||
|
of one.
|
||||||
|
The output must be in XMM.
|
||||||
|
'''
|
||||||
|
m = lc.Module.new('test_struct_arg')
|
||||||
|
|
||||||
|
float_type = lc.Type.float()
|
||||||
|
struct_type = lc.Type.vector(float_type, 2)
|
||||||
|
print('ABI size',
|
||||||
|
TM.target_data.abi_size(lc.Type.struct([float_type, float_type])))
|
||||||
|
func_type = lc.Type.function(struct_type, [struct_type])
|
||||||
|
func = m.add_function(func_type, name='foo')
|
||||||
|
|
||||||
|
# define function body
|
||||||
|
builder = lc.Builder.new(func.append_basic_block(''))
|
||||||
|
|
||||||
|
arg = func.args[0]
|
||||||
|
constint = lambda x: lc.Constant.int(lc.Type.int(), x)
|
||||||
|
e1, e2 = [builder.extract_element(arg, constint(i))
|
||||||
|
for i in range(2)]
|
||||||
|
se1 = builder.fmul(e1, e2)
|
||||||
|
se2 = builder.fdiv(e1, e2)
|
||||||
|
ret = builder.insert_element(lc.Constant.undef(struct_type), se1,
|
||||||
|
constint(0))
|
||||||
|
ret = builder.insert_element(ret, se2, constint(1))
|
||||||
|
builder.ret(ret)
|
||||||
|
|
||||||
|
del builder
|
||||||
|
|
||||||
|
# verify
|
||||||
|
m.verify()
|
||||||
|
|
||||||
|
print(m)
|
||||||
|
# use with ctypes
|
||||||
|
engine = le.EngineBuilder.new(m).create()
|
||||||
|
ptr = engine.get_pointer_to_function(func)
|
||||||
|
|
||||||
|
cfunctype = CFUNCTYPE(TwoFloat, TwoFloat)
|
||||||
|
cfunc = cfunctype(ptr)
|
||||||
|
|
||||||
|
arg = TwoFloat(x=1.321321, y=6.54352)
|
||||||
|
ret = cfunc(arg)
|
||||||
|
print(arg)
|
||||||
|
print(ret)
|
||||||
|
|
||||||
|
self.assertClose(arg.x * arg.y, ret.x)
|
||||||
|
self.assertClose(arg.x / arg.y, ret.y)
|
||||||
|
|
||||||
|
@skip_if_not_32bits
|
||||||
|
def test_structure_abi_32_1(self):
|
||||||
|
'''x86 is simple. Always pass structure as memory.
|
||||||
|
'''
|
||||||
|
m = lc.Module.new('test_struct_arg')
|
||||||
|
|
||||||
|
double_type = lc.Type.double()
|
||||||
|
uint8_type = lc.Type.int(8)
|
||||||
|
struct_type = lc.Type.struct([double_type, double_type, uint8_type])
|
||||||
|
struct_ptr_type = lc.Type.pointer(struct_type)
|
||||||
|
func_type = lc.Type.function(lc.Type.void(),
|
||||||
|
[struct_ptr_type, struct_ptr_type])
|
||||||
|
func = m.add_function(func_type, name='foo')
|
||||||
|
|
||||||
|
# return value pointer
|
||||||
|
func.args[0].add_attribute(lc.ATTR_STRUCT_RET)
|
||||||
|
|
||||||
|
# pass structure by value
|
||||||
|
func.args[1].add_attribute(lc.ATTR_BY_VAL)
|
||||||
|
|
||||||
|
# define function body
|
||||||
|
builder = lc.Builder.new(func.append_basic_block(''))
|
||||||
|
|
||||||
|
arg = builder.load(func.args[1])
|
||||||
|
e1, e2, e3 = [builder.extract_value(arg, i) for i in range(3)]
|
||||||
|
se1 = builder.fmul(e1, e2)
|
||||||
|
se2 = builder.fdiv(e1, e2)
|
||||||
|
ret = builder.insert_value(lc.Constant.undef(struct_type), se1, 0)
|
||||||
|
ret = builder.insert_value(ret, se2, 1)
|
||||||
|
ret = builder.insert_value(ret, e3, 2)
|
||||||
|
builder.store(ret, func.args[0])
|
||||||
|
builder.ret_void()
|
||||||
|
|
||||||
|
del builder
|
||||||
|
|
||||||
|
# verify
|
||||||
|
m.verify()
|
||||||
|
|
||||||
|
print(m)
|
||||||
|
# use with ctypes
|
||||||
|
engine = le.EngineBuilder.new(m).create()
|
||||||
|
ptr = engine.get_pointer_to_function(func)
|
||||||
|
|
||||||
|
cfunctype = CFUNCTYPE(TwoDoubleOneByte, TwoDoubleOneByte)
|
||||||
|
cfunc = cfunctype(ptr)
|
||||||
|
|
||||||
|
arg = TwoDoubleOneByte(x=1.321321, y=6.54352, z=128)
|
||||||
|
ret = cfunc(arg)
|
||||||
|
print(arg)
|
||||||
|
print(ret)
|
||||||
|
|
||||||
|
self.assertClose(arg.x * arg.y, ret.x)
|
||||||
|
self.assertClose(arg.x / arg.y, ret.y)
|
||||||
|
self.assertEqual(arg.z, ret.z)
|
||||||
|
|
||||||
|
|
||||||
|
@skip_if_not_32bits
|
||||||
|
def test_structure_abi_32_2(self):
|
||||||
|
'''x86 is simple. Always pass structure as memory.
|
||||||
|
'''
|
||||||
|
m = lc.Module.new('test_struct_arg')
|
||||||
|
|
||||||
|
float_type = lc.Type.float()
|
||||||
|
struct_type = lc.Type.struct([float_type, float_type])
|
||||||
|
struct_ptr_type = lc.Type.pointer(struct_type)
|
||||||
|
func_type = lc.Type.function(lc.Type.void(),
|
||||||
|
[struct_ptr_type, struct_ptr_type])
|
||||||
|
func = m.add_function(func_type, name='foo')
|
||||||
|
|
||||||
|
# return value pointer
|
||||||
|
func.args[0].add_attribute(lc.ATTR_STRUCT_RET)
|
||||||
|
|
||||||
|
# pass structure by value
|
||||||
|
func.args[1].add_attribute(lc.ATTR_BY_VAL)
|
||||||
|
|
||||||
|
# define function body
|
||||||
|
builder = lc.Builder.new(func.append_basic_block(''))
|
||||||
|
|
||||||
|
arg = builder.load(func.args[1])
|
||||||
|
e1, e2 = [builder.extract_value(arg, i) for i in range(2)]
|
||||||
|
se1 = builder.fmul(e1, e2)
|
||||||
|
se2 = builder.fdiv(e1, e2)
|
||||||
|
ret = builder.insert_value(lc.Constant.undef(struct_type), se1, 0)
|
||||||
|
ret = builder.insert_value(ret, se2, 1)
|
||||||
|
builder.store(ret, func.args[0])
|
||||||
|
builder.ret_void()
|
||||||
|
|
||||||
|
del builder
|
||||||
|
|
||||||
|
# verify
|
||||||
|
m.verify()
|
||||||
|
|
||||||
|
print(m)
|
||||||
|
# use with ctypes
|
||||||
|
engine = le.EngineBuilder.new(m).create()
|
||||||
|
ptr = engine.get_pointer_to_function(func)
|
||||||
|
|
||||||
|
cfunctype = CFUNCTYPE(TwoFloat, TwoFloat)
|
||||||
|
cfunc = cfunctype(ptr)
|
||||||
|
|
||||||
|
arg = TwoFloat(x=1.321321, y=6.54352)
|
||||||
|
ret = cfunc(arg)
|
||||||
|
print(arg)
|
||||||
|
print(ret)
|
||||||
|
|
||||||
|
self.assertClose(arg.x * arg.y, ret.x)
|
||||||
|
self.assertClose(arg.x / arg.y, ret.y)
|
||||||
|
|
||||||
|
|
||||||
|
@skip_if_not_32bits
|
||||||
|
def test_structure_abi_32_3(self):
|
||||||
|
'''x86 is simple. Always pass structure as memory.
|
||||||
|
'''
|
||||||
|
m = lc.Module.new('test_struct_arg')
|
||||||
|
|
||||||
|
uint8_type = lc.Type.int(8)
|
||||||
|
struct_type = lc.Type.struct([uint8_type])
|
||||||
|
struct_ptr_type = lc.Type.pointer(struct_type)
|
||||||
|
func_type = lc.Type.function(lc.Type.void(),
|
||||||
|
[struct_ptr_type, struct_ptr_type])
|
||||||
|
func = m.add_function(func_type, name='foo')
|
||||||
|
|
||||||
|
# return value pointer
|
||||||
|
func.args[0].add_attribute(lc.ATTR_STRUCT_RET)
|
||||||
|
|
||||||
|
# pass structure by value
|
||||||
|
func.args[1].add_attribute(lc.ATTR_BY_VAL)
|
||||||
|
|
||||||
|
# define function body
|
||||||
|
builder = lc.Builder.new(func.append_basic_block(''))
|
||||||
|
|
||||||
|
arg = builder.load(func.args[1])
|
||||||
|
e1 = builder.extract_value(arg, 0)
|
||||||
|
se1 = builder.mul(e1, e1)
|
||||||
|
ret = builder.insert_value(lc.Constant.undef(struct_type), se1, 0)
|
||||||
|
builder.store(ret, func.args[0])
|
||||||
|
builder.ret_void()
|
||||||
|
|
||||||
|
del builder
|
||||||
|
|
||||||
|
# verify
|
||||||
|
m.verify()
|
||||||
|
|
||||||
|
print(m)
|
||||||
|
# use with ctypes
|
||||||
|
engine = le.EngineBuilder.new(m).create()
|
||||||
|
ptr = engine.get_pointer_to_function(func)
|
||||||
|
|
||||||
|
cfunctype = CFUNCTYPE(OneByte, OneByte)
|
||||||
|
cfunc = cfunctype(ptr)
|
||||||
|
|
||||||
|
arg = OneByte(x=8)
|
||||||
|
ret = cfunc(arg)
|
||||||
|
print(arg)
|
||||||
|
print(ret)
|
||||||
|
|
||||||
|
self.assertEqual(arg.x * arg.x, ret.x)
|
||||||
|
|
||||||
|
tests.append(TestStructABI)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
3
setup.py
3
setup.py
|
|
@ -188,7 +188,8 @@ setup(
|
||||||
'llvm_cbuilder',
|
'llvm_cbuilder',
|
||||||
'llpython',
|
'llpython',
|
||||||
'llvm_array',
|
'llvm_array',
|
||||||
'llvmpy.api', 'llvmpy.api.llvm',],
|
'llvmpy.api', 'llvmpy.api.llvm',
|
||||||
|
'llvm.tests'],
|
||||||
package_data = {'llvm': ['llrt/*.ll']},
|
package_data = {'llvm': ['llrt/*.ll']},
|
||||||
py_modules = ['llvmpy',
|
py_modules = ['llvmpy',
|
||||||
'llvmpy._capsule',
|
'llvmpy._capsule',
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue