""" 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 ''. 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 ''. 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 ) ; :0 [#uses # } # # declare i32 @llvm.bswap.i32(i32) nounwind readnone # # define float @mysin(float %x) { # entry: # %cosx = call float @llvm.cos.f32( float %x ) ; [#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()