<commit_before>"""
IR Construction Tests
"""

from __future__ import print_function, absolute_import

import sys
import copy
import itertools
import pickle
import re
import textwrap
import unittest

from . import TestCase
from llvmlite import ir
from llvmlite import binding as llvm


int1 = ir.IntType(1)
int8 = ir.IntType(8)
int16 = ir.IntType(16)
int32 = ir.IntType(32)
int64 = ir.IntType(64)
flt = ir.FloatType()
dbl = ir.DoubleType()


PY2 = sys.version_info[0] == 2


class TestBase(TestCase):
    """
    Utilities for IR tests.
    """

    def assertInText(self, pattern, text):
        """
        Assert *pattern* is in *text*, ignoring any whitespace differences
        (including newlines).
        """

        def escape(c):
            if not c.isalnum() and not c.isspace():
                return '\\' + c
            return c

        pattern = ''.join(map(escape, pattern))
        regex = re.sub(r'\s+', r'\s*', pattern)
        self.assertRegexpMatches(text, regex)

    def assert_ir_line(self, line, mod):
        lines = [line.strip() for line in str(mod).splitlines()]
        self.assertIn(line, lines)

    def assert_valid_ir(self, mod):
        llvm.parse_assembly(str(mod))

    def module(self):
        return ir.Module()

    def function(self, module=None, name='my_func'):
        module = module or self.module()
        fnty = ir.FunctionType(int32, (int32, int32, dbl, ir.PointerType(int32)))
        return ir.Function(self.module(), fnty, name)

    def block(self, func=None, name=''):
        func = func or self.function()
        return func.append_basic_block(name)

    def descr(self, thing):
        buf = []
        thing.descr(buf)
        return "".join(buf)

    def _normalize_asm(self, asm):
        asm = textwrap.dedent(asm)
        # Normalize indent
        asm = asm.replace("\n    ", "\n  ")
        return asm

    def check_descr(self, descr, asm):
        expected = self._normalize_asm(asm)
        self.assertEqual(descr, expected)

    def check_block(self, block, asm):
        self.check_descr(self.descr(block), asm)

    def check_module_body(self, module, asm):
        expected = self._normalize_asm(asm)
        actual = module._stringify_body()
        self.assertEqual(actual.strip(), expected.strip())

    def check_metadata(self, module, asm):
        """
        Check module metadata against *asm*.
        """
        expected = self._normalize_asm(asm)
        actual = module._stringify_metadata()
        self.assertEqual(actual.strip(), expected.strip())

    def check_func_body(self, func, asm):
        expected = self._normalize_asm(asm)
        actual = self.descr(func)
        actual = actual.partition('{')[2].rpartition('}')[0]
        self.assertEqual(actual.strip(), expected.strip())


class TestFunction(TestBase):

    proto = """i32 @"my_func"(i32 %".1", i32 %".2", double %".3", i32* %".4")"""

    def test_declare(self):
        # A simple declaration
        func = self.function()
        asm = self.descr(func).strip()
        self.assertEqual(asm.strip(), "declare %s" % self.proto)

    def test_declare_attributes(self):
        # Now with function attributes
        func = self.function()
        func.attributes.add("optsize")
        func.attributes.add("alwaysinline")
        func.attributes.alignstack = 16
        tp_pers = ir.FunctionType(int8, (), var_arg=True)
        pers = ir.Function(self.module(), tp_pers, '__gxx_personality_v0')
        func.attributes.personality = pers
        asm = self.descr(func).strip()
        self.assertEqual(asm,
            ("declare %s alwaysinline optsize alignstack(16) "
             "personality i8 (...)* @\"__gxx_personality_v0\"") % self.proto)

    def test_function_attributes(self):
        # Now with parameter attributes
        func = self.function()
        func.args[0].add_attribute("zeroext")
        func.args[1].attributes.dereferenceable = 5
        func.args[1].attributes.dereferenceable_or_null = 10
        func.args[3].attributes.align = 4
        func.args[3].add_attribute("nonnull")
        func.return_value.add_attribute("noalias")
        asm = self.descr(func).strip()
        self.assertEqual(asm,
            """declare noalias i32 @"my_func"(i32 zeroext %".1", i32 dereferenceable(5) dereferenceable_or_null(10) %".2", double %".3", i32* nonnull align 4 %".4")"""
            )

    def test_function_metadata(self):
        # Now with function metadata
        module = self.module()
        func = self.function(module)
        func.set_metadata('dbg', module.add_metadata([]))
        asm = self.descr(func).strip()
        self.assertEqual(asm,
            """declare i32 @"my_func"(i32 %".1", i32 %".2", double %".3", i32* %".4") !dbg !0"""
            )

    def test_define(self):
        # A simple definition
        func = self.function()
        func.attributes.add("alwaysinline")
        block = func.append_basic_block('my_block')
        builder = ir.IRBuilder(block)
        builder.ret_void()
        asm = self.descr(func)
        self.check_descr(asm, """\
            define {proto} alwaysinline
            {{
            my_block:
                ret void
            }}
            """.format(proto=self.proto))

    def test_declare_intrinsics(self):
        module = self.module()
        pint8 = int8.as_pointer()

        powi = module.declare_intrinsic('llvm.powi', [dbl])
        memset = module.declare_intrinsic('llvm.memset', [pint8, int32])
        memcpy = module.declare_intrinsic('llvm.memcpy', [pint8, pint8, int32])
        assume = module.declare_intrinsic('llvm.assume')
        self.check_descr(self.descr(powi).strip(), """\
            declare double @"llvm.powi.f64"(double %".1", i32 %".2")""")
        self.check_descr(self.descr(memset).strip(), """\
            declare void @"llvm.memset.p0i8.i32"(i8* %".1", i8 %".2", i32 %".3", i32 %".4", i1 %".5")""")
        self.check_descr(self.descr(memcpy).strip(), """\
            declare void @"llvm.memcpy.p0i8.p0i8.i32"(i8* %".1", i8* %".2", i32 %".3", i32 %".4", i1 %".5")""")
        self.check_descr(self.descr(assume).strip(), """\
            declare void @"llvm.assume"(i1 %".1")""")

    def test_redeclare_intrinsic(self):
        module = self.module()
        powi = module.declare_intrinsic('llvm.powi', [dbl])
        powi2 = module.declare_intrinsic('llvm.powi', [dbl])
        self.assertIs(powi, powi2)


class TestIR(TestBase):

    def test_unnamed_metadata(self):
        # An unnamed metadata node
        mod = self.module()
        md = mod.add_metadata([int32(123), int8(42)])
        self.assert_ir_line("!0 = !{ i32 123, i8 42 }", mod)
        self.assert_valid_ir(mod)

    def test_unnamed_metadata_2(self):
        # Several unnamed metadata nodes
        mod = self.module()
        # First node has a literal metadata string
        m0 = mod.add_metadata([int32(123), "kernel"])
        # Second node refers to the first one
        m1 = mod.add_metadata([int64(456), m0])
        # Third node is the same as the second one
        m2 = mod.add_metadata([int64(456), m0])
        self.assertIs(m2, m1)
        # Fourth node refers to the first three
        m3 = mod.add_metadata([m0, m1, m2])
        self.assert_ir_line('!0 = !{ i32 123, !"kernel" }', mod)
        self.assert_ir_line('!1 = !{ i64 456, !0 }', mod)
        self.assert_ir_line('!2 = !{ !0, !1, !1 }', mod)

    def test_unnamed_metadata_3(self):
        # Passing nested metadata as a sequence
        mod = self.module()
        mod.add_metadata([int32(123), [int32(456)], [int32(789)], [int32(456)]])
        self.assert_ir_line('!0 = !{ i32 456 }', mod)
        self.assert_ir_line('!1 = !{ i32 789 }', mod)
        self.assert_ir_line('!2 = !{ i32 123, !0, !1, !0 }', mod)

    def test_metadata_string(self):
        # Escaping contents of a metadata string
        mod = self.module()
        mod.add_metadata(["\"\\$"])
        self.assert_ir_line('!0 = !{ !"\\22\\5c$" }', mod)

    def test_named_metadata(self):
        # Add a named metadata node and add metadata values to it
        mod = self.module()
        m0 = mod.add_metadata([int32(123)])
        m1 = mod.add_metadata([int64(456)])
        nmd = mod.add_named_metadata("foo")
        nmd.add(m0)
        nmd.add(m1)
        nmd.add(m0)
        self.assert_ir_line("!foo = !{ !0, !1, !0 }", mod)
        self.assert_valid_ir(mod)
        # Check get_named_metadata()
        self.assertIs(nmd, mod.get_named_metadata("foo"))
        with self.assertRaises(KeyError):
            mod.get_named_metadata("bar")

    def test_named_metadata_2(self):
        # Add and set named metadata through a single add_named_metadata() call
        mod = self.module()
        m0 = mod.add_metadata([int32(123)])
        mod.add_named_metadata("foo", m0)
        mod.add_named_metadata("foo", [int64(456)])
        mod.add_named_metadata("foo", ["kernel"])
        mod.add_named_metadata("bar", [])
        self.assert_ir_line("!foo = !{ !0, !1, !2 }", mod)
        self.assert_ir_line("!0 = !{ i32 123 }", mod)
        self.assert_ir_line("!1 = !{ i64 456 }", mod)
        self.assert_ir_line('!2 = !{ !"kernel" }', mod)
        self.assert_ir_line("!bar = !{ !3 }", mod)
        self.assert_ir_line('!3 = !{  }', mod)
        self.assert_valid_ir(mod)

    def test_metadata_null(self):
        # A null metadata (typed) value
        mod = self.module()
        md = mod.add_metadata([int32.as_pointer()(None)])
        self.assert_ir_line("!0 = !{ i32* null }", mod)
        self.assert_valid_ir(mod)
        # A null metadata (untyped) value
        mod = self.module()
        md = mod.add_metadata([None, int32(123)])
        self.assert_ir_line("!0 = !{ null, i32 123 }", mod)
        self.assert_valid_ir(mod)

    def test_debug_info(self):
        # Add real world-looking debug information to a module
        # (with various value types)
        mod = self.module()
        di_file = mod.add_debug_info("DIFile", {
            "filename":        "foo",
            "directory":       "bar",
        })
        di_func_type = mod.add_debug_info("DISubroutineType", {
            # None as `null`
            "types":           mod.add_metadata([None]),
            })
        di_compileunit = mod.add_debug_info("DICompileUnit", {
            "language":        ir.DIToken("DW_LANG_Python"),
            "file":            di_file,
            "producer":        "ARTIQ",
            "runtimeVersion":  0,
            "isOptimized":     True,
        }, is_distinct=True)
        di_func = mod.add_debug_info("DISubprogram", {
            "name":            "my_func",
            "file":            di_file,
            "line":            11,
            "type":            di_func_type,
            "isLocal":         False,
            "unit":            di_compileunit,
            }, is_distinct=True)

        # Check output
        strmod = str(mod)
        self.assert_ir_line('!0 = !DIFile(directory: "bar", filename: "foo")',
                            strmod)
        self.assert_ir_line('!1 = !{ null }', strmod)
        self.assert_ir_line('!2 = !DISubroutineType(types: !1)', strmod)
        # self.assert_ir_line('!4 = !{ !3 }', strmod)
        self.assert_ir_line('!3 = distinct !DICompileUnit(file: !0, '
                            'isOptimized: true, language: DW_LANG_Python, '
                            'producer: "ARTIQ", runtimeVersion: 0)',
                            strmod)
        self.assert_ir_line('!4 = distinct !DISubprogram(file: !0, isLocal: false, '
                            'line: 11, name: "my_func", type: !2, unit: !3)',
                            strmod)
        self.assert_valid_ir(mod)

    def test_debug_info_2(self):
        # Identical debug info nodes should be merged
        mod = self.module()
        di1 = mod.add_debug_info("DIFile",
                                 {"filename": "foo",
                                  "directory": "bar",
                                 })
        di2 = mod.add_debug_info("DIFile",
                                 {"filename": "foo",
                                  "directory": "bar",
                                 })
        di3 = mod.add_debug_info("DIFile",
                                 {"filename": "bar",
                                  "directory": "foo",
                                 })
        di4 = mod.add_debug_info("DIFile",
                                 {"filename": "foo",
                                  "directory": "bar",
                                 }, is_distinct=True)
        self.assertIs(di1, di2)
        self.assertEqual(len({di1, di2, di3, di4}), 3)
        # Check output
        strmod = str(mod)
        self.assert_ir_line('!0 = !DIFile(directory: "bar", filename: "foo")',
                            strmod)
        self.assert_ir_line('!1 = !DIFile(directory: "foo", filename: "bar")',
                            strmod)
        self.assert_ir_line('!2 = distinct !DIFile(directory: "bar", filename: "foo")',
                            strmod)
        self.assert_valid_ir(mod)

    @unittest.skipUnless(PY2, 'py2 only')
    def test_debug_info_py2_long(self):
        mod = self.module()
        di = mod.add_debug_info("DIBasicType",
                                {"name": "foo",
                                 "size": long(123)})  # long integer here
        self.assert_ir_line('!0 = !DIBasicType(name: "foo", size: 123)',
                            str(di))
        self.assert_valid_ir(mod)

    def test_inline_assembly(self):
        mod = self.module()
        foo = ir.Function(mod, ir.FunctionType(ir.VoidType(), []), 'foo')
        builder = ir.IRBuilder(foo.append_basic_block(''))
        asmty = ir.FunctionType(int32, [int32])
        asm = ir.InlineAsm(asmty, "mov $1, $2", "=r,r", side_effect=True)
        builder.call(asm, [int32(123)])
        builder.ret_void()
        pat = 'call i32 asm sideeffect "mov $1, $2", "=r,r" ( i32 123 )'
        self.assertInText(pat, str(mod))
        self.assert_valid_ir(mod)

    def test_builder_asm(self):
        mod = self.module()
        foo = ir.Function(mod, ir.FunctionType(ir.VoidType(), []), 'foo')
        builder = ir.IRBuilder(foo.append_basic_block(''))
        asmty = ir.FunctionType(int32, [int32])
        builder.asm(asmty, "mov $1, $2", "=r,r", [int32(123)], side_effect=True)
        builder.ret_void()
        pat = 'call i32 asm sideeffect "mov $1, $2", "=r,r" ( i32 123 )'
        self.assertInText(pat, str(mod))
        self.assert_valid_ir(mod)

    def test_builder_load_reg(self):
        mod = self.module()
        foo = ir.Function(mod, ir.FunctionType(ir.VoidType(), []), 'foo')
        builder = ir.IRBuilder(foo.append_basic_block(''))
        asmty = ir.FunctionType(int32, [int32])
        builder.load_reg(ir.IntType(64), "rax")
        builder.ret_void()
        pat = 'call i64 asm "", "={rax}"'
        self.assertInText(pat, str(mod))
        self.assert_valid_ir(mod)

    def test_builder_store_reg(self):
        mod = self.module()
        foo = ir.Function(mod, ir.FunctionType(ir.VoidType(), []), 'foo')
        builder = ir.IRBuilder(foo.append_basic_block(''))
        asmty = ir.FunctionType(ir.VoidType(), [int32])
        builder.store_reg(int64(123), ir.IntType(64), "rax")
        builder.ret_void()
        pat = 'call void asm sideeffect "", "{rax}" ( i64 123 )'
        self.assertInText(pat, str(mod))
        self.assert_valid_ir(mod)

