1603 lines
50 KiB
Python
1603 lines
50 KiB
Python
"""
|
|
LLVM tests
|
|
"""
|
|
import os
|
|
import sys
|
|
import math
|
|
import shutil
|
|
import unittest
|
|
import subprocess
|
|
import tempfile
|
|
import contextlib
|
|
from distutils.spawn import find_executable
|
|
|
|
is_py3k = sys.version_info[0] >= 3
|
|
BITS = tuple.__itemsize__ * 8
|
|
|
|
try:
|
|
from StringIO import StringIO
|
|
except ImportError:
|
|
from io import StringIO
|
|
|
|
|
|
import llvm
|
|
from llvm.core import (Module, Type, GlobalVariable, Function, Builder,
|
|
Constant, MetaData, MetaDataString, inline_function)
|
|
from llvm.ee import EngineBuilder
|
|
import llvm.core as lc
|
|
import llvm.passes as lp
|
|
import llvm.ee as le
|
|
import llvmpy
|
|
|
|
|
|
tests = []
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
if sys.version_info[:2] <= (2, 6):
|
|
# create custom TestCase
|
|
class TestCase(unittest.TestCase):
|
|
def assertIn(self, item, container):
|
|
self.assertTrue(item in container)
|
|
|
|
def assertNotIn(self, item, container):
|
|
self.assertFalse(item in container)
|
|
|
|
def assertLess(self, a, b):
|
|
self.assertTrue(a < b)
|
|
|
|
def assertIs(self, a, b):
|
|
self.assertTrue(a is b)
|
|
|
|
@contextlib.contextmanager
|
|
def assertRaises(self, exc):
|
|
try:
|
|
yield
|
|
except exc:
|
|
pass
|
|
else:
|
|
raise self.failureException("Did not raise %s" % exc)
|
|
|
|
else:
|
|
TestCase = unittest.TestCase
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestAsm(TestCase):
|
|
def setUp(self):
|
|
self.tmpdir = tempfile.mkdtemp()
|
|
|
|
def tearDown(self):
|
|
shutil.rmtree(self.tmpdir)
|
|
|
|
def test_asm(self):
|
|
# create a module
|
|
m = Module.new('module1')
|
|
m.add_global_variable(Type.int(), 'i')
|
|
|
|
# write it's assembly representation to a file
|
|
asm = str(m)
|
|
|
|
testasm_ll = os.path.join(self.tmpdir, 'testasm.ll')
|
|
with open(testasm_ll, "w") as fout:
|
|
fout.write(asm)
|
|
|
|
# read it back into a module
|
|
with open(testasm_ll) as fin:
|
|
m2 = Module.from_assembly(fin)
|
|
# The default `m.id` is '<string>'.
|
|
m2.id = m.id # Copy the name from `m`
|
|
|
|
self.assertEqual(str(m2).strip(), asm.strip())
|
|
|
|
def test_bitcode(self):
|
|
# create a module
|
|
m = Module.new('module1')
|
|
m.add_global_variable(Type.int(), 'i')
|
|
|
|
# write it's assembly representation to a file
|
|
asm = str(m)
|
|
|
|
testasm_bc = os.path.join(self.tmpdir, 'testasm.bc')
|
|
with open(testasm_bc, "wb") as fout:
|
|
m.to_bitcode(fout)
|
|
|
|
# read it back into a module
|
|
with open(testasm_bc, "rb") as fin:
|
|
m2 = Module.from_bitcode(fin)
|
|
# The default `m.id` is '<string>'.
|
|
m2.id = m.id # Copy the name from `m`
|
|
|
|
self.assertEqual(str(m2).strip(), asm.strip())
|
|
|
|
tests.append(TestAsm)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestAttr(TestCase):
|
|
def make_module(self):
|
|
test_module = """
|
|
define void @sum(i32*, i32*) {
|
|
entry:
|
|
ret void
|
|
}
|
|
"""
|
|
buf = StringIO(test_module)
|
|
return Module.from_assembly(buf)
|
|
|
|
def test_align(self):
|
|
m = self.make_module()
|
|
f = m.get_function_named('sum')
|
|
f.args[0].alignment = 16
|
|
self.assert_("align 16" in str(f))
|
|
self.assertEqual(f.args[0].alignment, 16)
|
|
|
|
tests.append(TestAttr)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestAtomic(TestCase):
|
|
orderings = ['unordered', 'monotonic', 'acquire',
|
|
'release', 'acq_rel', 'seq_cst']
|
|
|
|
atomic_op = ['xchg', 'add', 'sub', 'and', 'nand', 'or', 'xor',
|
|
'max', 'min', 'umax', 'umin']
|
|
|
|
def test_atomic_cmpxchg(self):
|
|
mod = Module.new('mod')
|
|
functype = Type.function(Type.void(), [])
|
|
func = mod.add_function(functype, name='foo')
|
|
bb = func.append_basic_block('entry')
|
|
bldr = Builder.new(bb)
|
|
ptr = bldr.alloca(Type.int())
|
|
|
|
old = bldr.load(ptr)
|
|
new = Constant.int(Type.int(), 1234)
|
|
|
|
for ordering in self.orderings:
|
|
inst = bldr.atomic_cmpxchg(ptr, old, new, ordering)
|
|
self.assertEqual(ordering, str(inst).strip().split(' ')[-1])
|
|
|
|
inst = bldr.atomic_cmpxchg(ptr, old, new, ordering, crossthread=False)
|
|
self.assertEqual('singlethread', str(inst).strip().split(' ')[-2])
|
|
|
|
def test_atomic_rmw(self):
|
|
mod = Module.new('mod')
|
|
functype = Type.function(Type.void(), [])
|
|
func = mod.add_function(functype, name='foo')
|
|
bb = func.append_basic_block('entry')
|
|
bldr = Builder.new(bb)
|
|
ptr = bldr.alloca(Type.int())
|
|
|
|
old = bldr.load(ptr)
|
|
val = Constant.int(Type.int(), 1234)
|
|
|
|
for ordering in self.orderings:
|
|
inst = bldr.atomic_rmw('xchg', ptr, val, ordering)
|
|
self.assertEqual(ordering, str(inst).split(' ')[-1])
|
|
|
|
for op in self.atomic_op:
|
|
inst = bldr.atomic_rmw(op, ptr, val, ordering)
|
|
self.assertEqual(op, str(inst).strip().split(' ')[3])
|
|
|
|
inst = bldr.atomic_rmw('xchg', ptr, val, ordering, crossthread=False)
|
|
self.assertEqual('singlethread', str(inst).strip().split(' ')[-2])
|
|
|
|
for op in self.atomic_op:
|
|
atomic_op = getattr(bldr, 'atomic_%s' % op)
|
|
inst = atomic_op(ptr, val, ordering)
|
|
self.assertEqual(op, str(inst).strip().split(' ')[3])
|
|
|
|
def test_atomic_ldst(self):
|
|
mod = Module.new('mod')
|
|
functype = Type.function(Type.void(), [])
|
|
func = mod.add_function(functype, name='foo')
|
|
bb = func.append_basic_block('entry')
|
|
bldr = Builder.new(bb)
|
|
ptr = bldr.alloca(Type.int())
|
|
|
|
val = Constant.int(Type.int(), 1234)
|
|
|
|
for ordering in self.orderings:
|
|
loaded = bldr.atomic_load(ptr, ordering)
|
|
self.assert_('load atomic' in str(loaded))
|
|
self.assertEqual(ordering,
|
|
str(loaded).strip().split(' ')[-3].rstrip(','))
|
|
self.assert_('align 1' in str(loaded))
|
|
|
|
stored = bldr.atomic_store(loaded, ptr, ordering)
|
|
self.assert_('store atomic' in str(stored))
|
|
self.assertEqual(ordering,
|
|
str(stored).strip().split(' ')[-3].rstrip(','))
|
|
self.assert_('align 1' in str(stored))
|
|
|
|
fenced = bldr.fence(ordering)
|
|
self.assertEqual(['fence', ordering],
|
|
str(fenced).strip().split(' '))
|
|
|
|
tests.append(TestAtomic)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestConstExpr(TestCase):
|
|
|
|
def test_constexpr_opcode(self):
|
|
mod = Module.new('test_constexpr_opcode')
|
|
func = mod.add_function(Type.function(Type.void(), []), name="foo")
|
|
builder = Builder.new(func.append_basic_block('entry'))
|
|
a = builder.inttoptr(Constant.int(Type.int(), 123),
|
|
Type.pointer(Type.int()))
|
|
self.assertTrue(isinstance(a, lc.ConstantExpr))
|
|
self.assertEqual(a.opcode, lc.OPCODE_INTTOPTR)
|
|
self.assertEqual(a.opcode_name, "inttoptr")
|
|
|
|
tests.append(TestConstExpr)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestOperands(TestCase):
|
|
# implement a test function
|
|
test_module = """
|
|
define i32 @prod(i32, i32) {
|
|
entry:
|
|
%2 = mul i32 %0, %1
|
|
ret i32 %2
|
|
}
|
|
|
|
define i32 @test_func(i32, i32, i32) {
|
|
entry:
|
|
%tmp1 = call i32 @prod(i32 %0, i32 %1)
|
|
%tmp2 = add i32 %tmp1, %2
|
|
%tmp3 = add i32 %tmp2, 1
|
|
%tmp4 = add i32 %tmp3, -1
|
|
%tmp5 = add i64 -81985529216486895, 12297829382473034410
|
|
ret i32 %tmp4
|
|
}
|
|
"""
|
|
def test_operands(self):
|
|
m = Module.from_assembly(StringIO(self.test_module))
|
|
|
|
test_func = m.get_function_named("test_func")
|
|
prod = m.get_function_named("prod")
|
|
|
|
# test operands
|
|
i1 = test_func.basic_blocks[0].instructions[0]
|
|
i2 = test_func.basic_blocks[0].instructions[1]
|
|
i3 = test_func.basic_blocks[0].instructions[2]
|
|
i4 = test_func.basic_blocks[0].instructions[3]
|
|
i5 = test_func.basic_blocks[0].instructions[4]
|
|
|
|
self.assertEqual(i1.operand_count, 3)
|
|
self.assertEqual(i2.operand_count, 2)
|
|
|
|
self.assertEqual(i3.operands[1].z_ext_value, 1)
|
|
self.assertEqual(i3.operands[1].s_ext_value, 1)
|
|
self.assertEqual(i4.operands[1].z_ext_value, 0xffffffff)
|
|
self.assertEqual(i4.operands[1].s_ext_value, -1)
|
|
self.assertEqual(i5.operands[0].s_ext_value, -81985529216486895)
|
|
self.assertEqual(i5.operands[1].z_ext_value, 12297829382473034410)
|
|
|
|
self.assert_(i1.operands[-1] is prod)
|
|
self.assert_(i1.operands[0] is test_func.args[0])
|
|
self.assert_(i1.operands[1] is test_func.args[1])
|
|
self.assert_(i2.operands[0] is i1)
|
|
self.assert_(i2.operands[1] is test_func.args[2])
|
|
self.assertEqual(len(i1.operands), 3)
|
|
self.assertEqual(len(i2.operands), 2)
|
|
|
|
self.assert_(i1.called_function is prod)
|
|
|
|
tests.append(TestOperands)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestPasses(TestCase):
|
|
# Create a module.
|
|
asm = """
|
|
|
|
define i32 @test() nounwind {
|
|
ret i32 42
|
|
}
|
|
|
|
define i32 @test1() nounwind {
|
|
entry:
|
|
%tmp = alloca i32
|
|
store i32 42, i32* %tmp, align 4
|
|
%tmp1 = load i32* %tmp, align 4
|
|
%tmp2 = call i32 @test()
|
|
%tmp3 = load i32* %tmp, align 4
|
|
%tmp4 = load i32* %tmp, align 4
|
|
ret i32 %tmp1
|
|
}
|
|
|
|
define i32 @test2() nounwind {
|
|
entry:
|
|
%tmp = call i32 @test()
|
|
ret i32 %tmp
|
|
}
|
|
"""
|
|
def test_passes(self):
|
|
m = Module.from_assembly(StringIO(self.asm))
|
|
|
|
fn_test1 = m.get_function_named('test1')
|
|
fn_test2 = m.get_function_named('test2')
|
|
|
|
original_test1 = str(fn_test1)
|
|
original_test2 = str(fn_test2)
|
|
|
|
# Let's run a module-level inlining pass. First, create a pass manager.
|
|
pm = lp.PassManager.new()
|
|
|
|
# Add the target data as the first "pass". This is mandatory.
|
|
pm.add(le.TargetData.new(''))
|
|
|
|
# Add the inlining pass.
|
|
pm.add(lp.PASS_INLINE)
|
|
|
|
# Run it!
|
|
pm.run(m)
|
|
|
|
# Done with the pass manager.
|
|
del pm
|
|
|
|
# Make sure test2 is inlined
|
|
self.assertNotEqual(str(fn_test2).strip(), original_test2.strip())
|
|
|
|
bb_entry = fn_test2.basic_blocks[0]
|
|
|
|
self.assertEqual(len(bb_entry.instructions), 1)
|
|
self.assertEqual(bb_entry.instructions[0].opcode_name, 'ret')
|
|
|
|
# Let's run a DCE pass on the the function 'test1' now. First create a
|
|
# function pass manager.
|
|
fpm = lp.FunctionPassManager.new(m)
|
|
|
|
# Add the target data as first "pass". This is mandatory.
|
|
fpm.add(le.TargetData.new(''))
|
|
|
|
# Add a DCE pass
|
|
fpm.add(lp.PASS_ADCE)
|
|
|
|
# Run the pass on the function 'test1'
|
|
fpm.run(m.get_function_named('test1'))
|
|
|
|
# Make sure test1 is modified
|
|
self.assertNotEqual(str(fn_test1).strip(), original_test1.strip())
|
|
|
|
def test_passes_with_pmb(self):
|
|
m = Module.from_assembly(StringIO(self.asm))
|
|
|
|
fn_test1 = m.get_function_named('test1')
|
|
fn_test2 = m.get_function_named('test2')
|
|
|
|
original_test1 = str(fn_test1)
|
|
original_test2 = str(fn_test2)
|
|
|
|
# Try out the PassManagerBuilder
|
|
|
|
pmb = lp.PassManagerBuilder.new()
|
|
|
|
self.assertEqual(pmb.opt_level, 2) # ensure default is level 2
|
|
pmb.opt_level = 3
|
|
self.assertEqual(pmb.opt_level, 3) # make sure it works
|
|
|
|
self.assertEqual(pmb.size_level, 0) # ensure default is level 0
|
|
pmb.size_level = 2
|
|
self.assertEqual(pmb.size_level, 2) # make sure it works
|
|
|
|
self.assertFalse(pmb.vectorize) # ensure default is False
|
|
pmb.vectorize = True
|
|
self.assertTrue(pmb.vectorize) # make sure it works
|
|
|
|
# make sure the default is False
|
|
self.assertFalse(pmb.disable_unit_at_a_time)
|
|
self.assertFalse(pmb.disable_unroll_loops)
|
|
self.assertFalse(pmb.disable_simplify_lib_calls)
|
|
|
|
pmb.disable_unit_at_a_time = True
|
|
self.assertTrue(pmb.disable_unit_at_a_time)
|
|
|
|
# Do function pass
|
|
fpm = lp.FunctionPassManager.new(m)
|
|
pmb.populate(fpm)
|
|
fpm.run(fn_test1)
|
|
|
|
# Make sure test1 has changed
|
|
self.assertNotEqual(str(fn_test1).strip(), original_test1.strip())
|
|
|
|
# Do module pass
|
|
pm = lp.PassManager.new()
|
|
pmb.populate(pm)
|
|
pm.run(m)
|
|
|
|
# Make sure test2 has changed
|
|
self.assertNotEqual(str(fn_test2).strip(), original_test2.strip())
|
|
|
|
def test_dump_passes(self):
|
|
self.assertTrue(len(lp.PASSES)>0, msg="Cannot have no passes")
|
|
|
|
tests.append(TestPasses)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestEngineBuilder(TestCase):
|
|
|
|
def make_test_module(self):
|
|
module = Module.new("testmodule")
|
|
fnty = Type.function(Type.int(), [])
|
|
function = module.add_function(fnty, 'foo')
|
|
bb_entry = function.append_basic_block('entry')
|
|
builder = Builder.new(bb_entry)
|
|
builder.ret(Constant.int(Type.int(), 0xcafe))
|
|
module.verify()
|
|
return module
|
|
|
|
def run_foo(self, ee, module):
|
|
function = module.get_function_named('foo')
|
|
retval = ee.run_function(function, [])
|
|
self.assertEqual(retval.as_int(), 0xcafe)
|
|
|
|
|
|
def test_enginebuilder_basic(self):
|
|
module = self.make_test_module()
|
|
self.assertTrue(llvmpy.capsule.has_ownership(module._ptr._ptr))
|
|
ee = EngineBuilder.new(module).create()
|
|
self.assertFalse(llvmpy.capsule.has_ownership(module._ptr._ptr))
|
|
self.run_foo(ee, module)
|
|
|
|
|
|
def test_enginebuilder_with_tm(self):
|
|
tm = le.TargetMachine.new()
|
|
module = self.make_test_module()
|
|
self.assertTrue(llvmpy.capsule.has_ownership(module._ptr._ptr))
|
|
ee = EngineBuilder.new(module).create(tm)
|
|
self.assertFalse(llvmpy.capsule.has_ownership(module._ptr._ptr))
|
|
self.run_foo(ee, module)
|
|
|
|
def test_enginebuilder_force_jit(self):
|
|
module = self.make_test_module()
|
|
ee = EngineBuilder.new(module).force_jit().create()
|
|
|
|
self.run_foo(ee, module)
|
|
#
|
|
# def test_enginebuilder_force_interpreter(self):
|
|
# module = self.make_test_module()
|
|
# ee = EngineBuilder.new(module).force_interpreter().create()
|
|
#
|
|
# self.run_foo(ee, module)
|
|
|
|
def test_enginebuilder_opt(self):
|
|
module = self.make_test_module()
|
|
ee = EngineBuilder.new(module).opt(3).create()
|
|
|
|
self.run_foo(ee, module)
|
|
|
|
tests.append(TestEngineBuilder)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestExecutionEngine(TestCase):
|
|
def test_get_pointer_to_global(self):
|
|
module = lc.Module.new(str(self))
|
|
gvar = module.add_global_variable(Type.int(), 'hello')
|
|
X = 1234
|
|
gvar.initializer = lc.Constant.int(Type.int(), X)
|
|
|
|
ee = le.ExecutionEngine.new(module)
|
|
ptr = ee.get_pointer_to_global(gvar)
|
|
from ctypes import c_void_p, cast, c_int, POINTER
|
|
casted = cast(c_void_p(ptr), POINTER(c_int))
|
|
self.assertEqual(X, casted[0])
|
|
|
|
def test_add_global_mapping(self):
|
|
module = lc.Module.new(str(self))
|
|
gvar = module.add_global_variable(Type.int(), 'hello')
|
|
|
|
fnty = lc.Type.function(Type.int(), [])
|
|
foo = module.add_function(fnty, name='foo')
|
|
bldr = lc.Builder.new(foo.append_basic_block('entry'))
|
|
bldr.ret(bldr.load(gvar))
|
|
|
|
ee = le.ExecutionEngine.new(module)
|
|
from ctypes import c_int, addressof, CFUNCTYPE
|
|
value = 0xABCD
|
|
value_ctype = c_int(value)
|
|
value_pointer = addressof(value_ctype)
|
|
|
|
ee.add_global_mapping(gvar, value_pointer)
|
|
|
|
foo_addr = ee.get_pointer_to_function(foo)
|
|
prototype = CFUNCTYPE(c_int)
|
|
foo_callable = prototype(foo_addr)
|
|
self.assertEqual(foo_callable(), value)
|
|
|
|
|
|
|
|
tests.append(TestExecutionEngine)
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestObjCache(TestCase):
|
|
|
|
def test_objcache(self):
|
|
# Testing module aliasing
|
|
m1 = Module.new('a')
|
|
t = Type.int()
|
|
ft = Type.function(t, [t])
|
|
f1 = m1.add_function(ft, "func")
|
|
m2 = f1.module
|
|
self.assert_(m1 is m2)
|
|
|
|
# Testing global vairable aliasing 1
|
|
gv1 = GlobalVariable.new(m1, t, "gv")
|
|
gv2 = GlobalVariable.get(m1, "gv")
|
|
self.assert_(gv1 is gv2)
|
|
|
|
# Testing global vairable aliasing 2
|
|
gv3 = m1.global_variables[0]
|
|
self.assert_(gv1 is gv3)
|
|
|
|
# Testing global vairable aliasing 3
|
|
gv2 = None
|
|
gv3 = None
|
|
|
|
gv1.delete()
|
|
|
|
gv4 = GlobalVariable.new(m1, t, "gv")
|
|
|
|
self.assert_(gv1 is not gv4)
|
|
|
|
# Testing function aliasing 1
|
|
b1 = f1.append_basic_block('entry')
|
|
f2 = b1.function
|
|
self.assert_(f1 is f2)
|
|
|
|
# Testing function aliasing 2
|
|
f3 = m1.get_function_named("func")
|
|
self.assert_(f1 is f3)
|
|
|
|
# Testing function aliasing 3
|
|
f4 = Function.get_or_insert(m1, ft, "func")
|
|
self.assert_(f1 is f4)
|
|
|
|
# Testing function aliasing 4
|
|
f5 = Function.get(m1, "func")
|
|
self.assert_(f1 is f5)
|
|
|
|
# Testing function aliasing 5
|
|
f6 = m1.get_or_insert_function(ft, "func")
|
|
self.assert_(f1 is f6)
|
|
|
|
# Testing function aliasing 6
|
|
f7 = m1.functions[0]
|
|
self.assert_(f1 is f7)
|
|
|
|
# Testing argument aliasing
|
|
a1 = f1.args[0]
|
|
a2 = f1.args[0]
|
|
self.assert_(a1 is a2)
|
|
|
|
# Testing basic block aliasing 1
|
|
b2 = f1.basic_blocks[0]
|
|
self.assert_(b1 is b2)
|
|
|
|
# Testing basic block aliasing 2
|
|
b3 = f1.entry_basic_block
|
|
self.assert_(b1 is b3)
|
|
|
|
# Testing basic block aliasing 3
|
|
b31 = f1.entry_basic_block
|
|
self.assert_(b1 is b31)
|
|
|
|
# Testing basic block aliasing 4
|
|
bldr = Builder.new(b1)
|
|
b4 = bldr.basic_block
|
|
self.assert_(b1 is b4)
|
|
|
|
# Testing basic block aliasing 5
|
|
i1 = bldr.ret_void()
|
|
b5 = i1.basic_block
|
|
self.assert_(b1 is b5)
|
|
|
|
# Testing instruction aliasing 1
|
|
i2 = b5.instructions[0]
|
|
self.assert_(i1 is i2)
|
|
|
|
# phi node
|
|
phi = bldr.phi(t)
|
|
phi.add_incoming(f1.args[0], b1)
|
|
v2 = phi.get_incoming_value(0)
|
|
b6 = phi.get_incoming_block(0)
|
|
|
|
# Testing PHI / basic block aliasing 5
|
|
self.assert_(b1 is b6)
|
|
|
|
# Testing PHI / value aliasing
|
|
self.assert_(f1.args[0] is v2)
|
|
|
|
tests.append(TestObjCache)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestTargetMachines(TestCase):
|
|
'''Exercise target machines
|
|
|
|
Require PTX backend
|
|
'''
|
|
def test_native(self):
|
|
m, _ = self._build_module()
|
|
tm = le.EngineBuilder.new(m).select_target()
|
|
|
|
self.assertTrue(tm.target_name)
|
|
self.assertTrue(tm.target_data)
|
|
self.assertTrue(tm.target_short_description)
|
|
self.assertTrue(tm.triple)
|
|
self.assertIn('foo', tm.emit_assembly(m))
|
|
self.assertTrue(le.get_host_cpu_name())
|
|
|
|
def test_ptx(self):
|
|
if le.initialize_target('PTX', noraise=True):
|
|
arch = 'ptx64'
|
|
elif le.initialize_target('NVPTX', noraise=True):
|
|
arch = 'nvptx64'
|
|
else:
|
|
return # skip this test
|
|
|
|
print(arch)
|
|
m, func = self._build_module()
|
|
func.calling_convention = lc.CC_PTX_KERNEL # set calling conv
|
|
ptxtm = le.TargetMachine.lookup(arch=arch, cpu='sm_20')
|
|
self.assertTrue(ptxtm.triple)
|
|
self.assertTrue(ptxtm.cpu)
|
|
ptxasm = ptxtm.emit_assembly(m)
|
|
self.assertIn('foo', ptxasm)
|
|
if arch == 'nvptx64':
|
|
self.assertIn('.address_size 64', ptxasm)
|
|
self.assertIn('sm_20', ptxasm)
|
|
|
|
def _build_module(self):
|
|
m = Module.new('TestTargetMachines')
|
|
|
|
fnty = Type.function(Type.void(), [])
|
|
func = m.add_function(fnty, name='foo')
|
|
|
|
bldr = Builder.new(func.append_basic_block('entry'))
|
|
bldr.ret_void()
|
|
m.verify()
|
|
return m, func
|
|
|
|
def _build_bad_archname(self):
|
|
with self.assertRaises(RuntimeError):
|
|
tm = TargetMachine.lookup("ain't no arch name")
|
|
|
|
tests.append(TestTargetMachines)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestNative(TestCase):
|
|
|
|
def setUp(self):
|
|
self.tmpdir = tempfile.mkdtemp()
|
|
|
|
def tearDown(self):
|
|
shutil.rmtree(self.tmpdir)
|
|
|
|
|
|
def _make_module(self):
|
|
m = Module.new('module1')
|
|
m.add_global_variable(Type.int(), 'i')
|
|
|
|
fty = Type.function(Type.int(), [])
|
|
f = m.add_function(fty, name='main')
|
|
|
|
bldr = Builder.new(f.append_basic_block('entry'))
|
|
bldr.ret(Constant.int(Type.int(), 0xab))
|
|
|
|
return m
|
|
|
|
def _compile(self, src):
|
|
cc = find_executable('cc')
|
|
if not cc:
|
|
return
|
|
|
|
dst = os.path.join(self.tmpdir, 'llvmobj.out')
|
|
s = subprocess.call([cc, '-o', dst, src])
|
|
if s != 0:
|
|
raise Exception("Cannot compile")
|
|
|
|
s = subprocess.call([dst])
|
|
self.assertEqual(s, 0xab)
|
|
|
|
def test_assembly(self):
|
|
if sys.platform == 'darwin':
|
|
# skip this test on MacOSX for now
|
|
return
|
|
|
|
m = self._make_module()
|
|
output = m.to_native_assembly()
|
|
|
|
src = os.path.join(self.tmpdir, 'llvmasm.s')
|
|
with open(src, 'wb') as fout:
|
|
if is_py3k:
|
|
fout.write(output.encode('utf-8'))
|
|
else:
|
|
fout.write(output)
|
|
|
|
self._compile(src)
|
|
|
|
def test_object(self):
|
|
if sys.platform == 'darwin':
|
|
# skip this test on MacOSX for now
|
|
return
|
|
|
|
m = self._make_module()
|
|
output = m.to_native_object()
|
|
|
|
src = os.path.join(self.tmpdir, 'llvmobj.o')
|
|
with open(src, 'wb') as fout:
|
|
fout.write(output)
|
|
|
|
self._compile(src)
|
|
|
|
if sys.platform != 'win32':
|
|
tests.append(TestNative)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestNativeAsm(TestCase):
|
|
|
|
def test_asm(self):
|
|
m = Module.new('module1')
|
|
|
|
foo = m.add_function(Type.function(Type.int(),
|
|
[Type.int(), Type.int()]),
|
|
name="foo")
|
|
bldr = Builder.new(foo.append_basic_block('entry'))
|
|
x = bldr.add(foo.args[0], foo.args[1])
|
|
bldr.ret(x)
|
|
|
|
att_syntax = m.to_native_assembly()
|
|
os.environ["LLVMPY_OPTIONS"] = "-x86-asm-syntax=intel"
|
|
lc.parse_environment_options(sys.argv[0], "LLVMPY_OPTIONS")
|
|
intel_syntax = m.to_native_assembly()
|
|
|
|
self.assertNotEqual(att_syntax, intel_syntax)
|
|
|
|
tests.append(TestNativeAsm)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestUses(TestCase):
|
|
|
|
def test_uses(self):
|
|
m = Module.new('a')
|
|
t = Type.int()
|
|
ft = Type.function(t, [t, t, t])
|
|
f = m.add_function(ft, "func")
|
|
b = f.append_basic_block('entry')
|
|
bld = Builder.new(b)
|
|
tmp1 = bld.add(Constant.int(t, 100), f.args[0], "tmp1")
|
|
tmp2 = bld.add(tmp1, f.args[1], "tmp2")
|
|
tmp3 = bld.add(tmp1, f.args[2], "tmp3")
|
|
bld.ret(tmp3)
|
|
|
|
# Testing use count
|
|
self.assertEqual(f.args[0].use_count, 1)
|
|
self.assertEqual(f.args[1].use_count, 1)
|
|
self.assertEqual(f.args[2].use_count, 1)
|
|
self.assertEqual(tmp1.use_count, 2)
|
|
self.assertEqual(tmp2.use_count, 0)
|
|
self.assertEqual(tmp3.use_count, 1)
|
|
|
|
# Testing uses
|
|
self.assert_(f.args[0].uses[0] is tmp1)
|
|
self.assertEqual(len(f.args[0].uses), 1)
|
|
self.assert_(f.args[1].uses[0] is tmp2)
|
|
self.assertEqual(len(f.args[1].uses), 1)
|
|
self.assert_(f.args[2].uses[0] is tmp3)
|
|
self.assertEqual(len(f.args[2].uses), 1)
|
|
self.assertEqual(len(tmp1.uses), 2)
|
|
self.assertEqual(len(tmp2.uses), 0)
|
|
self.assertEqual(len(tmp3.uses), 1)
|
|
|
|
tests.append(TestUses)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestMetaData(TestCase):
|
|
# test module metadata
|
|
def test_metadata(self):
|
|
m = Module.new('a')
|
|
t = Type.int()
|
|
metadata = MetaData.get(m, [Constant.int(t, 100),
|
|
MetaDataString.get(m, 'abcdef'),
|
|
None])
|
|
MetaData.add_named_operand(m, 'foo', metadata)
|
|
self.assertEqual(MetaData.get_named_operands(m, 'foo'), [metadata])
|
|
self.assertEqual(MetaData.get_named_operands(m, 'bar'), [])
|
|
self.assertEqual(len(metadata.operands), 3)
|
|
self.assertEqual(metadata.operands[0].z_ext_value, 100)
|
|
self.assertEqual(metadata.operands[1].string, 'abcdef')
|
|
self.assertTrue(metadata.operands[2] is None)
|
|
|
|
tests.append(TestMetaData)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestInlining(TestCase):
|
|
def test_inline_call(self):
|
|
mod = Module.new(__name__)
|
|
callee = mod.add_function(Type.function(Type.int(), [Type.int()]),
|
|
name='bar')
|
|
|
|
builder = Builder.new(callee.append_basic_block('entry'))
|
|
builder.ret(builder.add(callee.args[0], callee.args[0]))
|
|
|
|
caller = mod.add_function(Type.function(Type.int(), []),
|
|
name='foo')
|
|
|
|
builder = Builder.new(caller.append_basic_block('entry'))
|
|
callinst = builder.call(callee, [Constant.int(Type.int(), 1234)])
|
|
builder.ret(callinst)
|
|
|
|
pre_inlining = str(caller)
|
|
self.assertIn('call', pre_inlining)
|
|
|
|
self.assertTrue(inline_function(callinst))
|
|
|
|
post_inlining = str(caller)
|
|
self.assertNotIn('call', post_inlining)
|
|
self.assertIn('2468', post_inlining)
|
|
|
|
tests.append(TestInlining)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestIssue10(TestCase):
|
|
def test_issue10(self):
|
|
m = Module.new('a')
|
|
ti = Type.int()
|
|
tf = Type.function(ti, [ti, ti])
|
|
|
|
f = m.add_function(tf, "func1")
|
|
|
|
bb = f.append_basic_block('entry')
|
|
|
|
b = Builder.new(bb)
|
|
|
|
# There are no instructions in bb. Positioning of the
|
|
# builder at beginning (or end) should succeed (trivially).
|
|
b.position_at_end(bb)
|
|
b.position_at_beginning(bb)
|
|
|
|
tests.append(TestIssue10)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestOpaque(TestCase):
|
|
|
|
def test_opaque(self):
|
|
# Create an opaque type
|
|
ts = Type.opaque('mystruct')
|
|
self.assertTrue('type opaque' in str(ts))
|
|
self.assertTrue(ts.is_opaque)
|
|
self.assertTrue(ts.is_identified)
|
|
self.assertFalse(ts.is_literal)
|
|
#print(ts)
|
|
|
|
# Create a recursive type
|
|
ts.set_body([Type.int(), Type.pointer(ts)])
|
|
|
|
self.assertEqual(ts.elements[0], Type.int())
|
|
self.assertEqual(ts.elements[1], Type.pointer(ts))
|
|
self.assertEqual(ts.elements[1].pointee, ts)
|
|
self.assertFalse(ts.is_opaque) # is not longer a opaque type
|
|
#print(ts)
|
|
|
|
with self.assertRaises(llvm.LLVMException):
|
|
# Cannot redefine
|
|
ts.set_body([])
|
|
|
|
def test_opaque_with_no_name(self):
|
|
with self.assertRaises(llvm.LLVMException):
|
|
Type.opaque('')
|
|
|
|
tests.append(TestOpaque)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
class TestCPUSupport(TestCase):
|
|
|
|
def _build_test_module(self):
|
|
mod = Module.new('test')
|
|
|
|
float = Type.double()
|
|
mysinty = Type.function( float, [float] )
|
|
mysin = mod.add_function(mysinty, "mysin")
|
|
block = mysin.append_basic_block("entry")
|
|
b = Builder.new(block)
|
|
|
|
sqrt = Function.intrinsic(mod, lc.INTR_SQRT, [float])
|
|
pow = Function.intrinsic(mod, lc.INTR_POWI, [float])
|
|
cos = Function.intrinsic(mod, lc.INTR_COS, [float])
|
|
|
|
mysin.args[0].name = "x"
|
|
x = mysin.args[0]
|
|
one = Constant.real(float, "1")
|
|
cosx = b.call(cos, [x], "cosx")
|
|
cos2 = b.call(pow, [cosx, Constant.int(Type.int(), 2)], "cos2")
|
|
onemc2 = b.fsub(one, cos2, "onemc2") # Should use fsub
|
|
sin = b.call(sqrt, [onemc2], "sin")
|
|
b.ret(sin)
|
|
return mod, mysin
|
|
|
|
def _template(self, mattrs):
|
|
mod, func = self._build_test_module()
|
|
ee = self._build_engine(mod, mattrs=mattrs)
|
|
arg = le.GenericValue.real(Type.double(), 1.234)
|
|
retval = ee.run_function(func, [arg])
|
|
|
|
golden = math.sin(1.234)
|
|
answer = retval.as_real(Type.double())
|
|
self.assertTrue(abs(answer-golden)/golden < 1e-5)
|
|
|
|
|
|
def _build_engine(self, mod, mattrs):
|
|
if mattrs:
|
|
return EngineBuilder.new(mod).mattrs(mattrs).create()
|
|
else:
|
|
return EngineBuilder.new(mod).create()
|
|
|
|
def test_cpu_support2(self):
|
|
features = 'sse3', 'sse41', 'sse42', 'avx'
|
|
mattrs = ','.join(map(lambda s: '-%s' % s, features))
|
|
print('disable mattrs', mattrs)
|
|
self._template(mattrs)
|
|
|
|
def test_cpu_support3(self):
|
|
features = 'sse41', 'sse42', 'avx'
|
|
mattrs = ','.join(map(lambda s: '-%s' % s, features))
|
|
print('disable mattrs', mattrs)
|
|
self._template(mattrs)
|
|
|
|
def test_cpu_support4(self):
|
|
features = 'sse42', 'avx'
|
|
mattrs = ','.join(map(lambda s: '-%s' % s, features))
|
|
print('disable mattrs', mattrs)
|
|
self._template(mattrs)
|
|
|
|
def test_cpu_support5(self):
|
|
features = 'avx',
|
|
mattrs = ','.join(map(lambda s: '-%s' % s, features))
|
|
print('disable mattrs', mattrs)
|
|
self._template(mattrs)
|
|
|
|
def test_cpu_support6(self):
|
|
features = []
|
|
from llvm.workaround.avx_support import detect_avx_support
|
|
if not detect_avx_support():
|
|
print('Skipping: no AVX')
|
|
else:
|
|
mattrs = ','.join(map(lambda s: '-%s' % s, features))
|
|
print('disable mattrs', mattrs)
|
|
self._template(mattrs)
|
|
|
|
tests.append(TestCPUSupport)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
class TestIntrinsicBasic(TestCase):
|
|
|
|
def _build_module(self, float):
|
|
mod = Module.new('test')
|
|
functy = Type.function(float, [float])
|
|
func = mod.add_function(functy, "mytest%s" % float)
|
|
block = func.append_basic_block("entry")
|
|
b = Builder.new(block)
|
|
return mod, func, b
|
|
|
|
def _template(self, mod, func, pyfunc):
|
|
float = func.type.pointee.return_type
|
|
|
|
from llvm.workaround.avx_support import detect_avx_support
|
|
if not detect_avx_support():
|
|
ee = le.EngineBuilder.new(mod).mattrs("-avx").create()
|
|
else:
|
|
ee = le.EngineBuilder.new(mod).create()
|
|
arg = le.GenericValue.real(float, 1.234)
|
|
retval = ee.run_function(func, [arg])
|
|
golden = pyfunc(1.234)
|
|
answer = retval.as_real(float)
|
|
self.assertTrue(abs(answer - golden) / golden < 1e-7)
|
|
|
|
def test_sqrt_f32(self):
|
|
float = Type.float()
|
|
mod, func, b = self._build_module(float)
|
|
intr = Function.intrinsic(mod, lc.INTR_SQRT, [float])
|
|
b.ret(b.call(intr, func.args))
|
|
self._template(mod, func, math.sqrt)
|
|
|
|
def test_sqrt_f64(self):
|
|
float = Type.double()
|
|
mod, func, b = self._build_module(float)
|
|
intr = Function.intrinsic(mod, lc.INTR_SQRT, [float])
|
|
b.ret(b.call(intr, func.args))
|
|
self._template(mod, func, math.sqrt)
|
|
|
|
def test_cos_f32(self):
|
|
if sys.platform == 'win32' and BITS == 32:
|
|
# float32 support is known to fail on 32-bit Windows
|
|
return
|
|
float = Type.float()
|
|
mod, func, b = self._build_module(float)
|
|
intr = Function.intrinsic(mod, lc.INTR_COS, [float])
|
|
b.ret(b.call(intr, func.args))
|
|
self._template(mod, func, math.cos)
|
|
|
|
def test_cos_f64(self):
|
|
float = Type.double()
|
|
mod, func, b = self._build_module(float)
|
|
intr = Function.intrinsic(mod, lc.INTR_COS, [float])
|
|
b.ret(b.call(intr, func.args))
|
|
self._template(mod, func, math.cos)
|
|
|
|
def test_sin_f32(self):
|
|
if sys.platform == 'win32' and BITS == 32:
|
|
# float32 support is known to fail on 32-bit Windows
|
|
return
|
|
float = Type.float()
|
|
mod, func, b = self._build_module(float)
|
|
intr = Function.intrinsic(mod, lc.INTR_SIN, [float])
|
|
b.ret(b.call(intr, func.args))
|
|
self._template(mod, func, math.sin)
|
|
|
|
def test_sin_f64(self):
|
|
float = Type.double()
|
|
mod, func, b = self._build_module(float)
|
|
intr = Function.intrinsic(mod, lc.INTR_SIN, [float])
|
|
b.ret(b.call(intr, func.args))
|
|
self._template(mod, func, math.sin)
|
|
|
|
def test_powi_f32(self):
|
|
float = Type.float()
|
|
mod, func, b = self._build_module(float)
|
|
intr = Function.intrinsic(mod, lc.INTR_POWI, [float])
|
|
b.ret(b.call(intr, [func.args[0], lc.Constant.int(Type.int(), 2)]))
|
|
self._template(mod, func, lambda x: x**2)
|
|
|
|
def test_powi_f64(self):
|
|
float = Type.double()
|
|
mod, func, b = self._build_module(float)
|
|
intr = Function.intrinsic(mod, lc.INTR_POWI, [float])
|
|
b.ret(b.call(intr, [func.args[0], lc.Constant.int(Type.int(), 2)]))
|
|
self._template(mod, func, lambda x: x**2)
|
|
|
|
|
|
|
|
tests.append(TestIntrinsicBasic)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestIntrinsic(TestCase):
|
|
def test_bswap(self):
|
|
# setup a function and a builder
|
|
mod = Module.new('test')
|
|
functy = Type.function(Type.int(), [])
|
|
func = mod.add_function(functy, "showme")
|
|
block = func.append_basic_block("entry")
|
|
b = Builder.new(block)
|
|
|
|
# let's do bswap on a 32-bit integer using llvm.bswap
|
|
val = Constant.int(Type.int(), 0x42)
|
|
bswap = Function.intrinsic(mod, lc.INTR_BSWAP, [Type.int()])
|
|
|
|
bswap_res = b.call(bswap, [val])
|
|
b.ret(bswap_res)
|
|
|
|
# logging.debug(mod)
|
|
|
|
# the output is:
|
|
#
|
|
# ; ModuleID = 'test'
|
|
#
|
|
# define void @showme() {
|
|
# entry:
|
|
# %0 = call i32 @llvm.bswap.i32(i32 42)
|
|
# ret i32 %0
|
|
# }
|
|
|
|
# let's run the function
|
|
ee = le.ExecutionEngine.new(mod)
|
|
retval = ee.run_function(func, [])
|
|
self.assertEqual(retval.as_int(), 0x42000000)
|
|
|
|
def test_mysin(self):
|
|
if sys.platform == 'win32' and BITS == 32:
|
|
# float32 support is known to fail on 32-bit Windows
|
|
return
|
|
|
|
# mysin(x) = sqrt(1.0 - pow(cos(x), 2))
|
|
mod = Module.new('test')
|
|
|
|
float = Type.float()
|
|
mysinty = Type.function( float, [float] )
|
|
mysin = mod.add_function(mysinty, "mysin")
|
|
block = mysin.append_basic_block("entry")
|
|
b = Builder.new(block)
|
|
|
|
sqrt = Function.intrinsic(mod, lc.INTR_SQRT, [float])
|
|
pow = Function.intrinsic(mod, lc.INTR_POWI, [float])
|
|
cos = Function.intrinsic(mod, lc.INTR_COS, [float])
|
|
|
|
mysin.args[0].name = "x"
|
|
x = mysin.args[0]
|
|
one = Constant.real(float, "1")
|
|
cosx = b.call(cos, [x], "cosx")
|
|
cos2 = b.call(pow, [cosx, Constant.int(Type.int(), 2)], "cos2")
|
|
onemc2 = b.fsub(one, cos2, "onemc2") # Should use fsub
|
|
sin = b.call(sqrt, [onemc2], "sin")
|
|
b.ret(sin)
|
|
#logging.debug(mod)
|
|
|
|
# ; ModuleID = 'test'
|
|
#
|
|
# define void @showme() {
|
|
# entry:
|
|
# call i32 @llvm.bswap.i32( i32 42 ) ; <i32>:0 [#uses
|
|
# }
|
|
#
|
|
# declare i32 @llvm.bswap.i32(i32) nounwind readnone
|
|
#
|
|
# define float @mysin(float %x) {
|
|
# entry:
|
|
# %cosx = call float @llvm.cos.f32( float %x ) ; <float
|
|
# %cos2 = call float @llvm.powi.f32( float %cosx, i32 2 )
|
|
# %onemc2 = sub float 1.000000e+00, %cos2 ; <float> [#uses
|
|
# %sin = call float @llvm.sqrt.f32( float %onemc2 )
|
|
# ret float %sin
|
|
# }
|
|
#
|
|
# declare float @llvm.sqrt.f32(float) nounwind readnone
|
|
#
|
|
# declare float @llvm.powi.f32(float, i32) nounwind readnone
|
|
#
|
|
# declare float @llvm.cos.f32(float) nounwind readnone
|
|
|
|
# let's run the function
|
|
|
|
from llvm.workaround.avx_support import detect_avx_support
|
|
if not detect_avx_support():
|
|
ee = le.EngineBuilder.new(mod).mattrs("-avx").create()
|
|
else:
|
|
ee = le.EngineBuilder.new(mod).create()
|
|
|
|
arg = le.GenericValue.real(Type.float(), 1.234)
|
|
retval = ee.run_function(mysin, [arg])
|
|
|
|
golden = math.sin(1.234)
|
|
answer = retval.as_real(Type.float())
|
|
self.assertTrue(abs(answer-golden)/golden < 1e-5)
|
|
|
|
tests.append(TestIntrinsic)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestVolatile(TestCase):
|
|
|
|
def test_volatile(self):
|
|
mod = Module.new('mod')
|
|
functype = Type.function(Type.void(), [])
|
|
func = mod.add_function(functype, name='foo')
|
|
bb = func.append_basic_block('entry')
|
|
bldr = Builder.new(bb)
|
|
ptr = bldr.alloca(Type.int())
|
|
|
|
# test load inst
|
|
val = bldr.load(ptr)
|
|
self.assertFalse(val.is_volatile, "default must be non-volatile")
|
|
val.set_volatile(True)
|
|
self.assertTrue(val.is_volatile, "fail to set volatile")
|
|
val.set_volatile(False)
|
|
self.assertFalse(val.is_volatile, "fail to unset volatile")
|
|
|
|
# test store inst
|
|
store_inst = bldr.store(val, ptr)
|
|
self.assertFalse(store_inst.is_volatile, "default must be non-volatile")
|
|
store_inst.set_volatile(True)
|
|
self.assertTrue(store_inst.is_volatile, "fail to set volatile")
|
|
store_inst.set_volatile(False)
|
|
self.assertFalse(store_inst.is_volatile, "fail to unset volatile")
|
|
|
|
def test_volatile_another(self):
|
|
mod = Module.new('mod')
|
|
functype = Type.function(Type.void(), [])
|
|
func = mod.add_function(functype, name='foo')
|
|
bb = func.append_basic_block('entry')
|
|
bldr = Builder.new(bb)
|
|
ptr = bldr.alloca(Type.int())
|
|
|
|
# test load inst
|
|
val = bldr.load(ptr, volatile=True)
|
|
self.assertTrue(val.is_volatile, "volatile kwarg does not work")
|
|
val.set_volatile(False)
|
|
self.assertFalse(val.is_volatile, "fail to unset volatile")
|
|
val.set_volatile(True)
|
|
self.assertTrue(val.is_volatile, "fail to set volatile")
|
|
|
|
# test store inst
|
|
store_inst = bldr.store(val, ptr, volatile=True)
|
|
self.assertTrue(store_inst.is_volatile, "volatile kwarg does not work")
|
|
store_inst.set_volatile(False)
|
|
self.assertFalse(store_inst.is_volatile, "fail to unset volatile")
|
|
store_inst.set_volatile(True)
|
|
self.assertTrue(store_inst.is_volatile, "fail to set volatile")
|
|
|
|
tests.append(TestVolatile)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestNamedMetaData(TestCase):
|
|
def test_named_md(self):
|
|
m = Module.new('test_named_md')
|
|
nmd = m.get_or_insert_named_metadata('something')
|
|
md = MetaData.get(m, [Constant.int(Type.int(), 0xbeef)])
|
|
nmd.add(md)
|
|
self.assertTrue(str(nmd).startswith('!something'))
|
|
ir = str(m)
|
|
self.assertTrue('!something' in ir)
|
|
|
|
tests.append(TestNamedMetaData)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestStruct(TestCase):
|
|
def test_struct_identical(self):
|
|
m = Module.new('test_struct_identical')
|
|
ta = Type.struct([Type.int(32), Type.float()], name='ta')
|
|
tb = Type.struct([Type.int(32), Type.float()])
|
|
self.assertTrue(ta.is_layout_identical(tb))
|
|
|
|
tests.append(TestStruct)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestTypeHash(TestCase):
|
|
def test_scalar_type(self):
|
|
i32a = Type.int(32)
|
|
i32b = Type.int(32)
|
|
i64a = Type.int(64)
|
|
i64b = Type.int(64)
|
|
ts = set([i32a, i32b, i64a, i64b])
|
|
self.assertTrue(len(ts))
|
|
self.assertTrue(i32a in ts)
|
|
self.assertTrue(i64b in ts)
|
|
|
|
def test_struct_type(self):
|
|
ta = Type.struct([Type.int(32), Type.float()])
|
|
tb = Type.struct([Type.int(32), Type.float()])
|
|
tc = Type.struct([Type.int(32), Type.int(32), Type.float()])
|
|
ts = set([ta, tb, tc])
|
|
self.assertTrue(len(ts) == 2)
|
|
self.assertTrue(ta in ts)
|
|
self.assertTrue(tb in ts)
|
|
self.assertTrue(tc in ts)
|
|
|
|
tests.append(TestTypeHash)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestArgAttr(TestCase):
|
|
def test_arg_attr(self):
|
|
m = Module.new('oifjda')
|
|
vptr = Type.pointer(Type.float())
|
|
sptr = Type.pointer(Type.struct([]))
|
|
fnty = Type.function(Type.void(), [vptr] * 5)
|
|
func = m.add_function(fnty, 'foo')
|
|
attrs = [lc.ATTR_STRUCT_RET, lc.ATTR_BY_VAL, lc.ATTR_NEST,
|
|
lc.ATTR_NO_ALIAS, lc.ATTR_NO_CAPTURE]
|
|
for i, attr in enumerate(attrs):
|
|
arg = func.args[i]
|
|
self.assertEqual(i, arg.arg_no)
|
|
arg.add_attribute(attr)
|
|
self.assertTrue(attr in func.args[i])
|
|
|
|
tests.append(TestArgAttr)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestSwitch(TestCase):
|
|
def test_arg_attr(self):
|
|
m = Module.new('oifjda')
|
|
fnty = Type.function(Type.void(), [Type.int()])
|
|
func = m.add_function(fnty, 'foo')
|
|
bb = func.append_basic_block('')
|
|
bbdef = func.append_basic_block('')
|
|
bbsw1 = func.append_basic_block('')
|
|
bbsw2 = func.append_basic_block('')
|
|
bldr = Builder.new(bb)
|
|
|
|
swt = bldr.switch(func.args[0], bbdef, n=2)
|
|
swt.add_case(Constant.int(Type.int(), 0), bbsw1)
|
|
swt.add_case(Constant.int(Type.int(), 1), bbsw2)
|
|
|
|
bldr.position_at_end(bbsw1)
|
|
bldr.ret_void()
|
|
|
|
bldr.position_at_end(bbsw2)
|
|
bldr.ret_void()
|
|
|
|
bldr.position_at_end(bbdef)
|
|
bldr.ret_void()
|
|
|
|
func.verify()
|
|
|
|
tests.append(TestSwitch)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestCmp(TestCase):
|
|
def test_arg_attr(self):
|
|
m = Module.new('oifjda')
|
|
fnty = Type.function(Type.void(), [Type.int()])
|
|
func = m.add_function(fnty, 'foo')
|
|
bb = func.append_basic_block('')
|
|
bldr = Builder.new(bb)
|
|
|
|
cmpinst = bldr.icmp(lc.ICMP_ULE, func.args[0],
|
|
Constant.int(Type.int(), 123))
|
|
self.assertTrue(repr(cmpinst.predicate).startswith('ICMP_ULE'))
|
|
self.assertEqual(cmpinst.predicate, lc.ICMP_ULE)
|
|
bldr.ret_void()
|
|
|
|
func.verify()
|
|
|
|
tests.append(TestCmp)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestMCJIT(TestCase):
|
|
def test_mcjit(self):
|
|
m = Module.new('oidfjs')
|
|
fnty = Type.function(Type.int(), [Type.int(), Type.int()])
|
|
func = m.add_function(fnty, 'foo')
|
|
bb = func.append_basic_block('')
|
|
bldr = Builder.new(bb)
|
|
bldr.ret(bldr.add(*func.args))
|
|
|
|
func.verify()
|
|
|
|
engine = EngineBuilder.new(m).mcjit(True).create()
|
|
ptr = engine.get_pointer_to_function(func)
|
|
|
|
from ctypes import c_int, CFUNCTYPE
|
|
callee = CFUNCTYPE(c_int, c_int, c_int)(ptr)
|
|
self.assertEqual(321 + 123, callee(321, 123))
|
|
|
|
def test_multi_module_linking(self):
|
|
# generate external library module
|
|
m = Module.new('external-library-module')
|
|
fnty = Type.function(Type.int(), [Type.int(), Type.int()])
|
|
libfname = 'myadd'
|
|
func = m.add_function(fnty, libfname)
|
|
bb = func.append_basic_block('')
|
|
bldr = Builder.new(bb)
|
|
bldr.ret(bldr.add(*func.args))
|
|
func.verify()
|
|
|
|
# JIT the lib module and bind dynamic symbol
|
|
libengine = EngineBuilder.new(m).mcjit(True).create()
|
|
myadd_ptr = libengine.get_pointer_to_function(func)
|
|
le.dylib_add_symbol(libfname, myadd_ptr)
|
|
|
|
# reference external library
|
|
m = Module.new('user')
|
|
fnty = Type.function(Type.int(), [Type.int(), Type.int()])
|
|
func = m.add_function(fnty, 'foo')
|
|
bb = func.append_basic_block('')
|
|
bldr = Builder.new(bb)
|
|
extadd = m.get_or_insert_function(fnty, name=libfname)
|
|
bldr.ret(bldr.call(extadd, func.args))
|
|
func.verify()
|
|
|
|
# JIT the user module
|
|
engine = EngineBuilder.new(m).mcjit(True).create()
|
|
ptr = engine.get_pointer_to_function(func)
|
|
self.assertEqual(myadd_ptr,
|
|
engine.get_pointer_to_named_function(libfname))
|
|
|
|
from ctypes import c_int, CFUNCTYPE
|
|
callee = CFUNCTYPE(c_int, c_int, c_int)(ptr)
|
|
self.assertEqual(321 + 123, callee(321, 123))
|
|
|
|
|
|
if (llvm.version >= (3, 3) and
|
|
not (sys.platform.startswith('win32') and BITS == 64)):
|
|
# MCJIT broken in 3.2, the test will segfault in OSX?
|
|
# Compatbility problem on windows 7 64-bit?
|
|
tests.append(TestMCJIT)
|
|
|
|
|
|
class TestLLRT(TestCase):
|
|
def test_llrt_divmod(self):
|
|
from llvm import llrt
|
|
m = lc.Module.new('testllrt')
|
|
longlong = lc.Type.int(64)
|
|
lfunc = m.add_function(lc.Type.function(longlong, [longlong, longlong]), 'foo')
|
|
bldr = lc.Builder.new(lfunc.append_basic_block(''))
|
|
bldr.ret(bldr.udiv(*lfunc.args))
|
|
|
|
llrt.replace_divmod64(lfunc)
|
|
|
|
rt = llrt.LLRT()
|
|
rt.install_symbols()
|
|
|
|
engine = le.EngineBuilder.new(m).create()
|
|
pointer = engine.get_pointer_to_function(lfunc)
|
|
|
|
from ctypes import CFUNCTYPE, c_uint64, c_int64
|
|
func = CFUNCTYPE(c_uint64, c_uint64, c_uint64)(pointer)
|
|
a, b = 98342, 2231
|
|
self.assertEqual(func(98342, 2231), 98342 // 2231)
|
|
|
|
rt.uninstall_symbols()
|
|
|
|
tests.append(TestLLRT)
|
|
|
|
class TestArith(TestCase):
|
|
'''
|
|
Test basic arithmetic support with LLVM MCJIT
|
|
'''
|
|
def func_template(self, ty, op):
|
|
m = Module.new('dofjaa')
|
|
fnty = Type.function(ty, [ty, ty])
|
|
fn = m.add_function(fnty, 'foo')
|
|
bldr = Builder.new(fn.append_basic_block(''))
|
|
bldr.ret(getattr(bldr, op)(*fn.args))
|
|
|
|
engine = EngineBuilder.new(m).mcjit(True).create()
|
|
ptr = engine.get_pointer_to_function(fn)
|
|
|
|
from ctypes import c_uint32, c_uint64, c_float, c_double, CFUNCTYPE
|
|
|
|
maptypes = {
|
|
Type.int(32): c_uint32,
|
|
Type.int(64): c_uint64,
|
|
Type.float(): c_float,
|
|
Type.double(): c_double,
|
|
}
|
|
cty = maptypes[ty]
|
|
prototype = CFUNCTYPE(*[cty] * 3)
|
|
callee = prototype(ptr)
|
|
callee(12, 23)
|
|
|
|
def template(self, iop, fop):
|
|
inttys = [Type.int(32), Type.int(64)]
|
|
flttys = [Type.float(), Type.double()]
|
|
|
|
if iop:
|
|
for ty in inttys:
|
|
self.func_template(ty, iop)
|
|
if fop:
|
|
for ty in flttys:
|
|
self.func_template(ty, fop)
|
|
|
|
def test_add(self):
|
|
self.template('add', 'fadd')
|
|
|
|
def test_sub(self):
|
|
self.template('sub', 'fsub')
|
|
|
|
def test_mul(self):
|
|
self.template('mul', 'fmul')
|
|
|
|
def test_div(self):
|
|
if BITS == 32:
|
|
print('skipped test for div')
|
|
print('known failure due to unresolved external symbol __udivdi3')
|
|
return
|
|
self.template('udiv', None) # 'fdiv')
|
|
|
|
def test_rem(self):
|
|
if BITS == 32:
|
|
print('skipped test for rem')
|
|
print('known failure due to unresolved external symbol __umoddi3')
|
|
return
|
|
self.template('urem', None) # 'frem')
|
|
|
|
if llvm.version >= (3, 3):
|
|
# MCJIT is broken in 3.2
|
|
tests.append(TestArith)
|
|
|
|
class TestNUWNSW(TestCase):
|
|
def make_module(self):
|
|
mod = Module.new('asdfa')
|
|
fnty = Type.function(Type.void(), [Type.int()] * 2)
|
|
func = mod.add_function(fnty, 'foo')
|
|
bldr = Builder.new(func.append_basic_block(''))
|
|
return mod, func, bldr
|
|
|
|
def has_nsw(self, inst, op):
|
|
self.assertTrue(('%s nsw' % op) in str(inst), "NSW flag does not work")
|
|
|
|
def has_nuw(self, inst, op):
|
|
self.assertTrue(('%s nuw' % op) in str(inst), "NUW flag does not work")
|
|
|
|
def _test_template(self, opf, opname):
|
|
mod, func, bldr = self.make_module()
|
|
a, b = func.args
|
|
self.has_nsw(opf(bldr, a, b, nsw=True), opname)
|
|
self.has_nuw(opf(bldr, a, b, nuw=True), opname)
|
|
|
|
def test_add_nuw_nsw(self):
|
|
self._test_template(Builder.add, 'add')
|
|
|
|
def test_sub_nuw_nsw(self):
|
|
self._test_template(Builder.sub, 'sub')
|
|
|
|
def test_mul_nuw_nsw(self):
|
|
self._test_template(Builder.mul, 'mul')
|
|
|
|
def test_shl_nuw_nsw(self):
|
|
self._test_template(Builder.shl, 'shl')
|
|
|
|
def test_neg_nuw_nsw(self):
|
|
mod, func, bldr = self.make_module()
|
|
a, b = func.args
|
|
self.has_nsw(bldr.neg(a, nsw=True), 'sub')
|
|
self.has_nuw(bldr.neg(a, nuw=True), 'sub')
|
|
|
|
|
|
tests.append(TestNUWNSW)
|
|
|
|
|
|
class TestExact(TestCase):
|
|
def make_module(self):
|
|
mod = Module.new('asdfa')
|
|
fnty = Type.function(Type.void(), [Type.int()] * 2)
|
|
func = mod.add_function(fnty, 'foo')
|
|
bldr = Builder.new(func.append_basic_block(''))
|
|
return mod, func, bldr
|
|
|
|
def has_exact(self, inst, op):
|
|
self.assertTrue(('%s exact' % op) in str(inst), "exact flag does not work")
|
|
|
|
def _test_template(self, opf, opname):
|
|
mod, func, bldr = self.make_module()
|
|
a, b = func.args
|
|
self.has_exact(opf(bldr, a, b, exact=True), opname)
|
|
|
|
def test_udiv_exact(self):
|
|
self._test_template(Builder.udiv, 'udiv')
|
|
|
|
def test_sdiv_exact(self):
|
|
self._test_template(Builder.sdiv, 'sdiv')
|
|
|
|
def test_lshr_exact(self):
|
|
self._test_template(Builder.lshr, 'lshr')
|
|
|
|
def test_ashr_exact(self):
|
|
self._test_template(Builder.ashr, 'ashr')
|
|
|
|
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()
|