class TestGlobalValues(TestBase):

    def test_globals_access(self):
        mod = self.module()
        foo = ir.Function(mod, ir.FunctionType(ir.VoidType(), []), 'foo')
        ir.Function(mod, ir.FunctionType(ir.VoidType(), []), 'bar')
        globdouble = ir.GlobalVariable(mod, ir.DoubleType(), 'globdouble')
        self.assertEqual(mod.get_global('foo'), foo)
        self.assertEqual(mod.get_global('globdouble'), globdouble)
        with self.assertRaises(KeyError):
            mod.get_global('kkk')
        # Globals should have a useful repr()
        self.assertEqual(repr(globdouble),
                         "<ir.GlobalVariable 'globdouble' of type 'double*'>")

    def test_functions_global_values_access(self):
        """
        Accessing functions and global values through Module.functions
        and Module.global_values.
        """
        mod = self.module()
        fty = ir.FunctionType(ir.VoidType(), [])
        foo = ir.Function(mod, fty, 'foo')
        bar = ir.Function(mod, fty, 'bar')
        globdouble = ir.GlobalVariable(mod, ir.DoubleType(), 'globdouble')
        self.assertEqual(set(mod.functions), set((foo, bar)))
        self.assertEqual(set(mod.global_values), set((foo, bar, globdouble)))

    def test_global_variables_ir(self):
        """
        IR serialization of global variables.
        """
        mod = self.module()
        a = ir.GlobalVariable(mod, int8, 'a')
        b = ir.GlobalVariable(mod, int8, 'b', addrspace=42)
        # Initialized global variable doesn't default to "external"
        c = ir.GlobalVariable(mod, int32, 'c')
        c.initializer = int32(123)
        d = ir.GlobalVariable(mod, int32, 'd')
        d.global_constant = True
        # Non-external linkage implies default "undef" initializer
        e = ir.GlobalVariable(mod, int32, 'e')
        e.linkage = "internal"
        f = ir.GlobalVariable(mod, int32, 'f', addrspace=456)
        f.unnamed_addr = True
        g = ir.GlobalVariable(mod, int32, 'g')
        g.linkage = "internal"
        g.initializer = int32(123)
        g.align = 16
        self.check_module_body(mod, """\
            @"a" = external global i8
            @"b" = external addrspace(42) global i8
            @"c" = global i32 123
            @"d" = external constant i32
            @"e" = internal global i32 undef
            @"f" = external unnamed_addr addrspace(456) global i32
            @"g" = internal global i32 123, align 16
            """)


class TestBlock(TestBase):

    def test_attributes(self):
        func = self.function()
        block = ir.Block(parent=func, name='start')
        self.assertIs(block.parent, func)
        self.assertFalse(block.is_terminated)

    def test_descr(self):
        block = self.block(name='my_block')
        self.assertEqual(self.descr(block), "my_block:\n")
        block.instructions.extend(['a', 'b'])
        self.assertEqual(self.descr(block), "my_block:\n  a\n  b\n")

    def test_replace(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        c = builder.add(a, b, 'c')
        d = builder.sub(a, b, 'd')
        builder.mul(d, b, 'e')
        f = ir.Instruction(block, a.type, 'sdiv', (c, b), 'f')
        block.replace(d, f)
        self.check_block(block, """\
            my_block:
                %"c" = add i32 %".1", %".2"
                %"f" = sdiv i32 %"c", %".2"
                %"e" = mul i32 %"f", %".2"
            """)

    def test_repr(self):
        """
        Blocks should have a useful repr()
        """
        func = self.function()
        block = ir.Block(parent=func, name='start')
        self.assertEqual(repr(block), "<ir.Block 'start' of type 'label'>")


class TestBuildInstructions(TestBase):
    """
    Test IR generation of LLVM instructions through the IRBuilder class.
    """

    maxDiff = 4000

    def test_simple(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        inst = builder.add(a, b, 'res')
        self.check_block(block, """\
            my_block:
                %"res" = add i32 %".1", %".2"
            """)
        # Instructions should have a useful repr()
        self.assertEqual(repr(inst),
                         "<ir.Instruction 'res' of type 'i32', opname 'add', "
                         "operands (<ir.Argument '.1' of type i32>, <ir.Argument '.2' of type i32>)>")

    def test_binops(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b, ff = builder.function.args[:3]
        builder.add(a, b, 'c')
        builder.fadd(a, b, 'd')
        builder.sub(a, b, 'e')
        builder.fsub(a, b, 'f')
        builder.mul(a, b, 'g')
        builder.fmul(a, b, 'h')
        builder.udiv(a, b, 'i')
        builder.sdiv(a, b, 'j')
        builder.fdiv(a, b, 'k')
        builder.urem(a, b, 'l')
        builder.srem(a, b, 'm')
        builder.frem(a, b, 'n')
        builder.or_(a, b, 'o')
        builder.and_(a, b, 'p')
        builder.xor(a, b, 'q')
        builder.shl(a, b, 'r')
        builder.ashr(a, b, 's')
        builder.lshr(a, b, 't')
        with self.assertRaises(ValueError) as cm:
            builder.add(a, ff)
        self.assertEqual(str(cm.exception),
                         "Operands must be the same type, got (i32, double)")
        self.assertFalse(block.is_terminated)
        self.check_block(block, """\
            my_block:
                %"c" = add i32 %".1", %".2"
                %"d" = fadd i32 %".1", %".2"
                %"e" = sub i32 %".1", %".2"
                %"f" = fsub i32 %".1", %".2"
                %"g" = mul i32 %".1", %".2"
                %"h" = fmul i32 %".1", %".2"
                %"i" = udiv i32 %".1", %".2"
                %"j" = sdiv i32 %".1", %".2"
                %"k" = fdiv i32 %".1", %".2"
                %"l" = urem i32 %".1", %".2"
                %"m" = srem i32 %".1", %".2"
                %"n" = frem i32 %".1", %".2"
                %"o" = or i32 %".1", %".2"
                %"p" = and i32 %".1", %".2"
                %"q" = xor i32 %".1", %".2"
                %"r" = shl i32 %".1", %".2"
                %"s" = ashr i32 %".1", %".2"
                %"t" = lshr i32 %".1", %".2"
            """)

    def test_binop_flags(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        # As tuple
        builder.add(a, b, 'c', flags=('nuw',))
        # and as list
        builder.sub(a, b, 'd', flags=['nuw', 'nsw'])
        self.check_block(block, """\
            my_block:
                %"c" = add nuw i32 %".1", %".2"
                %"d" = sub nuw nsw i32 %".1", %".2"
            """)

    def test_binop_fastmath_flags(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        # As tuple
        builder.fadd(a, b, 'c', flags=('fast',))
        # and as list
        builder.fsub(a, b, 'd', flags=['ninf', 'nsz'])
        self.check_block(block, """\
            my_block:
                %"c" = fadd fast i32 %".1", %".2"
                %"d" = fsub ninf nsz i32 %".1", %".2"
            """)

    def test_binops_with_overflow(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        builder.sadd_with_overflow(a, b, 'c')
        builder.smul_with_overflow(a, b, 'd')
        builder.ssub_with_overflow(a, b, 'e')
        builder.uadd_with_overflow(a, b, 'f')
        builder.umul_with_overflow(a, b, 'g')
        builder.usub_with_overflow(a, b, 'h')
        self.check_block(block, """\
            my_block:
                %"c" = call {i32, i1} @"llvm.sadd.with.overflow.i32"(i32 %".1", i32 %".2")
                %"d" = call {i32, i1} @"llvm.smul.with.overflow.i32"(i32 %".1", i32 %".2")
                %"e" = call {i32, i1} @"llvm.ssub.with.overflow.i32"(i32 %".1", i32 %".2")
                %"f" = call {i32, i1} @"llvm.uadd.with.overflow.i32"(i32 %".1", i32 %".2")
                %"g" = call {i32, i1} @"llvm.umul.with.overflow.i32"(i32 %".1", i32 %".2")
                %"h" = call {i32, i1} @"llvm.usub.with.overflow.i32"(i32 %".1", i32 %".2")
            """)

    def test_unary_ops(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        builder.neg(a, 'c')
        builder.not_(b, 'd')
        self.assertFalse(block.is_terminated)
        self.check_block(block, """\
            my_block:
                %"c" = sub i32 0, %".1"
                %"d" = xor i32 %".2", -1
            """)

    def test_integer_comparisons(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        builder.icmp_unsigned('==', a, b, 'c')
        builder.icmp_unsigned('!=', a, b, 'd')
        builder.icmp_unsigned('<', a, b, 'e')
        builder.icmp_unsigned('<=', a, b, 'f')
        builder.icmp_unsigned('>', a, b, 'g')
        builder.icmp_unsigned('>=', a, b, 'h')
        builder.icmp_signed('==', a, b, 'i')
        builder.icmp_signed('!=', a, b, 'j')
        builder.icmp_signed('<', a, b, 'k')
        builder.icmp_signed('<=', a, b, 'l')
        builder.icmp_signed('>', a, b, 'm')
        builder.icmp_signed('>=', a, b, 'n')
        with self.assertRaises(ValueError):
            builder.icmp_signed('uno', a, b, 'zz')
        with self.assertRaises(ValueError):
            builder.icmp_signed('foo', a, b, 'zz')
        self.assertFalse(block.is_terminated)
        self.check_block(block, """\
            my_block:
                %"c" = icmp eq i32 %".1", %".2"
                %"d" = icmp ne i32 %".1", %".2"
                %"e" = icmp ult i32 %".1", %".2"
                %"f" = icmp ule i32 %".1", %".2"
                %"g" = icmp ugt i32 %".1", %".2"
                %"h" = icmp uge i32 %".1", %".2"
                %"i" = icmp eq i32 %".1", %".2"
                %"j" = icmp ne i32 %".1", %".2"
                %"k" = icmp slt i32 %".1", %".2"
                %"l" = icmp sle i32 %".1", %".2"
                %"m" = icmp sgt i32 %".1", %".2"
                %"n" = icmp sge i32 %".1", %".2"
            """)

    def test_float_comparisons(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        builder.fcmp_ordered('==', a, b, 'c')
        builder.fcmp_ordered('!=', a, b, 'd')
        builder.fcmp_ordered('<', a, b, 'e')
        builder.fcmp_ordered('<=', a, b, 'f')
        builder.fcmp_ordered('>', a, b, 'g')
        builder.fcmp_ordered('>=', a, b, 'h')
        builder.fcmp_unordered('==', a, b, 'i')
        builder.fcmp_unordered('!=', a, b, 'j')
        builder.fcmp_unordered('<', a, b, 'k')
        builder.fcmp_unordered('<=', a, b, 'l')
        builder.fcmp_unordered('>', a, b, 'm')
        builder.fcmp_unordered('>=', a, b, 'n')
        # fcmp_ordered and fcmp_unordered are the same for these cases
        builder.fcmp_ordered('ord', a, b, 'u')
        builder.fcmp_ordered('uno', a, b, 'v')
        builder.fcmp_unordered('ord', a, b, 'w')
        builder.fcmp_unordered('uno', a, b, 'x')
        builder.fcmp_unordered('olt', a, b, 'y', flags=['nnan', 'ninf', 'nsz', 'arcp', 'fast'])
        self.assertFalse(block.is_terminated)
        self.check_block(block, """\
            my_block:
                %"c" = fcmp oeq i32 %".1", %".2"
                %"d" = fcmp one i32 %".1", %".2"
                %"e" = fcmp olt i32 %".1", %".2"
                %"f" = fcmp ole i32 %".1", %".2"
                %"g" = fcmp ogt i32 %".1", %".2"
                %"h" = fcmp oge i32 %".1", %".2"
                %"i" = fcmp ueq i32 %".1", %".2"
                %"j" = fcmp une i32 %".1", %".2"
                %"k" = fcmp ult i32 %".1", %".2"
                %"l" = fcmp ule i32 %".1", %".2"
                %"m" = fcmp ugt i32 %".1", %".2"
                %"n" = fcmp uge i32 %".1", %".2"
                %"u" = fcmp ord i32 %".1", %".2"
                %"v" = fcmp uno i32 %".1", %".2"
                %"w" = fcmp ord i32 %".1", %".2"
                %"x" = fcmp uno i32 %".1", %".2"
                %"y" = fcmp nnan ninf nsz arcp fast olt i32 %".1", %".2"
            """)

    def test_misc_ops(self):
        block = self.block(name='my_block')
        t = ir.Constant(int1, True)
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        builder.select(t, a, b, 'c')
        self.assertFalse(block.is_terminated)
        builder.unreachable()
        self.assertTrue(block.is_terminated)
        self.check_block(block, """\
            my_block:
                %"c" = select i1 true, i32 %".1", i32 %".2"
                unreachable
            """)

    def test_phi(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        bb2 = builder.function.append_basic_block('b2')
        bb3 = builder.function.append_basic_block('b3')
        phi = builder.phi(int32, 'my_phi')
        phi.add_incoming(a, bb2)
        phi.add_incoming(b, bb3)
        self.assertFalse(block.is_terminated)
        self.check_block(block, """\
            my_block:
                %"my_phi" = phi i32 [%".1", %"b2"], [%".2", %"b3"]
            """)

    def test_mem_ops(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b, z = builder.function.args[:3]
        c = builder.alloca(int32, name='c')
        d = builder.alloca(int32, size=42, name='d')
        e = builder.alloca(dbl, size=a, name='e')
        e.align = 8
        self.assertEqual(e.type, ir.PointerType(dbl))
        ee = builder.store(z, e)
        self.assertEqual(ee.type, ir.VoidType())
        f = builder.store(b, c)
        self.assertEqual(f.type, ir.VoidType())
        g = builder.load(c, 'g')
        self.assertEqual(g.type, int32)
        # With alignment
        h = builder.store(b, c, align=1)
        self.assertEqual(h.type, ir.VoidType())
        i = builder.load(c, 'i', align=1)
        self.assertEqual(i.type, int32)
        # Not pointer types
        with self.assertRaises(TypeError):
            builder.store(b, a)
        with self.assertRaises(TypeError):
            builder.load(b)
        # Mismatching pointer type
        with self.assertRaises(TypeError) as cm:
            builder.store(b, e)
        self.assertEqual(str(cm.exception),
                         "cannot store i32 to double*: mismatching types")
        self.check_block(block, """\
            my_block:
                %"c" = alloca i32
                %"d" = alloca i32, i32 42
                %"e" = alloca double, i32 %".1", align 8
                store double %".3", double* %"e"
                store i32 %".2", i32* %"c"
                %"g" = load i32, i32* %"c"
                store i32 %".2", i32* %"c", align 1
                %"i" = load i32, i32* %"c", align 1
            """)

    def test_gep(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        c = builder.alloca(ir.PointerType(int32), name='c')
        d = builder.gep(c, [ir.Constant(int32, 5), a], name='d')
        self.assertEqual(d.type, ir.PointerType(int32))
        self.check_block(block, """\
            my_block:
                %"c" = alloca i32*
                %"d" = getelementptr i32*, i32** %"c", i32 5, i32 %".1"
            """)
        # XXX test with more complex types

    def test_gep_castinstr(self):
        # similar to:
        # numba::runtime::nrtdynmod.py_define_nrt_meminfo_data()
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        int8ptr = int8.as_pointer()
        ls = ir.LiteralStructType([int64, int8ptr, int8ptr, int8ptr, int64])
        d = builder.bitcast(a, ls.as_pointer(), name='d')
        e = builder.gep(d, [ir.Constant(int32, x) for x in [0, 3]], name='e')
        self.assertEqual(e.type, ir.PointerType(int8ptr))
        self.check_block(block, """\
            my_block:
                %"d" = bitcast i32 %".1" to {i64, i8*, i8*, i8*, i64}*
                %"e" = getelementptr {i64, i8*, i8*, i8*, i64}, {i64, i8*, i8*, i8*, i64}* %"d", i32 0, i32 3
            """)

    def test_gep_castinstr_addrspace(self):
        # similar to:
        # numba::runtime::nrtdynmod.py_define_nrt_meminfo_data()
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        addrspace = 4
        int8ptr = int8.as_pointer()
        ls = ir.LiteralStructType([int64, int8ptr, int8ptr, int8ptr, int64])
        d = builder.bitcast(a, ls.as_pointer(addrspace=addrspace), name='d')
        e = builder.gep(d, [ir.Constant(int32, x) for x in [0, 3]], name='e')
        self.assertEqual(e.type.addrspace, addrspace)
        self.assertEqual(e.type, ir.PointerType(int8ptr, addrspace=addrspace))
        self.check_block(block, """\
            my_block:
                %"d" = bitcast i32 %".1" to {i64, i8*, i8*, i8*, i64} addrspace(4)*
                %"e" = getelementptr {i64, i8*, i8*, i8*, i64}, {i64, i8*, i8*, i8*, i64} addrspace(4)* %"d", i32 0, i32 3
            """)

    def test_gep_addrspace(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        addrspace = 4
        c = builder.alloca(ir.PointerType(int32, addrspace=addrspace), name='c')
        self.assertEqual(str(c.type), 'i32 addrspace(4)**')
        self.assertEqual(c.type.pointee.addrspace, addrspace)
        d = builder.gep(c, [ir.Constant(int32, 5), a], name='d')
        self.assertEqual(d.type.addrspace, addrspace)
        e = builder.gep(d, [ir.Constant(int32, 10)], name='e')
        self.assertEqual(e.type.addrspace, addrspace)
        self.check_block(block, """\
            my_block:
                %"c" = alloca i32 addrspace(4)*
                %"d" = getelementptr i32 addrspace(4)*, i32 addrspace(4)** %"c", i32 5, i32 %".1"
                %"e" = getelementptr i32, i32 addrspace(4)* %"d", i32 10
            """)

    def test_extract_insert_value(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        tp_inner = ir.LiteralStructType([int32, int1])
        tp_outer = ir.LiteralStructType([int8, tp_inner])
        c_inner = ir.Constant(tp_inner, (ir.Constant(int32, 4),
                                         ir.Constant(int1, True)))
        # Flat structure
        c = builder.extract_value(c_inner, 0, name='c')
        d = builder.insert_value(c_inner, a, 0, name='d')
        e = builder.insert_value(d, ir.Constant(int1, False), 1, name='e')
        self.assertEqual(d.type, tp_inner)
        self.assertEqual(e.type, tp_inner)
        # Nested structure
        p_outer = builder.alloca(tp_outer, name='ptr')
        j = builder.load(p_outer, name='j')
        k = builder.extract_value(j, 0, name='k')
        l = builder.extract_value(j, 1, name='l')
        m = builder.extract_value(j, (1, 0), name='m')
        n = builder.extract_value(j, (1, 1), name='n')
        o = builder.insert_value(j, l, 1, name='o')
        p = builder.insert_value(j, a, (1, 0), name='p')
        self.assertEqual(k.type, int8)
        self.assertEqual(l.type, tp_inner)
        self.assertEqual(m.type, int32)
        self.assertEqual(n.type, int1)
        self.assertEqual(o.type, tp_outer)
        self.assertEqual(p.type, tp_outer)

        with self.assertRaises(TypeError):
            # Not an aggregate
            builder.extract_value(p_outer, 0)
        with self.assertRaises(TypeError):
            # Indexing too deep
            builder.extract_value(c_inner, (0, 0))
        with self.assertRaises(TypeError):
            # Index out of structure bounds
            builder.extract_value(c_inner, 5)
        with self.assertRaises(TypeError):
            # Not an aggregate
            builder.insert_value(a, b, 0)
        with self.assertRaises(TypeError):
            # Replacement value has the wrong type
            builder.insert_value(c_inner, a, 1)

        self.check_block(block, """\
            my_block:
                %"c" = extractvalue {i32, i1} {i32 4, i1 true}, 0
                %"d" = insertvalue {i32, i1} {i32 4, i1 true}, i32 %".1", 0
                %"e" = insertvalue {i32, i1} %"d", i1 false, 1
                %"ptr" = alloca {i8, {i32, i1}}
                %"j" = load {i8, {i32, i1}}, {i8, {i32, i1}}* %"ptr"
                %"k" = extractvalue {i8, {i32, i1}} %"j", 0
                %"l" = extractvalue {i8, {i32, i1}} %"j", 1
                %"m" = extractvalue {i8, {i32, i1}} %"j", 1, 0
                %"n" = extractvalue {i8, {i32, i1}} %"j", 1, 1
                %"o" = insertvalue {i8, {i32, i1}} %"j", {i32, i1} %"l", 1
                %"p" = insertvalue {i8, {i32, i1}} %"j", i32 %".1", 1, 0
            """)

    def test_cast_ops(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b, fa, ptr = builder.function.args[:4]
        c = builder.trunc(a, int8, name='c')
        d = builder.zext(c, int32, name='d')
        e = builder.sext(c, int32, name='e')
        fb = builder.fptrunc(fa, flt, 'fb')
        fc = builder.fpext(fb, dbl, 'fc')
        g = builder.fptoui(fa, int32, 'g')
        h = builder.fptosi(fa, int8, 'h')
        fd = builder.uitofp(g, flt, 'fd')
        fe = builder.sitofp(h, dbl, 'fe')
        i = builder.ptrtoint(ptr, int32, 'i')
        j = builder.inttoptr(i, ir.PointerType(int8), 'j')
        k = builder.bitcast(a, flt, "k")
        self.assertFalse(block.is_terminated)
        self.check_block(block, """\
            my_block:
                %"c" = trunc i32 %".1" to i8
                %"d" = zext i8 %"c" to i32
                %"e" = sext i8 %"c" to i32
                %"fb" = fptrunc double %".3" to float
                %"fc" = fpext float %"fb" to double
                %"g" = fptoui double %".3" to i32
                %"h" = fptosi double %".3" to i8
                %"fd" = uitofp i32 %"g" to float
                %"fe" = sitofp i8 %"h" to double
                %"i" = ptrtoint i32* %".4" to i32
                %"j" = inttoptr i32 %"i" to i8*
                %"k" = bitcast i32 %".1" to float
            """)

    def test_atomicrmw(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        c = builder.alloca(int32, name='c')
        d = builder.atomic_rmw('add', c, a, 'monotonic', 'd')
        self.assertEqual(d.type, int32)
        self.check_block(block, """\
            my_block:
                %"c" = alloca i32
                %"d" = atomicrmw add i32* %"c", i32 %".1" monotonic
            """)

    def test_branch(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        bb_target = builder.function.append_basic_block(name='target')
        builder.branch(bb_target)
        self.assertTrue(block.is_terminated)
        self.check_block(block, """\
            my_block:
                br label %"target"
            """)

    def test_cbranch(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        bb_true = builder.function.append_basic_block(name='b_true')
        bb_false = builder.function.append_basic_block(name='b_false')
        builder.cbranch(ir.Constant(int1, False), bb_true, bb_false)
        self.assertTrue(block.is_terminated)
        self.check_block(block, """\
            my_block:
                br i1 false, label %"b_true", label %"b_false"
            """)

    def test_cbranch_weights(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        bb_true = builder.function.append_basic_block(name='b_true')
        bb_false = builder.function.append_basic_block(name='b_false')
        br = builder.cbranch(ir.Constant(int1, False), bb_true, bb_false)
        br.set_weights([5, 42])
        self.assertTrue(block.is_terminated)
        self.check_block(block, """\
            my_block:
                br i1 false, label %"b_true", label %"b_false", !prof !0
            """)
        self.check_metadata(builder.module, """\
            !0 = !{ !"branch_weights", i32 5, i32 42 }
            """)

    def test_branch_indirect(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        bb_1 = builder.function.append_basic_block(name='b_1')
        bb_2 = builder.function.append_basic_block(name='b_2')
        indirectbr = builder.branch_indirect(ir.BlockAddress(builder.function, bb_1))
        indirectbr.add_destination(bb_1)
        indirectbr.add_destination(bb_2)
        self.assertTrue(block.is_terminated)
        self.check_block(block, """\
            my_block:
                indirectbr i8* blockaddress(@"my_func", %"b_1"), [label %"b_1", label %"b_2"]
            """)

    def test_returns(self):
        def check(block, expected_ir):
            self.assertTrue(block.is_terminated)
            self.check_block(block, expected_ir)

        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        builder.ret_void()
        check(block, """\
            my_block:
                ret void
            """)

        block = self.block(name='other_block')
        builder = ir.IRBuilder(block)
        builder.ret(int32(5))
        check(block, """\
            other_block:
                ret i32 5
            """)

        # With metadata
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        inst = builder.ret_void()
        inst.set_metadata("dbg", block.module.add_metadata(()))
        check(block, """\
            my_block:
                ret void, !dbg !0
            """)

        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        inst = builder.ret(int32(6))
        inst.set_metadata("dbg", block.module.add_metadata(()))
        check(block, """\
            my_block:
                ret i32 6, !dbg !0
            """)

    def test_switch(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        bb_onzero = builder.function.append_basic_block(name='onzero')
        bb_onone = builder.function.append_basic_block(name='onone')
        bb_ontwo = builder.function.append_basic_block(name='ontwo')
        bb_else = builder.function.append_basic_block(name='otherwise')
        sw = builder.switch(a, bb_else)
        sw.add_case(ir.Constant(int32, 0), bb_onzero)
        sw.add_case(ir.Constant(int32, 1), bb_onone)
        # A plain Python value gets converted into the right IR constant
        sw.add_case(2, bb_ontwo)
        self.assertTrue(block.is_terminated)
        self.check_block(block, """\
            my_block:
                switch i32 %".1", label %"otherwise" [i32 0, label %"onzero" i32 1, label %"onone" i32 2, label %"ontwo"]
            """)

    def test_call(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        tp_f = ir.FunctionType(flt, (int32, int32))
        tp_g = ir.FunctionType(dbl, (int32,), var_arg=True)
        f = ir.Function(builder.function.module, tp_f, 'f')
        g = ir.Function(builder.function.module, tp_g, 'g')
        builder.call(f, (a, b), 'res_f')
        builder.call(g, (b, a), 'res_g')
        builder.call(f, (a, b), 'res_f_fast', cconv='fastcc')
        res_f_readonly = builder.call(f, (a, b), 'res_f_readonly')
        res_f_readonly.attributes.add('readonly')
        builder.call(f, (a, b), 'res_fast', fastmath='fast')
        builder.call(f, (a, b), 'res_nnan_ninf', fastmath=('nnan', 'ninf'))
        self.check_block(block, """\
            my_block:
                %"res_f" = call float @"f"(i32 %".1", i32 %".2")
                %"res_g" = call double (i32, ...) @"g"(i32 %".2", i32 %".1")
                %"res_f_fast" = call fastcc float @"f"(i32 %".1", i32 %".2")
                %"res_f_readonly" = call float @"f"(i32 %".1", i32 %".2") readonly
                %"res_fast" = call fast float @"f"(i32 %".1", i32 %".2")
                %"res_nnan_ninf" = call ninf nnan float @"f"(i32 %".1", i32 %".2")
            """)

    def test_call_metadata(self):
        """
        Function calls with metadata arguments.
        """
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        dbg_declare_ty = ir.FunctionType(ir.VoidType(), [ir.MetaDataType()] * 3)
        dbg_declare = ir.Function(builder.module, dbg_declare_ty, 'llvm.dbg.declare')
        a = builder.alloca(int32, name="a")
        b = builder.module.add_metadata(())
        builder.call(dbg_declare, (a, b, b))
        self.check_block(block, """\
            my_block:
                %"a" = alloca i32
                call void @"llvm.dbg.declare"(metadata i32* %"a", metadata !0, metadata !0)
            """)

    def test_invoke(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        tp_f = ir.FunctionType(flt, (int32, int32))
        f = ir.Function(builder.function.module, tp_f, 'f')
        bb_normal = builder.function.append_basic_block(name='normal')
        bb_unwind = builder.function.append_basic_block(name='unwind')
        builder.invoke(f, (a, b), bb_normal, bb_unwind, 'res_f')
        self.check_block(block, """\
            my_block:
                %"res_f" = invoke float @"f"(i32 %".1", i32 %".2")
                    to label %"normal" unwind label %"unwind"
            """)

    def test_landingpad(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        lp = builder.landingpad(ir.LiteralStructType([int32, int8.as_pointer()]), 'lp')
        int_typeinfo = ir.GlobalVariable(builder.function.module, int8.as_pointer(), "_ZTIi")
        int_typeinfo.global_constant = True
        lp.add_clause(ir.CatchClause(int_typeinfo))
        lp.add_clause(ir.FilterClause(ir.Constant(ir.ArrayType(int_typeinfo.type, 1),
                                                  [int_typeinfo])))
        builder.resume(lp)
        self.check_block(block, """\
            my_block:
                %"lp" = landingpad {i32, i8*}
                    catch i8** @"_ZTIi"
                    filter [1 x i8**] [i8** @"_ZTIi"]
                resume {i32, i8*} %"lp"
            """)

    def test_assume(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        c = builder.icmp_signed('>', a, b, name='c')
        builder.assume(c)
        self.check_block(block, """\
            my_block:
                %"c" = icmp sgt i32 %".1", %".2"
                call void @"llvm.assume"(i1 %"c")
            """)

    def test_bitreverse(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a = ir.Constant(int64, 5)
        c = builder.bitreverse(a, name='c')
        builder.ret(c)
        self.check_block(block, """\
            my_block:
                %"c" = call i64 @"llvm.bitreverse.i64"(i64 5)
                ret i64 %"c"
            """)

    def test_bswap(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a = ir.Constant(int32, 5)
        c = builder.bswap(a, name='c')
        builder.ret(c)
        self.check_block(block, """\
            my_block:
                %"c" = call i32 @"llvm.bswap.i32"(i32 5)
                ret i32 %"c"
            """)

    def test_ctpop(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a = ir.Constant(int16, 5)
        c = builder.ctpop(a, name='c')
        builder.ret(c)
        self.check_block(block, """\
            my_block:
                %"c" = call i16 @"llvm.ctpop.i16"(i16 5)
                ret i16 %"c"
            """)

    def test_ctlz(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a = ir.Constant(int16, 5)
        b = ir.Constant(int1, 1)
        c = builder.ctlz(a, b, name='c')
        builder.ret(c)
        self.check_block(block, """\
            my_block:
                %"c" = call i16 @"llvm.ctlz.i16"(i16 5, i1 1)
                ret i16 %"c"
            """)

    def test_cttz(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a = ir.Constant(int64, 5)
        b = ir.Constant(int1, 1)
        c = builder.cttz(a, b, name='c')
        builder.ret(c)
        self.check_block(block, """\
            my_block:
                %"c" = call i64 @"llvm.cttz.i64"(i64 5, i1 1)
                ret i64 %"c"
            """)


class TestBuilderMisc(TestBase):
    """
    Test various other features of the IRBuilder class.
    """

    def test_attributes(self):
        block = self.block(name='start')
        builder = ir.IRBuilder(block)
        self.assertIs(builder.function, block.parent)
        self.assertIsInstance(builder.function, ir.Function)<commit_msg>test error messages<commit_after>"""
IR Construction Tests
"""

from __future__ import print_function, absolute_import

import sys
import copy
import itertools
import pickle
import re
import textwrap
import unittest

from . import TestCase
from llvmlite import ir
from llvmlite import binding as llvm


int1 = ir.IntType(1)
int8 = ir.IntType(8)
int16 = ir.IntType(16)
int32 = ir.IntType(32)
int64 = ir.IntType(64)
flt = ir.FloatType()
dbl = ir.DoubleType()


PY2 = sys.version_info[0] == 2


class TestBase(TestCase):
    """
    Utilities for IR tests.
    """

    def assertInText(self, pattern, text):
        """
        Assert *pattern* is in *text*, ignoring any whitespace differences
        (including newlines).
        """

        def escape(c):
            if not c.isalnum() and not c.isspace():
                return '\\' + c
            return c

        pattern = ''.join(map(escape, pattern))
        regex = re.sub(r'\s+', r'\s*', pattern)
        self.assertRegexpMatches(text, regex)

    def assert_ir_line(self, line, mod):
        lines = [line.strip() for line in str(mod).splitlines()]
        self.assertIn(line, lines)

    def assert_valid_ir(self, mod):
        llvm.parse_assembly(str(mod))

    def module(self):
        return ir.Module()

    def function(self, module=None, name='my_func'):
        module = module or self.module()
        fnty = ir.FunctionType(int32, (int32, int32, dbl, ir.PointerType(int32)))
        return ir.Function(self.module(), fnty, name)

    def block(self, func=None, name=''):
        func = func or self.function()
        return func.append_basic_block(name)

    def descr(self, thing):
        buf = []
        thing.descr(buf)
        return "".join(buf)

    def _normalize_asm(self, asm):
        asm = textwrap.dedent(asm)
        # Normalize indent
        asm = asm.replace("\n    ", "\n  ")
        return asm

    def check_descr(self, descr, asm):
        expected = self._normalize_asm(asm)
        self.assertEqual(descr, expected)

    def check_block(self, block, asm):
        self.check_descr(self.descr(block), asm)

    def check_module_body(self, module, asm):
        expected = self._normalize_asm(asm)
        actual = module._stringify_body()
        self.assertEqual(actual.strip(), expected.strip())

    def check_metadata(self, module, asm):
        """
        Check module metadata against *asm*.
        """
        expected = self._normalize_asm(asm)
        actual = module._stringify_metadata()
        self.assertEqual(actual.strip(), expected.strip())

    def check_func_body(self, func, asm):
        expected = self._normalize_asm(asm)
        actual = self.descr(func)
        actual = actual.partition('{')[2].rpartition('}')[0]
        self.assertEqual(actual.strip(), expected.strip())


class TestFunction(TestBase):

    proto = """i32 @"my_func"(i32 %".1", i32 %".2", double %".3", i32* %".4")"""

    def test_declare(self):
        # A simple declaration
        func = self.function()
        asm = self.descr(func).strip()
        self.assertEqual(asm.strip(), "declare %s" % self.proto)

    def test_declare_attributes(self):
        # Now with function attributes
        func = self.function()
        func.attributes.add("optsize")
        func.attributes.add("alwaysinline")
        func.attributes.alignstack = 16
        tp_pers = ir.FunctionType(int8, (), var_arg=True)
        pers = ir.Function(self.module(), tp_pers, '__gxx_personality_v0')
        func.attributes.personality = pers
        asm = self.descr(func).strip()
        self.assertEqual(asm,
            ("declare %s alwaysinline optsize alignstack(16) "
             "personality i8 (...)* @\"__gxx_personality_v0\"") % self.proto)

    def test_function_attributes(self):
        # Now with parameter attributes
        func = self.function()
        func.args[0].add_attribute("zeroext")
        func.args[1].attributes.dereferenceable = 5
        func.args[1].attributes.dereferenceable_or_null = 10
        func.args[3].attributes.align = 4
        func.args[3].add_attribute("nonnull")
        func.return_value.add_attribute("noalias")
        asm = self.descr(func).strip()
        self.assertEqual(asm,
            """declare noalias i32 @"my_func"(i32 zeroext %".1", i32 dereferenceable(5) dereferenceable_or_null(10) %".2", double %".3", i32* nonnull align 4 %".4")"""
            )

    def test_function_metadata(self):
        # Now with function metadata
        module = self.module()
        func = self.function(module)
        func.set_metadata('dbg', module.add_metadata([]))
        asm = self.descr(func).strip()
        self.assertEqual(asm,
            """declare i32 @"my_func"(i32 %".1", i32 %".2", double %".3", i32* %".4") !dbg !0"""
            )

    def test_define(self):
        # A simple definition
        func = self.function()
        func.attributes.add("alwaysinline")
        block = func.append_basic_block('my_block')
        builder = ir.IRBuilder(block)
        builder.ret_void()
        asm = self.descr(func)
        self.check_descr(asm, """\
            define {proto} alwaysinline
            {{
            my_block:
                ret void
            }}
            """.format(proto=self.proto))

    def test_declare_intrinsics(self):
        module = self.module()
        pint8 = int8.as_pointer()

        powi = module.declare_intrinsic('llvm.powi', [dbl])
        memset = module.declare_intrinsic('llvm.memset', [pint8, int32])
        memcpy = module.declare_intrinsic('llvm.memcpy', [pint8, pint8, int32])
        assume = module.declare_intrinsic('llvm.assume')
        self.check_descr(self.descr(powi).strip(), """\
            declare double @"llvm.powi.f64"(double %".1", i32 %".2")""")
        self.check_descr(self.descr(memset).strip(), """\
            declare void @"llvm.memset.p0i8.i32"(i8* %".1", i8 %".2", i32 %".3", i32 %".4", i1 %".5")""")
        self.check_descr(self.descr(memcpy).strip(), """\
            declare void @"llvm.memcpy.p0i8.p0i8.i32"(i8* %".1", i8* %".2", i32 %".3", i32 %".4", i1 %".5")""")
        self.check_descr(self.descr(assume).strip(), """\
            declare void @"llvm.assume"(i1 %".1")""")

    def test_redeclare_intrinsic(self):
        module = self.module()
        powi = module.declare_intrinsic('llvm.powi', [dbl])
        powi2 = module.declare_intrinsic('llvm.powi', [dbl])
        self.assertIs(powi, powi2)


class TestIR(TestBase):

    def test_unnamed_metadata(self):
        # An unnamed metadata node
        mod = self.module()
        md = mod.add_metadata([int32(123), int8(42)])
        self.assert_ir_line("!0 = !{ i32 123, i8 42 }", mod)
        self.assert_valid_ir(mod)

    def test_unnamed_metadata_2(self):
        # Several unnamed metadata nodes
        mod = self.module()
        # First node has a literal metadata string
        m0 = mod.add_metadata([int32(123), "kernel"])
        # Second node refers to the first one
        m1 = mod.add_metadata([int64(456), m0])
        # Third node is the same as the second one
        m2 = mod.add_metadata([int64(456), m0])
        self.assertIs(m2, m1)
        # Fourth node refers to the first three
        m3 = mod.add_metadata([m0, m1, m2])
        self.assert_ir_line('!0 = !{ i32 123, !"kernel" }', mod)
        self.assert_ir_line('!1 = !{ i64 456, !0 }', mod)
        self.assert_ir_line('!2 = !{ !0, !1, !1 }', mod)

    def test_unnamed_metadata_3(self):
        # Passing nested metadata as a sequence
        mod = self.module()
        mod.add_metadata([int32(123), [int32(456)], [int32(789)], [int32(456)]])
        self.assert_ir_line('!0 = !{ i32 456 }', mod)
        self.assert_ir_line('!1 = !{ i32 789 }', mod)
        self.assert_ir_line('!2 = !{ i32 123, !0, !1, !0 }', mod)

    def test_metadata_string(self):
        # Escaping contents of a metadata string
        mod = self.module()
        mod.add_metadata(["\"\\$"])
        self.assert_ir_line('!0 = !{ !"\\22\\5c$" }', mod)

    def test_named_metadata(self):
        # Add a named metadata node and add metadata values to it
        mod = self.module()
        m0 = mod.add_metadata([int32(123)])
        m1 = mod.add_metadata([int64(456)])
        nmd = mod.add_named_metadata("foo")
        nmd.add(m0)
        nmd.add(m1)
        nmd.add(m0)
        self.assert_ir_line("!foo = !{ !0, !1, !0 }", mod)
        self.assert_valid_ir(mod)
        # Check get_named_metadata()
        self.assertIs(nmd, mod.get_named_metadata("foo"))
        with self.assertRaises(KeyError):
            mod.get_named_metadata("bar")

    def test_named_metadata_2(self):
        # Add and set named metadata through a single add_named_metadata() call
        mod = self.module()
        m0 = mod.add_metadata([int32(123)])
        mod.add_named_metadata("foo", m0)
        mod.add_named_metadata("foo", [int64(456)])
        mod.add_named_metadata("foo", ["kernel"])
        mod.add_named_metadata("bar", [])
        self.assert_ir_line("!foo = !{ !0, !1, !2 }", mod)
        self.assert_ir_line("!0 = !{ i32 123 }", mod)
        self.assert_ir_line("!1 = !{ i64 456 }", mod)
        self.assert_ir_line('!2 = !{ !"kernel" }', mod)
        self.assert_ir_line("!bar = !{ !3 }", mod)
        self.assert_ir_line('!3 = !{  }', mod)
        self.assert_valid_ir(mod)

    def test_metadata_null(self):
        # A null metadata (typed) value
        mod = self.module()
        md = mod.add_metadata([int32.as_pointer()(None)])
        self.assert_ir_line("!0 = !{ i32* null }", mod)
        self.assert_valid_ir(mod)
        # A null metadata (untyped) value
        mod = self.module()
        md = mod.add_metadata([None, int32(123)])
        self.assert_ir_line("!0 = !{ null, i32 123 }", mod)
        self.assert_valid_ir(mod)

    def test_debug_info(self):
        # Add real world-looking debug information to a module
        # (with various value types)
        mod = self.module()
        di_file = mod.add_debug_info("DIFile", {
            "filename":        "foo",
            "directory":       "bar",
        })
        di_func_type = mod.add_debug_info("DISubroutineType", {
            # None as `null`
            "types":           mod.add_metadata([None]),
            })
        di_compileunit = mod.add_debug_info("DICompileUnit", {
            "language":        ir.DIToken("DW_LANG_Python"),
            "file":            di_file,
            "producer":        "ARTIQ",
            "runtimeVersion":  0,
            "isOptimized":     True,
        }, is_distinct=True)
        di_func = mod.add_debug_info("DISubprogram", {
            "name":            "my_func",
            "file":            di_file,
            "line":            11,
            "type":            di_func_type,
            "isLocal":         False,
            "unit":            di_compileunit,
            }, is_distinct=True)

        # Check output
        strmod = str(mod)
        self.assert_ir_line('!0 = !DIFile(directory: "bar", filename: "foo")',
                            strmod)
        self.assert_ir_line('!1 = !{ null }', strmod)
        self.assert_ir_line('!2 = !DISubroutineType(types: !1)', strmod)
        # self.assert_ir_line('!4 = !{ !3 }', strmod)
        self.assert_ir_line('!3 = distinct !DICompileUnit(file: !0, '
                            'isOptimized: true, language: DW_LANG_Python, '
                            'producer: "ARTIQ", runtimeVersion: 0)',
                            strmod)
        self.assert_ir_line('!4 = distinct !DISubprogram(file: !0, isLocal: false, '
                            'line: 11, name: "my_func", type: !2, unit: !3)',
                            strmod)
        self.assert_valid_ir(mod)

    def test_debug_info_2(self):
        # Identical debug info nodes should be merged
        mod = self.module()
        di1 = mod.add_debug_info("DIFile",
                                 {"filename": "foo",
                                  "directory": "bar",
                                 })
        di2 = mod.add_debug_info("DIFile",
                                 {"filename": "foo",
                                  "directory": "bar",
                                 })
        di3 = mod.add_debug_info("DIFile",
                                 {"filename": "bar",
                                  "directory": "foo",
                                 })
        di4 = mod.add_debug_info("DIFile",
                                 {"filename": "foo",
                                  "directory": "bar",
                                 }, is_distinct=True)
        self.assertIs(di1, di2)
        self.assertEqual(len({di1, di2, di3, di4}), 3)
        # Check output
        strmod = str(mod)
        self.assert_ir_line('!0 = !DIFile(directory: "bar", filename: "foo")',
                            strmod)
        self.assert_ir_line('!1 = !DIFile(directory: "foo", filename: "bar")',
                            strmod)
        self.assert_ir_line('!2 = distinct !DIFile(directory: "bar", filename: "foo")',
                            strmod)
        self.assert_valid_ir(mod)

    @unittest.skipUnless(PY2, 'py2 only')
    def test_debug_info_py2_long(self):
        mod = self.module()
        di = mod.add_debug_info("DIBasicType",
                                {"name": "foo",
                                 "size": long(123)})  # long integer here
        self.assert_ir_line('!0 = !DIBasicType(name: "foo", size: 123)',
                            str(di))
        self.assert_valid_ir(mod)

    def test_inline_assembly(self):
        mod = self.module()
        foo = ir.Function(mod, ir.FunctionType(ir.VoidType(), []), 'foo')
        builder = ir.IRBuilder(foo.append_basic_block(''))
        asmty = ir.FunctionType(int32, [int32])
        asm = ir.InlineAsm(asmty, "mov $1, $2", "=r,r", side_effect=True)
        builder.call(asm, [int32(123)])
        builder.ret_void()
        pat = 'call i32 asm sideeffect "mov $1, $2", "=r,r" ( i32 123 )'
        self.assertInText(pat, str(mod))
        self.assert_valid_ir(mod)

    def test_builder_asm(self):
        mod = self.module()
        foo = ir.Function(mod, ir.FunctionType(ir.VoidType(), []), 'foo')
        builder = ir.IRBuilder(foo.append_basic_block(''))
        asmty = ir.FunctionType(int32, [int32])
        builder.asm(asmty, "mov $1, $2", "=r,r", [int32(123)], side_effect=True)
        builder.ret_void()
        pat = 'call i32 asm sideeffect "mov $1, $2", "=r,r" ( i32 123 )'
        self.assertInText(pat, str(mod))
        self.assert_valid_ir(mod)

    def test_builder_load_reg(self):
        mod = self.module()
        foo = ir.Function(mod, ir.FunctionType(ir.VoidType(), []), 'foo')
        builder = ir.IRBuilder(foo.append_basic_block(''))
        asmty = ir.FunctionType(int32, [int32])
        builder.load_reg(ir.IntType(64), "rax")
        builder.ret_void()
        pat = 'call i64 asm "", "={rax}"'
        self.assertInText(pat, str(mod))
        self.assert_valid_ir(mod)

    def test_builder_store_reg(self):
        mod = self.module()
        foo = ir.Function(mod, ir.FunctionType(ir.VoidType(), []), 'foo')
        builder = ir.IRBuilder(foo.append_basic_block(''))
        asmty = ir.FunctionType(ir.VoidType(), [int32])
        builder.store_reg(int64(123), ir.IntType(64), "rax")
        builder.ret_void()
        pat = 'call void asm sideeffect "", "{rax}" ( i64 123 )'
        self.assertInText(pat, str(mod))
        self.assert_valid_ir(mod)

class TestGlobalValues(TestBase):

    def test_globals_access(self):
        mod = self.module()
        foo = ir.Function(mod, ir.FunctionType(ir.VoidType(), []), 'foo')
        ir.Function(mod, ir.FunctionType(ir.VoidType(), []), 'bar')
        globdouble = ir.GlobalVariable(mod, ir.DoubleType(), 'globdouble')
        self.assertEqual(mod.get_global('foo'), foo)
        self.assertEqual(mod.get_global('globdouble'), globdouble)
        with self.assertRaises(KeyError):
            mod.get_global('kkk')
        # Globals should have a useful repr()
        self.assertEqual(repr(globdouble),
                         "<ir.GlobalVariable 'globdouble' of type 'double*'>")

    def test_functions_global_values_access(self):
        """
        Accessing functions and global values through Module.functions
        and Module.global_values.
        """
        mod = self.module()
        fty = ir.FunctionType(ir.VoidType(), [])
        foo = ir.Function(mod, fty, 'foo')
        bar = ir.Function(mod, fty, 'bar')
        globdouble = ir.GlobalVariable(mod, ir.DoubleType(), 'globdouble')
        self.assertEqual(set(mod.functions), set((foo, bar)))
        self.assertEqual(set(mod.global_values), set((foo, bar, globdouble)))

    def test_global_variables_ir(self):
        """
        IR serialization of global variables.
        """
        mod = self.module()
        a = ir.GlobalVariable(mod, int8, 'a')
        b = ir.GlobalVariable(mod, int8, 'b', addrspace=42)
        # Initialized global variable doesn't default to "external"
        c = ir.GlobalVariable(mod, int32, 'c')
        c.initializer = int32(123)
        d = ir.GlobalVariable(mod, int32, 'd')
        d.global_constant = True
        # Non-external linkage implies default "undef" initializer
        e = ir.GlobalVariable(mod, int32, 'e')
        e.linkage = "internal"
        f = ir.GlobalVariable(mod, int32, 'f', addrspace=456)
        f.unnamed_addr = True
        g = ir.GlobalVariable(mod, int32, 'g')
        g.linkage = "internal"
        g.initializer = int32(123)
        g.align = 16
        self.check_module_body(mod, """\
            @"a" = external global i8
            @"b" = external addrspace(42) global i8
            @"c" = global i32 123
            @"d" = external constant i32
            @"e" = internal global i32 undef
            @"f" = external unnamed_addr addrspace(456) global i32
            @"g" = internal global i32 123, align 16
            """)


class TestBlock(TestBase):

    def test_attributes(self):
        func = self.function()
        block = ir.Block(parent=func, name='start')
        self.assertIs(block.parent, func)
        self.assertFalse(block.is_terminated)

    def test_descr(self):
        block = self.block(name='my_block')
        self.assertEqual(self.descr(block), "my_block:\n")
        block.instructions.extend(['a', 'b'])
        self.assertEqual(self.descr(block), "my_block:\n  a\n  b\n")

    def test_replace(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        c = builder.add(a, b, 'c')
        d = builder.sub(a, b, 'd')
        builder.mul(d, b, 'e')
        f = ir.Instruction(block, a.type, 'sdiv', (c, b), 'f')
        block.replace(d, f)
        self.check_block(block, """\
            my_block:
                %"c" = add i32 %".1", %".2"
                %"f" = sdiv i32 %"c", %".2"
                %"e" = mul i32 %"f", %".2"
            """)

    def test_repr(self):
        """
        Blocks should have a useful repr()
        """
        func = self.function()
        block = ir.Block(parent=func, name='start')
        self.assertEqual(repr(block), "<ir.Block 'start' of type 'label'>")


class TestBuildInstructions(TestBase):
    """
    Test IR generation of LLVM instructions through the IRBuilder class.
    """

    maxDiff = 4000

    def test_simple(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        inst = builder.add(a, b, 'res')
        self.check_block(block, """\
            my_block:
                %"res" = add i32 %".1", %".2"
            """)
        # Instructions should have a useful repr()
        self.assertEqual(repr(inst),
                         "<ir.Instruction 'res' of type 'i32', opname 'add', "
                         "operands (<ir.Argument '.1' of type i32>, <ir.Argument '.2' of type i32>)>")

    def test_binops(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b, ff = builder.function.args[:3]
        builder.add(a, b, 'c')
        builder.fadd(a, b, 'd')
        builder.sub(a, b, 'e')
        builder.fsub(a, b, 'f')
        builder.mul(a, b, 'g')
        builder.fmul(a, b, 'h')
        builder.udiv(a, b, 'i')
        builder.sdiv(a, b, 'j')
        builder.fdiv(a, b, 'k')
        builder.urem(a, b, 'l')
        builder.srem(a, b, 'm')
        builder.frem(a, b, 'n')
        builder.or_(a, b, 'o')
        builder.and_(a, b, 'p')
        builder.xor(a, b, 'q')
        builder.shl(a, b, 'r')
        builder.ashr(a, b, 's')
        builder.lshr(a, b, 't')
        with self.assertRaises(ValueError) as cm:
            builder.add(a, ff)
        self.assertEqual(str(cm.exception),
                         "Operands must be the same type, got (i32, double)")
        self.assertFalse(block.is_terminated)
        self.check_block(block, """\
            my_block:
                %"c" = add i32 %".1", %".2"
                %"d" = fadd i32 %".1", %".2"
                %"e" = sub i32 %".1", %".2"
                %"f" = fsub i32 %".1", %".2"
                %"g" = mul i32 %".1", %".2"
                %"h" = fmul i32 %".1", %".2"
                %"i" = udiv i32 %".1", %".2"
                %"j" = sdiv i32 %".1", %".2"
                %"k" = fdiv i32 %".1", %".2"
                %"l" = urem i32 %".1", %".2"
                %"m" = srem i32 %".1", %".2"
                %"n" = frem i32 %".1", %".2"
                %"o" = or i32 %".1", %".2"
                %"p" = and i32 %".1", %".2"
                %"q" = xor i32 %".1", %".2"
                %"r" = shl i32 %".1", %".2"
                %"s" = ashr i32 %".1", %".2"
                %"t" = lshr i32 %".1", %".2"
            """)

    def test_binop_flags(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        # As tuple
        builder.add(a, b, 'c', flags=('nuw',))
        # and as list
        builder.sub(a, b, 'd', flags=['nuw', 'nsw'])
        self.check_block(block, """\
            my_block:
                %"c" = add nuw i32 %".1", %".2"
                %"d" = sub nuw nsw i32 %".1", %".2"
            """)

    def test_binop_fastmath_flags(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        # As tuple
        builder.fadd(a, b, 'c', flags=('fast',))
        # and as list
        builder.fsub(a, b, 'd', flags=['ninf', 'nsz'])
        self.check_block(block, """\
            my_block:
                %"c" = fadd fast i32 %".1", %".2"
                %"d" = fsub ninf nsz i32 %".1", %".2"
            """)

    def test_binops_with_overflow(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        builder.sadd_with_overflow(a, b, 'c')
        builder.smul_with_overflow(a, b, 'd')
        builder.ssub_with_overflow(a, b, 'e')
        builder.uadd_with_overflow(a, b, 'f')
        builder.umul_with_overflow(a, b, 'g')
        builder.usub_with_overflow(a, b, 'h')
        self.check_block(block, """\
            my_block:
                %"c" = call {i32, i1} @"llvm.sadd.with.overflow.i32"(i32 %".1", i32 %".2")
                %"d" = call {i32, i1} @"llvm.smul.with.overflow.i32"(i32 %".1", i32 %".2")
                %"e" = call {i32, i1} @"llvm.ssub.with.overflow.i32"(i32 %".1", i32 %".2")
                %"f" = call {i32, i1} @"llvm.uadd.with.overflow.i32"(i32 %".1", i32 %".2")
                %"g" = call {i32, i1} @"llvm.umul.with.overflow.i32"(i32 %".1", i32 %".2")
                %"h" = call {i32, i1} @"llvm.usub.with.overflow.i32"(i32 %".1", i32 %".2")
            """)

    def test_unary_ops(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        builder.neg(a, 'c')
        builder.not_(b, 'd')
        self.assertFalse(block.is_terminated)
        self.check_block(block, """\
            my_block:
                %"c" = sub i32 0, %".1"
                %"d" = xor i32 %".2", -1
            """)

    def test_integer_comparisons(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        builder.icmp_unsigned('==', a, b, 'c')
        builder.icmp_unsigned('!=', a, b, 'd')
        builder.icmp_unsigned('<', a, b, 'e')
        builder.icmp_unsigned('<=', a, b, 'f')
        builder.icmp_unsigned('>', a, b, 'g')
        builder.icmp_unsigned('>=', a, b, 'h')
        builder.icmp_signed('==', a, b, 'i')
        builder.icmp_signed('!=', a, b, 'j')
        builder.icmp_signed('<', a, b, 'k')
        builder.icmp_signed('<=', a, b, 'l')
        builder.icmp_signed('>', a, b, 'm')
        builder.icmp_signed('>=', a, b, 'n')
        with self.assertRaises(ValueError):
            builder.icmp_signed('uno', a, b, 'zz')
        with self.assertRaises(ValueError):
            builder.icmp_signed('foo', a, b, 'zz')
        self.assertFalse(block.is_terminated)
        self.check_block(block, """\
            my_block:
                %"c" = icmp eq i32 %".1", %".2"
                %"d" = icmp ne i32 %".1", %".2"
                %"e" = icmp ult i32 %".1", %".2"
                %"f" = icmp ule i32 %".1", %".2"
                %"g" = icmp ugt i32 %".1", %".2"
                %"h" = icmp uge i32 %".1", %".2"
                %"i" = icmp eq i32 %".1", %".2"
                %"j" = icmp ne i32 %".1", %".2"
                %"k" = icmp slt i32 %".1", %".2"
                %"l" = icmp sle i32 %".1", %".2"
                %"m" = icmp sgt i32 %".1", %".2"
                %"n" = icmp sge i32 %".1", %".2"
            """)

    def test_float_comparisons(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        builder.fcmp_ordered('==', a, b, 'c')
        builder.fcmp_ordered('!=', a, b, 'd')
        builder.fcmp_ordered('<', a, b, 'e')
        builder.fcmp_ordered('<=', a, b, 'f')
        builder.fcmp_ordered('>', a, b, 'g')
        builder.fcmp_ordered('>=', a, b, 'h')
        builder.fcmp_unordered('==', a, b, 'i')
        builder.fcmp_unordered('!=', a, b, 'j')
        builder.fcmp_unordered('<', a, b, 'k')
        builder.fcmp_unordered('<=', a, b, 'l')
        builder.fcmp_unordered('>', a, b, 'm')
        builder.fcmp_unordered('>=', a, b, 'n')
        # fcmp_ordered and fcmp_unordered are the same for these cases
        builder.fcmp_ordered('ord', a, b, 'u')
        builder.fcmp_ordered('uno', a, b, 'v')
        builder.fcmp_unordered('ord', a, b, 'w')
        builder.fcmp_unordered('uno', a, b, 'x')
        builder.fcmp_unordered('olt', a, b, 'y', flags=['nnan', 'ninf', 'nsz', 'arcp', 'fast'])
        self.assertFalse(block.is_terminated)
        self.check_block(block, """\
            my_block:
                %"c" = fcmp oeq i32 %".1", %".2"
                %"d" = fcmp one i32 %".1", %".2"
                %"e" = fcmp olt i32 %".1", %".2"
                %"f" = fcmp ole i32 %".1", %".2"
                %"g" = fcmp ogt i32 %".1", %".2"
                %"h" = fcmp oge i32 %".1", %".2"
                %"i" = fcmp ueq i32 %".1", %".2"
                %"j" = fcmp une i32 %".1", %".2"
                %"k" = fcmp ult i32 %".1", %".2"
                %"l" = fcmp ule i32 %".1", %".2"
                %"m" = fcmp ugt i32 %".1", %".2"
                %"n" = fcmp uge i32 %".1", %".2"
                %"u" = fcmp ord i32 %".1", %".2"
                %"v" = fcmp uno i32 %".1", %".2"
                %"w" = fcmp ord i32 %".1", %".2"
                %"x" = fcmp uno i32 %".1", %".2"
                %"y" = fcmp nnan ninf nsz arcp fast olt i32 %".1", %".2"
            """)

    def test_misc_ops(self):
        block = self.block(name='my_block')
        t = ir.Constant(int1, True)
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        builder.select(t, a, b, 'c')
        self.assertFalse(block.is_terminated)
        builder.unreachable()
        self.assertTrue(block.is_terminated)
        self.check_block(block, """\
            my_block:
                %"c" = select i1 true, i32 %".1", i32 %".2"
                unreachable
            """)

    def test_phi(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        bb2 = builder.function.append_basic_block('b2')
        bb3 = builder.function.append_basic_block('b3')
        phi = builder.phi(int32, 'my_phi')
        phi.add_incoming(a, bb2)
        phi.add_incoming(b, bb3)
        self.assertFalse(block.is_terminated)
        self.check_block(block, """\
            my_block:
                %"my_phi" = phi i32 [%".1", %"b2"], [%".2", %"b3"]
            """)

    def test_mem_ops(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b, z = builder.function.args[:3]
        c = builder.alloca(int32, name='c')
        d = builder.alloca(int32, size=42, name='d')
        e = builder.alloca(dbl, size=a, name='e')
        e.align = 8
        self.assertEqual(e.type, ir.PointerType(dbl))
        ee = builder.store(z, e)
        self.assertEqual(ee.type, ir.VoidType())
        f = builder.store(b, c)
        self.assertEqual(f.type, ir.VoidType())
        g = builder.load(c, 'g')
        self.assertEqual(g.type, int32)
        # With alignment
        h = builder.store(b, c, align=1)
        self.assertEqual(h.type, ir.VoidType())
        i = builder.load(c, 'i', align=1)
        self.assertEqual(i.type, int32)
        # Not pointer types
        with self.assertRaises(TypeError):
            builder.store(b, a)
        with self.assertRaises(TypeError):
            builder.load(b)
        # Mismatching pointer type
        with self.assertRaises(TypeError) as cm:
            builder.store(b, e)
        self.assertEqual(str(cm.exception),
                         "cannot store i32 to double*: mismatching types")
        self.check_block(block, """\
            my_block:
                %"c" = alloca i32
                %"d" = alloca i32, i32 42
                %"e" = alloca double, i32 %".1", align 8
                store double %".3", double* %"e"
                store i32 %".2", i32* %"c"
                %"g" = load i32, i32* %"c"
                store i32 %".2", i32* %"c", align 1
                %"i" = load i32, i32* %"c", align 1
            """)

    def test_gep(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        c = builder.alloca(ir.PointerType(int32), name='c')
        d = builder.gep(c, [ir.Constant(int32, 5), a], name='d')
        self.assertEqual(d.type, ir.PointerType(int32))
        self.check_block(block, """\
            my_block:
                %"c" = alloca i32*
                %"d" = getelementptr i32*, i32** %"c", i32 5, i32 %".1"
            """)
        # XXX test with more complex types

    def test_gep_castinstr(self):
        # similar to:
        # numba::runtime::nrtdynmod.py_define_nrt_meminfo_data()
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        int8ptr = int8.as_pointer()
        ls = ir.LiteralStructType([int64, int8ptr, int8ptr, int8ptr, int64])
        d = builder.bitcast(a, ls.as_pointer(), name='d')
        e = builder.gep(d, [ir.Constant(int32, x) for x in [0, 3]], name='e')
        self.assertEqual(e.type, ir.PointerType(int8ptr))
        self.check_block(block, """\
            my_block:
                %"d" = bitcast i32 %".1" to {i64, i8*, i8*, i8*, i64}*
                %"e" = getelementptr {i64, i8*, i8*, i8*, i64}, {i64, i8*, i8*, i8*, i64}* %"d", i32 0, i32 3
            """)

    def test_gep_castinstr_addrspace(self):
        # similar to:
        # numba::runtime::nrtdynmod.py_define_nrt_meminfo_data()
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        addrspace = 4
        int8ptr = int8.as_pointer()
        ls = ir.LiteralStructType([int64, int8ptr, int8ptr, int8ptr, int64])
        d = builder.bitcast(a, ls.as_pointer(addrspace=addrspace), name='d')
        e = builder.gep(d, [ir.Constant(int32, x) for x in [0, 3]], name='e')
        self.assertEqual(e.type.addrspace, addrspace)
        self.assertEqual(e.type, ir.PointerType(int8ptr, addrspace=addrspace))
        self.check_block(block, """\
            my_block:
                %"d" = bitcast i32 %".1" to {i64, i8*, i8*, i8*, i64} addrspace(4)*
                %"e" = getelementptr {i64, i8*, i8*, i8*, i64}, {i64, i8*, i8*, i8*, i64} addrspace(4)* %"d", i32 0, i32 3
            """)

    def test_gep_addrspace(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        addrspace = 4
        c = builder.alloca(ir.PointerType(int32, addrspace=addrspace), name='c')
        self.assertEqual(str(c.type), 'i32 addrspace(4)**')
        self.assertEqual(c.type.pointee.addrspace, addrspace)
        d = builder.gep(c, [ir.Constant(int32, 5), a], name='d')
        self.assertEqual(d.type.addrspace, addrspace)
        e = builder.gep(d, [ir.Constant(int32, 10)], name='e')
        self.assertEqual(e.type.addrspace, addrspace)
        self.check_block(block, """\
            my_block:
                %"c" = alloca i32 addrspace(4)*
                %"d" = getelementptr i32 addrspace(4)*, i32 addrspace(4)** %"c", i32 5, i32 %".1"
                %"e" = getelementptr i32, i32 addrspace(4)* %"d", i32 10
            """)

    def test_extract_insert_value(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        tp_inner = ir.LiteralStructType([int32, int1])
        tp_outer = ir.LiteralStructType([int8, tp_inner])
        c_inner = ir.Constant(tp_inner, (ir.Constant(int32, 4),
                                         ir.Constant(int1, True)))
        # Flat structure
        c = builder.extract_value(c_inner, 0, name='c')
        d = builder.insert_value(c_inner, a, 0, name='d')
        e = builder.insert_value(d, ir.Constant(int1, False), 1, name='e')
        self.assertEqual(d.type, tp_inner)
        self.assertEqual(e.type, tp_inner)
        # Nested structure
        p_outer = builder.alloca(tp_outer, name='ptr')
        j = builder.load(p_outer, name='j')
        k = builder.extract_value(j, 0, name='k')
        l = builder.extract_value(j, 1, name='l')
        m = builder.extract_value(j, (1, 0), name='m')
        n = builder.extract_value(j, (1, 1), name='n')
        o = builder.insert_value(j, l, 1, name='o')
        p = builder.insert_value(j, a, (1, 0), name='p')
        self.assertEqual(k.type, int8)
        self.assertEqual(l.type, tp_inner)
        self.assertEqual(m.type, int32)
        self.assertEqual(n.type, int1)
        self.assertEqual(o.type, tp_outer)
        self.assertEqual(p.type, tp_outer)

        with self.assertRaises(TypeError):
            # Not an aggregate
            builder.extract_value(p_outer, 0)
        with self.assertRaises(TypeError):
            # Indexing too deep
            builder.extract_value(c_inner, (0, 0))
        with self.assertRaises(TypeError):
            # Index out of structure bounds
            builder.extract_value(c_inner, 5)
        with self.assertRaises(TypeError):
            # Not an aggregate
            builder.insert_value(a, b, 0)
        with self.assertRaises(TypeError):
            # Replacement value has the wrong type
            builder.insert_value(c_inner, a, 1)

        self.check_block(block, """\
            my_block:
                %"c" = extractvalue {i32, i1} {i32 4, i1 true}, 0
                %"d" = insertvalue {i32, i1} {i32 4, i1 true}, i32 %".1", 0
                %"e" = insertvalue {i32, i1} %"d", i1 false, 1
                %"ptr" = alloca {i8, {i32, i1}}
                %"j" = load {i8, {i32, i1}}, {i8, {i32, i1}}* %"ptr"
                %"k" = extractvalue {i8, {i32, i1}} %"j", 0
                %"l" = extractvalue {i8, {i32, i1}} %"j", 1
                %"m" = extractvalue {i8, {i32, i1}} %"j", 1, 0
                %"n" = extractvalue {i8, {i32, i1}} %"j", 1, 1
                %"o" = insertvalue {i8, {i32, i1}} %"j", {i32, i1} %"l", 1
                %"p" = insertvalue {i8, {i32, i1}} %"j", i32 %".1", 1, 0
            """)

    def test_cast_ops(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b, fa, ptr = builder.function.args[:4]
        c = builder.trunc(a, int8, name='c')
        d = builder.zext(c, int32, name='d')
        e = builder.sext(c, int32, name='e')
        fb = builder.fptrunc(fa, flt, 'fb')
        fc = builder.fpext(fb, dbl, 'fc')
        g = builder.fptoui(fa, int32, 'g')
        h = builder.fptosi(fa, int8, 'h')
        fd = builder.uitofp(g, flt, 'fd')
        fe = builder.sitofp(h, dbl, 'fe')
        i = builder.ptrtoint(ptr, int32, 'i')
        j = builder.inttoptr(i, ir.PointerType(int8), 'j')
        k = builder.bitcast(a, flt, "k")
        self.assertFalse(block.is_terminated)
        self.check_block(block, """\
            my_block:
                %"c" = trunc i32 %".1" to i8
                %"d" = zext i8 %"c" to i32
                %"e" = sext i8 %"c" to i32
                %"fb" = fptrunc double %".3" to float
                %"fc" = fpext float %"fb" to double
                %"g" = fptoui double %".3" to i32
                %"h" = fptosi double %".3" to i8
                %"fd" = uitofp i32 %"g" to float
                %"fe" = sitofp i8 %"h" to double
                %"i" = ptrtoint i32* %".4" to i32
                %"j" = inttoptr i32 %"i" to i8*
                %"k" = bitcast i32 %".1" to float
            """)

    def test_atomicrmw(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        c = builder.alloca(int32, name='c')
        d = builder.atomic_rmw('add', c, a, 'monotonic', 'd')
        self.assertEqual(d.type, int32)
        self.check_block(block, """\
            my_block:
                %"c" = alloca i32
                %"d" = atomicrmw add i32* %"c", i32 %".1" monotonic
            """)

    def test_branch(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        bb_target = builder.function.append_basic_block(name='target')
        builder.branch(bb_target)
        self.assertTrue(block.is_terminated)
        self.check_block(block, """\
            my_block:
                br label %"target"
            """)

    def test_cbranch(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        bb_true = builder.function.append_basic_block(name='b_true')
        bb_false = builder.function.append_basic_block(name='b_false')
        builder.cbranch(ir.Constant(int1, False), bb_true, bb_false)
        self.assertTrue(block.is_terminated)
        self.check_block(block, """\
            my_block:
                br i1 false, label %"b_true", label %"b_false"
            """)

    def test_cbranch_weights(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        bb_true = builder.function.append_basic_block(name='b_true')
        bb_false = builder.function.append_basic_block(name='b_false')
        br = builder.cbranch(ir.Constant(int1, False), bb_true, bb_false)
        br.set_weights([5, 42])
        self.assertTrue(block.is_terminated)
        self.check_block(block, """\
            my_block:
                br i1 false, label %"b_true", label %"b_false", !prof !0
            """)
        self.check_metadata(builder.module, """\
            !0 = !{ !"branch_weights", i32 5, i32 42 }
            """)

    def test_branch_indirect(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        bb_1 = builder.function.append_basic_block(name='b_1')
        bb_2 = builder.function.append_basic_block(name='b_2')
        indirectbr = builder.branch_indirect(ir.BlockAddress(builder.function, bb_1))
        indirectbr.add_destination(bb_1)
        indirectbr.add_destination(bb_2)
        self.assertTrue(block.is_terminated)
        self.check_block(block, """\
            my_block:
                indirectbr i8* blockaddress(@"my_func", %"b_1"), [label %"b_1", label %"b_2"]
            """)

    def test_returns(self):
        def check(block, expected_ir):
            self.assertTrue(block.is_terminated)
            self.check_block(block, expected_ir)

        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        builder.ret_void()
        check(block, """\
            my_block:
                ret void
            """)

        block = self.block(name='other_block')
        builder = ir.IRBuilder(block)
        builder.ret(int32(5))
        check(block, """\
            other_block:
                ret i32 5
            """)

        # With metadata
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        inst = builder.ret_void()
        inst.set_metadata("dbg", block.module.add_metadata(()))
        check(block, """\
            my_block:
                ret void, !dbg !0
            """)

        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        inst = builder.ret(int32(6))
        inst.set_metadata("dbg", block.module.add_metadata(()))
        check(block, """\
            my_block:
                ret i32 6, !dbg !0
            """)

    def test_switch(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        bb_onzero = builder.function.append_basic_block(name='onzero')
        bb_onone = builder.function.append_basic_block(name='onone')
        bb_ontwo = builder.function.append_basic_block(name='ontwo')
        bb_else = builder.function.append_basic_block(name='otherwise')
        sw = builder.switch(a, bb_else)
        sw.add_case(ir.Constant(int32, 0), bb_onzero)
        sw.add_case(ir.Constant(int32, 1), bb_onone)
        # A plain Python value gets converted into the right IR constant
        sw.add_case(2, bb_ontwo)
        self.assertTrue(block.is_terminated)
        self.check_block(block, """\
            my_block:
                switch i32 %".1", label %"otherwise" [i32 0, label %"onzero" i32 1, label %"onone" i32 2, label %"ontwo"]
            """)

    def test_call(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        tp_f = ir.FunctionType(flt, (int32, int32))
        tp_g = ir.FunctionType(dbl, (int32,), var_arg=True)
        f = ir.Function(builder.function.module, tp_f, 'f')
        g = ir.Function(builder.function.module, tp_g, 'g')
        builder.call(f, (a, b), 'res_f')
        builder.call(g, (b, a), 'res_g')
        builder.call(f, (a, b), 'res_f_fast', cconv='fastcc')
        res_f_readonly = builder.call(f, (a, b), 'res_f_readonly')
        res_f_readonly.attributes.add('readonly')
        builder.call(f, (a, b), 'res_fast', fastmath='fast')
        builder.call(f, (a, b), 'res_nnan_ninf', fastmath=('nnan', 'ninf'))
        self.check_block(block, """\
            my_block:
                %"res_f" = call float @"f"(i32 %".1", i32 %".2")
                %"res_g" = call double (i32, ...) @"g"(i32 %".2", i32 %".1")
                %"res_f_fast" = call fastcc float @"f"(i32 %".1", i32 %".2")
                %"res_f_readonly" = call float @"f"(i32 %".1", i32 %".2") readonly
                %"res_fast" = call fast float @"f"(i32 %".1", i32 %".2")
                %"res_nnan_ninf" = call ninf nnan float @"f"(i32 %".1", i32 %".2")
            """)

    def test_call_metadata(self):
        """
        Function calls with metadata arguments.
        """
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        dbg_declare_ty = ir.FunctionType(ir.VoidType(), [ir.MetaDataType()] * 3)
        dbg_declare = ir.Function(builder.module, dbg_declare_ty, 'llvm.dbg.declare')
        a = builder.alloca(int32, name="a")
        b = builder.module.add_metadata(())
        builder.call(dbg_declare, (a, b, b))
        self.check_block(block, """\
            my_block:
                %"a" = alloca i32
                call void @"llvm.dbg.declare"(metadata i32* %"a", metadata !0, metadata !0)
            """)

    def test_invoke(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        tp_f = ir.FunctionType(flt, (int32, int32))
        f = ir.Function(builder.function.module, tp_f, 'f')
        bb_normal = builder.function.append_basic_block(name='normal')
        bb_unwind = builder.function.append_basic_block(name='unwind')
        builder.invoke(f, (a, b), bb_normal, bb_unwind, 'res_f')
        self.check_block(block, """\
            my_block:
                %"res_f" = invoke float @"f"(i32 %".1", i32 %".2")
                    to label %"normal" unwind label %"unwind"
            """)

    def test_landingpad(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        lp = builder.landingpad(ir.LiteralStructType([int32, int8.as_pointer()]), 'lp')
        int_typeinfo = ir.GlobalVariable(builder.function.module, int8.as_pointer(), "_ZTIi")
        int_typeinfo.global_constant = True
        lp.add_clause(ir.CatchClause(int_typeinfo))
        lp.add_clause(ir.FilterClause(ir.Constant(ir.ArrayType(int_typeinfo.type, 1),
                                                  [int_typeinfo])))
        builder.resume(lp)
        self.check_block(block, """\
            my_block:
                %"lp" = landingpad {i32, i8*}
                    catch i8** @"_ZTIi"
                    filter [1 x i8**] [i8** @"_ZTIi"]
                resume {i32, i8*} %"lp"
            """)

    def test_assume(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a, b = builder.function.args[:2]
        c = builder.icmp_signed('>', a, b, name='c')
        builder.assume(c)
        self.check_block(block, """\
            my_block:
                %"c" = icmp sgt i32 %".1", %".2"
                call void @"llvm.assume"(i1 %"c")
            """)

    def test_bitreverse(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a = ir.Constant(int64, 5)
        c = builder.bitreverse(a, name='c')
        builder.ret(c)
        self.check_block(block, """\
            my_block:
                %"c" = call i64 @"llvm.bitreverse.i64"(i64 5)
                ret i64 %"c"
            """)

    def test_bitreverse_wrongtype(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a = ir.Constant(flt, 5)

        with self.assertRaises(TypeError) as raises:
            builder.bitreverse(a, name='c')
        self.assertIn(
            "expected an integer type, got float",
            str(raises.exception))

    def test_bswap(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a = ir.Constant(int32, 5)
        c = builder.bswap(a, name='c')
        builder.ret(c)
        self.check_block(block, """\
            my_block:
                %"c" = call i32 @"llvm.bswap.i32"(i32 5)
                ret i32 %"c"
            """)

    def test_ctpop(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a = ir.Constant(int16, 5)
        c = builder.ctpop(a, name='c')
        builder.ret(c)
        self.check_block(block, """\
            my_block:
                %"c" = call i16 @"llvm.ctpop.i16"(i16 5)
                ret i16 %"c"
            """)

    def test_ctlz(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a = ir.Constant(int16, 5)
        b = ir.Constant(int1, 1)
        c = builder.ctlz(a, b, name='c')
        builder.ret(c)
        self.check_block(block, """\
            my_block:
                %"c" = call i16 @"llvm.ctlz.i16"(i16 5, i1 1)
                ret i16 %"c"
            """)

    def test_cttz(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a = ir.Constant(int64, 5)
        b = ir.Constant(int1, 1)
        c = builder.cttz(a, b, name='c')
        builder.ret(c)
        self.check_block(block, """\
            my_block:
                %"c" = call i64 @"llvm.cttz.i64"(i64 5, i1 1)
                ret i64 %"c"
            """)

    def test_cttz_wrongflag(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a = ir.Constant(int64, 5)
        b = ir.Constant(int32, 3)

        with self.assertRaises(TypeError) as raises:
            builder.cttz(a, b, name='c')
        self.assertIn(
            "expected an i1 type, got i32",
            str(raises.exception))

    def test_cttz_wrongtype(self):
        block = self.block(name='my_block')
        builder = ir.IRBuilder(block)
        a = ir.Constant(flt, 5)
        b = ir.Constant(int1, 1)

        with self.assertRaises(TypeError) as raises:
            builder.cttz(a, b, name='c')
        self.assertIn(
            "expected an integer type, got float",
            str(raises.exception))


class TestBuilderMisc(TestBase):
    """
    Test various other features of the IRBuilder class.
    """

    def test_attributes(self):
        block = self.block(name='start')
        builder = ir.IRBuilder(block)
        self.assertIs(builder.function, block.parent)
        self.assertIsInstance(builder.function, ir.Function)
{'fraction_non_alphanumeric': 0.1601909961820911, 'fraction_numerical': 0.02805508265420222, 'mean_word_length': 1.967713004484305, 'pattern_counts': {'":': 48, '<': 29, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7188673', 'n_tokens_mistral': 35252, 'n_tokens_neox': 33211, 'n_words': 6980}