2730 lines
103 KiB
Python
2730 lines
103 KiB
Python
"""
|
|
IR Construction Tests
|
|
"""
|
|
|
|
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)
|
|
hlf = ir.HalfType()
|
|
flt = ir.FloatType()
|
|
dbl = ir.DoubleType()
|
|
|
|
|
|
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.assertRegex(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 assert_pickle_correctly(self, irobject):
|
|
"""Assert that the IR object pickles and unpickles correctly.
|
|
The IR string is equal and that their type is equal
|
|
"""
|
|
newobject = pickle.loads(pickle.dumps(irobject, protocol=-1))
|
|
self.assertIs(irobject.__class__, newobject.__class__)
|
|
self.assertEqual(str(irobject), str(newobject))
|
|
return newobject
|
|
|
|
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(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_regex(self, descr, asm):
|
|
expected = self._normalize_asm(asm)
|
|
self.assertRegex(descr, expected)
|
|
|
|
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_block_regex(self, block, asm):
|
|
self.check_descr_regex(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.add("convergent")
|
|
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 convergent optsize "
|
|
"alignstack(16) "
|
|
"personality i8 (...)* @\"__gxx_personality_v0\"") %
|
|
self.proto)
|
|
# Check pickling
|
|
self.assert_pickle_correctly(func)
|
|
|
|
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")""" # noqa E501
|
|
)
|
|
# Check pickling
|
|
self.assert_pickle_correctly(func)
|
|
|
|
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,
|
|
f'declare {self.proto} !dbg !0'
|
|
)
|
|
# Check pickling
|
|
self.assert_pickle_correctly(func)
|
|
|
|
def test_function_section(self):
|
|
# Test function with section
|
|
func = self.function()
|
|
func.section = "a_section"
|
|
asm = self.descr(func).strip()
|
|
self.assertEqual(asm,
|
|
f'declare {self.proto} section "a_section"'
|
|
)
|
|
# Check pickling
|
|
self.assert_pickle_correctly(func)
|
|
|
|
def test_function_section_meta(self):
|
|
# Test function with section and metadata
|
|
module = self.module()
|
|
func = self.function(module)
|
|
func.section = "a_section"
|
|
func.set_metadata('dbg', module.add_metadata([]))
|
|
asm = self.descr(func).strip()
|
|
self.assertEqual(asm,
|
|
f'declare {self.proto} section "a_section" !dbg !0'
|
|
)
|
|
# Check pickling
|
|
self.assert_pickle_correctly(func)
|
|
|
|
def test_function_attr_meta(self):
|
|
# Test function with attributes and metadata
|
|
module = self.module()
|
|
func = self.function(module)
|
|
func.attributes.add("alwaysinline")
|
|
func.set_metadata('dbg', module.add_metadata([]))
|
|
asm = self.descr(func).strip()
|
|
self.assertEqual(asm,
|
|
f'declare {self.proto} alwaysinline !dbg !0'
|
|
)
|
|
# Check pickling
|
|
self.assert_pickle_correctly(func)
|
|
|
|
def test_function_attr_section(self):
|
|
# Test function with attributes and section
|
|
func = self.function()
|
|
func.attributes.add("optsize")
|
|
func.section = "a_section"
|
|
asm = self.descr(func).strip()
|
|
self.assertEqual(asm,
|
|
f'declare {self.proto} optsize section "a_section"')
|
|
# Check pickling
|
|
self.assert_pickle_correctly(func)
|
|
|
|
def test_function_attr_section_meta(self):
|
|
# Test function with attributes, section and metadata
|
|
module = self.module()
|
|
func = self.function(module)
|
|
func.attributes.add("alwaysinline")
|
|
func.section = "a_section"
|
|
func.set_metadata('dbg', module.add_metadata([]))
|
|
asm = self.descr(func).strip()
|
|
self.assertEqual(asm,
|
|
f'declare {self.proto} alwaysinline section "a_section" !dbg !0' # noqa E501
|
|
)
|
|
# Check pickling
|
|
self.assert_pickle_correctly(func)
|
|
|
|
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", i1 %".4")""") # noqa E501
|
|
self.check_descr(self.descr(memcpy).strip(), """\
|
|
declare void @"llvm.memcpy.p0i8.p0i8.i32"(i8* %".1", i8* %".2", i32 %".3", i1 %".4")""") # noqa E501
|
|
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)
|
|
|
|
def test_pickling(self):
|
|
fn = self.function()
|
|
self.assert_pickle_correctly(fn)
|
|
|
|
def test_alwaysinline_noinline_disallowed(self):
|
|
module = self.module()
|
|
func = self.function(module)
|
|
func.attributes.add('alwaysinline')
|
|
|
|
msg = "Can't have alwaysinline and noinline"
|
|
with self.assertRaisesRegex(ValueError, msg):
|
|
func.attributes.add('noinline')
|
|
|
|
def test_noinline_alwaysinline_disallowed(self):
|
|
module = self.module()
|
|
func = self.function(module)
|
|
func.attributes.add('noinline')
|
|
|
|
msg = "Can't have alwaysinline and noinline"
|
|
with self.assertRaisesRegex(ValueError, msg):
|
|
func.attributes.add('alwaysinline')
|
|
|
|
|
|
class TestIR(TestBase):
|
|
|
|
def test_unnamed_metadata(self):
|
|
# An unnamed metadata node
|
|
mod = self.module()
|
|
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
|
|
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()
|
|
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()
|
|
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)
|
|
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)
|
|
|
|
def test_debug_info_gvar(self):
|
|
# This test defines a module with a global variable named 'gvar'.
|
|
# When the module is compiled and linked with a main function, gdb can
|
|
# be used to interpret and print the the value of 'gvar'.
|
|
mod = self.module()
|
|
|
|
gvar = ir.GlobalVariable(mod, ir.FloatType(), 'gvar')
|
|
gvar.initializer = ir.Constant(ir.FloatType(), 42)
|
|
|
|
di_float = mod.add_debug_info("DIBasicType", {
|
|
"name": "float",
|
|
"size": 32,
|
|
"encoding": ir.DIToken("DW_ATE_float")
|
|
})
|
|
di_gvar = mod.add_debug_info("DIGlobalVariableExpression", {
|
|
"expr": mod.add_debug_info("DIExpression", {}),
|
|
"var": mod.add_debug_info("DIGlobalVariable", {
|
|
"name": gvar.name,
|
|
"type": di_float,
|
|
"isDefinition": True
|
|
}, is_distinct=True)
|
|
})
|
|
gvar.set_metadata('dbg', di_gvar)
|
|
|
|
# Check output
|
|
strmod = str(mod)
|
|
self.assert_ir_line('!0 = !DIBasicType(encoding: DW_ATE_float, '
|
|
'name: "float", size: 32)', strmod)
|
|
self.assert_ir_line('!1 = !DIExpression()', strmod)
|
|
self.assert_ir_line('!2 = distinct !DIGlobalVariable(isDefinition: '
|
|
'true, name: "gvar", type: !0)', strmod)
|
|
self.assert_ir_line('!3 = !DIGlobalVariableExpression(expr: !1, '
|
|
'var: !2)', strmod)
|
|
self.assert_ir_line('@"gvar" = global float 0x4045000000000000, '
|
|
'!dbg !3', strmod)
|
|
|
|
# The remaining debug info is not part of the automated test, but
|
|
# can be used to produce an object file that can be loaded into a
|
|
# debugger to print the value of gvar. This can be done by printing the
|
|
# module then compiling it with clang and inspecting with gdb:
|
|
#
|
|
# clang test_debug_info_gvar.ll -c
|
|
# printf "file test_debug_info_gvar.o \n p gvar" | gdb
|
|
#
|
|
# Which should result in the output:
|
|
#
|
|
# (gdb) $1 = 42
|
|
|
|
dver = [ir.IntType(32)(2), 'Dwarf Version', ir.IntType(32)(4)]
|
|
diver = [ir.IntType(32)(2), 'Debug Info Version', ir.IntType(32)(3)]
|
|
dver = mod.add_metadata(dver)
|
|
diver = mod.add_metadata(diver)
|
|
flags = mod.add_named_metadata('llvm.module.flags')
|
|
flags.add(dver)
|
|
flags.add(diver)
|
|
|
|
di_file = mod.add_debug_info("DIFile", {
|
|
"filename": "foo",
|
|
"directory": "bar",
|
|
})
|
|
di_cu = mod.add_debug_info("DICompileUnit", {
|
|
"language": ir.DIToken("DW_LANG_Python"),
|
|
"file": di_file,
|
|
'emissionKind': ir.DIToken('FullDebug'),
|
|
"globals": mod.add_metadata([di_gvar])
|
|
}, is_distinct=True)
|
|
mod.add_named_metadata('llvm.dbg.cu', di_cu)
|
|
|
|
def test_debug_info_unicode_string(self):
|
|
mod = self.module()
|
|
mod.add_debug_info("DILocalVariable", {"name": "a∆"})
|
|
# Check output
|
|
strmod = str(mod)
|
|
# The unicode character is utf8 encoded with \XX format, where XX is hex
|
|
name = ''.join(map(lambda x: f"\\{x:02x}", "∆".encode()))
|
|
self.assert_ir_line(f'!0 = !DILocalVariable(name: "a{name}")', strmod)
|
|
|
|
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(''))
|
|
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(''))
|
|
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()
|
|
# the following have side effects and write to self.module()
|
|
a = ir.GlobalVariable(mod, int8, 'a') # noqa F841
|
|
b = ir.GlobalVariable(mod, int8, 'b', addrspace=42) # noqa F841
|
|
# 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
|
|
h = ir.GlobalVariable(mod, int32, 'h')
|
|
h.linkage = "internal"
|
|
h.initializer = int32(123)
|
|
h.section = "h_section"
|
|
i = ir.GlobalVariable(mod, int32, 'i')
|
|
i.linkage = "internal"
|
|
i.initializer = int32(456)
|
|
i.align = 8
|
|
i.section = "i_section"
|
|
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
|
|
@"h" = internal global i32 123, section "h_section"
|
|
@"i" = internal global i32 456, section "i_section", align 8
|
|
""")
|
|
|
|
def test_pickle(self):
|
|
mod = self.module()
|
|
self.assert_pickle_correctly(mod)
|
|
|
|
|
|
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')
|
|
self.check_block(block, """\
|
|
my_block:
|
|
%"c" = add i32 %".1", %".2"
|
|
%"d" = sub i32 %".1", %".2"
|
|
%"e" = mul i32 %"d", %".2"
|
|
""")
|
|
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, c = builder.function.args[:3]
|
|
builder.neg(a, 'd')
|
|
builder.not_(b, 'e')
|
|
builder.fneg(c, 'f')
|
|
self.assertFalse(block.is_terminated)
|
|
self.check_block(block, """\
|
|
my_block:
|
|
%"d" = sub i32 0, %".1"
|
|
%"e" = xor i32 %".2", -1
|
|
%"f" = fneg double %".3"
|
|
""")
|
|
|
|
def test_replace_operand(self):
|
|
block = self.block(name='my_block')
|
|
builder = ir.IRBuilder(block)
|
|
a, b = builder.function.args[:2]
|
|
undef1 = ir.Constant(ir.IntType(32), ir.Undefined)
|
|
undef2 = ir.Constant(ir.IntType(32), ir.Undefined)
|
|
c = builder.add(undef1, undef2, 'c')
|
|
self.check_block(block, """\
|
|
my_block:
|
|
%"c" = add i32 undef, undef
|
|
""")
|
|
c.replace_usage(undef1, a)
|
|
c.replace_usage(undef2, b)
|
|
self.check_block(block, """\
|
|
my_block:
|
|
%"c" = add i32 %".1", %".2"
|
|
""")
|
|
|
|
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', flags=('arcp', 'nnan'))
|
|
self.assertFalse(block.is_terminated)
|
|
builder.unreachable()
|
|
self.assertTrue(block.is_terminated)
|
|
self.check_block(block, """\
|
|
my_block:
|
|
%"c" = select arcp nnan 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', flags=('fast',))
|
|
phi.add_incoming(a, bb2)
|
|
phi.add_incoming(b, bb3)
|
|
self.assertFalse(block.is_terminated)
|
|
self.check_block(block, """\
|
|
my_block:
|
|
%"my_phi" = phi fast 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') # noqa F841
|
|
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)
|
|
# Atomics
|
|
j = builder.store_atomic(b, c, ordering="seq_cst", align=4)
|
|
self.assertEqual(j.type, ir.VoidType())
|
|
k = builder.load_atomic(c, ordering="seq_cst", align=4, name='k')
|
|
self.assertEqual(k.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
|
|
store atomic i32 %".2", i32* %"c" seq_cst, align 4
|
|
%"k" = load atomic i32, i32* %"c" seq_cst, align 4
|
|
""")
|
|
|
|
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
|
|
""") # noqa E501
|
|
|
|
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
|
|
""") # noqa E501
|
|
|
|
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
|
|
""") # noqa E501
|
|
|
|
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') # noqa F841
|
|
d = builder.insert_value(c_inner, a, 0, name='d') # noqa F841
|
|
e = builder.insert_value(d, ir.Constant(int1, False), 1, name='e') # noqa F841 E501
|
|
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') # noqa F841
|
|
e = builder.sext(c, int32, name='e') # noqa F841
|
|
fb = builder.fptrunc(fa, flt, 'fb')
|
|
fc = builder.fpext(fb, dbl, 'fc') # noqa F841
|
|
g = builder.fptoui(fa, int32, 'g')
|
|
h = builder.fptosi(fa, int8, 'h')
|
|
fd = builder.uitofp(g, flt, 'fd') # noqa F841
|
|
fe = builder.sitofp(h, dbl, 'fe') # noqa F841
|
|
i = builder.ptrtoint(ptr, int32, 'i')
|
|
j = builder.inttoptr(i, ir.PointerType(int8), 'j') # noqa F841
|
|
k = builder.bitcast(a, flt, "k") # noqa F841
|
|
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"]
|
|
""") # noqa E501
|
|
|
|
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"]
|
|
""") # noqa E501
|
|
|
|
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)
|
|
tp_h = ir.FunctionType(hlf, (int32, int32))
|
|
f = ir.Function(builder.function.module, tp_f, 'f')
|
|
g = ir.Function(builder.function.module, tp_g, 'g')
|
|
h = ir.Function(builder.function.module, tp_h, 'h')
|
|
builder.call(f, (a, b), 'res_f')
|
|
builder.call(g, (b, a), 'res_g')
|
|
builder.call(h, (a, b), 'res_h')
|
|
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'))
|
|
builder.call(f, (a, b), 'res_noinline', attrs='noinline')
|
|
builder.call(f, (a, b), 'res_alwaysinline', attrs='alwaysinline')
|
|
builder.call(f, (a, b), 'res_noinline_ro', attrs=('noinline',
|
|
'readonly'))
|
|
builder.call(f, (a, b), 'res_convergent', attrs='convergent')
|
|
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_h" = call half @"h"(i32 %".1", i32 %".2")
|
|
%"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")
|
|
%"res_noinline" = call float @"f"(i32 %".1", i32 %".2") noinline
|
|
%"res_alwaysinline" = call float @"f"(i32 %".1", i32 %".2") alwaysinline
|
|
%"res_noinline_ro" = call float @"f"(i32 %".1", i32 %".2") noinline readonly
|
|
%"res_convergent" = call float @"f"(i32 %".1", i32 %".2") convergent
|
|
""") # noqa E501
|
|
|
|
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)
|
|
""") # noqa E501
|
|
|
|
def test_call_attributes(self):
|
|
block = self.block(name='my_block')
|
|
builder = ir.IRBuilder(block)
|
|
fun_ty = ir.FunctionType(
|
|
ir.VoidType(), (int32.as_pointer(), int32, int32.as_pointer()))
|
|
fun = ir.Function(builder.function.module, fun_ty, 'fun')
|
|
fun.args[0].add_attribute('sret')
|
|
retval = builder.alloca(int32, name='retval')
|
|
other = builder.alloca(int32, name='other')
|
|
builder.call(
|
|
fun,
|
|
(retval, ir.Constant(int32, 42), other),
|
|
arg_attrs={
|
|
0: ('sret', 'noalias'),
|
|
2: 'noalias'
|
|
}
|
|
)
|
|
self.check_block_regex(block, """\
|
|
my_block:
|
|
%"retval" = alloca i32
|
|
%"other" = alloca i32
|
|
call void @"fun"\\(i32\\* noalias sret(\\(i32\\))? %"retval", i32 42, i32\\* noalias %"other"\\)
|
|
""") # noqa E501
|
|
|
|
def test_call_tail(self):
|
|
block = self.block(name='my_block')
|
|
builder = ir.IRBuilder(block)
|
|
fun_ty = ir.FunctionType(ir.VoidType(), ())
|
|
fun = ir.Function(builder.function.module, fun_ty, 'my_fun')
|
|
|
|
builder.call(fun, ())
|
|
builder.call(fun, (), tail=False)
|
|
builder.call(fun, (), tail=True)
|
|
builder.call(fun, (), tail='tail')
|
|
builder.call(fun, (), tail='notail')
|
|
builder.call(fun, (), tail='musttail')
|
|
builder.call(fun, (), tail=[]) # This is a falsy value
|
|
builder.call(fun, (), tail='not a marker') # This is a truthy value
|
|
|
|
self.check_block(block, """\
|
|
my_block:
|
|
call void @"my_fun"()
|
|
call void @"my_fun"()
|
|
tail call void @"my_fun"()
|
|
tail call void @"my_fun"()
|
|
notail call void @"my_fun"()
|
|
musttail call void @"my_fun"()
|
|
call void @"my_fun"()
|
|
tail call void @"my_fun"()
|
|
""") # noqa E501
|
|
|
|
def test_invalid_call_attributes(self):
|
|
block = self.block()
|
|
builder = ir.IRBuilder(block)
|
|
fun_ty = ir.FunctionType(ir.VoidType(), ())
|
|
fun = ir.Function(builder.function.module, fun_ty, 'fun')
|
|
with self.assertRaises(ValueError):
|
|
# The function has no arguments, so this should fail.
|
|
builder.call(fun, (), arg_attrs={0: 'sret'})
|
|
|
|
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_invoke_attributes(self):
|
|
block = self.block(name='my_block')
|
|
builder = ir.IRBuilder(block)
|
|
fun_ty = ir.FunctionType(
|
|
ir.VoidType(), (int32.as_pointer(), int32, int32.as_pointer()))
|
|
fun = ir.Function(builder.function.module, fun_ty, 'fun')
|
|
fun.calling_convention = "fastcc"
|
|
fun.args[0].add_attribute('sret')
|
|
retval = builder.alloca(int32, name='retval')
|
|
other = builder.alloca(int32, name='other')
|
|
bb_normal = builder.function.append_basic_block(name='normal')
|
|
bb_unwind = builder.function.append_basic_block(name='unwind')
|
|
builder.invoke(
|
|
fun,
|
|
(retval, ir.Constant(int32, 42), other),
|
|
bb_normal,
|
|
bb_unwind,
|
|
cconv='fastcc',
|
|
fastmath='fast',
|
|
attrs='noinline',
|
|
arg_attrs={
|
|
0: ('sret', 'noalias'),
|
|
2: 'noalias'
|
|
}
|
|
)
|
|
self.check_block_regex(block, """\
|
|
my_block:
|
|
%"retval" = alloca i32
|
|
%"other" = alloca i32
|
|
invoke fast fastcc void @"fun"\\(i32\\* noalias sret(\\(i32\\))? %"retval", i32 42, i32\\* noalias %"other"\\) noinline
|
|
to label %"normal" unwind label %"unwind"
|
|
""") # noqa E501
|
|
|
|
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_vector_ops(self):
|
|
block = self.block(name='insert_block')
|
|
builder = ir.IRBuilder(block)
|
|
a, b = builder.function.args[:2]
|
|
a.name = 'a'
|
|
b.name = 'b'
|
|
|
|
vecty = ir.VectorType(a.type, 2)
|
|
vec = ir.Constant(vecty, ir.Undefined)
|
|
idxty = ir.IntType(32)
|
|
vec = builder.insert_element(vec, a, idxty(0), name='vec1')
|
|
vec = builder.insert_element(vec, b, idxty(1), name='vec2')
|
|
|
|
self.check_block(block, """\
|
|
insert_block:
|
|
%"vec1" = insertelement <2 x i32> <i32 undef, i32 undef>, i32 %"a", i32 0
|
|
%"vec2" = insertelement <2 x i32> %"vec1", i32 %"b", i32 1
|
|
""")
|
|
|
|
block = builder.append_basic_block("shuffle_block")
|
|
builder.branch(block)
|
|
builder.position_at_end(block)
|
|
|
|
mask = ir.Constant(vecty, [1, 0])
|
|
builder.shuffle_vector(vec, vec, mask, name='shuf')
|
|
|
|
self.check_block(block, """\
|
|
shuffle_block:
|
|
%"shuf" = shufflevector <2 x i32> %"vec2", <2 x i32> %"vec2", <2 x i32> <i32 1, i32 0>
|
|
""") # noqa E501
|
|
|
|
block = builder.append_basic_block("add_block")
|
|
builder.branch(block)
|
|
builder.position_at_end(block)
|
|
|
|
builder.add(vec, vec, name='sum')
|
|
|
|
self.check_block(block, """\
|
|
add_block:
|
|
%"sum" = add <2 x i32> %"vec2", %"vec2"
|
|
""")
|
|
|
|
block = builder.append_basic_block("extract_block")
|
|
builder.branch(block)
|
|
builder.position_at_end(block)
|
|
|
|
c = builder.extract_element(vec, idxty(0), name='ex1')
|
|
d = builder.extract_element(vec, idxty(1), name='ex2')
|
|
|
|
self.check_block(block, """\
|
|
extract_block:
|
|
%"ex1" = extractelement <2 x i32> %"vec2", i32 0
|
|
%"ex2" = extractelement <2 x i32> %"vec2", i32 1
|
|
""")
|
|
|
|
builder.ret(builder.add(c, d))
|
|
self.assert_valid_ir(builder.module)
|
|
|
|
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_fence(self):
|
|
block = self.block(name='my_block')
|
|
builder = ir.IRBuilder(block)
|
|
with self.assertRaises(ValueError) as raises:
|
|
builder.fence("monotonic", None)
|
|
self.assertIn(
|
|
"Invalid fence ordering \"monotonic\"!",
|
|
str(raises.exception))
|
|
with self.assertRaises(ValueError) as raises:
|
|
builder.fence(None, "monotonic")
|
|
self.assertIn(
|
|
"Invalid fence ordering \"None\"!",
|
|
str(raises.exception))
|
|
builder.fence("acquire", None)
|
|
builder.fence("release", "singlethread")
|
|
builder.fence("acq_rel", "singlethread")
|
|
builder.fence("seq_cst")
|
|
builder.ret_void()
|
|
self.check_block(block, """\
|
|
my_block:
|
|
fence acquire
|
|
fence syncscope("singlethread") release
|
|
fence syncscope("singlethread") acq_rel
|
|
fence seq_cst
|
|
ret void
|
|
""")
|
|
|
|
def test_comment(self):
|
|
block = self.block(name='my_block')
|
|
builder = ir.IRBuilder(block)
|
|
with self.assertRaises(AssertionError):
|
|
builder.comment("so\nmany lines")
|
|
builder.comment("my comment")
|
|
builder.ret_void()
|
|
self.check_block(block, """\
|
|
my_block:
|
|
; my comment
|
|
ret void
|
|
""")
|
|
|
|
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_convert_to_fp16_f32(self):
|
|
block = self.block(name='my_block')
|
|
builder = ir.IRBuilder(block)
|
|
a = ir.Constant(flt, 5.0)
|
|
b = builder.convert_to_fp16(a, name='b')
|
|
builder.ret(b)
|
|
self.check_block(block, """\
|
|
my_block:
|
|
%"b" = call i16 @"llvm.convert.to.fp16.f32"(float 0x4014000000000000)
|
|
ret i16 %"b"
|
|
""") # noqa E501
|
|
|
|
def test_convert_to_fp16_f32_wrongtype(self):
|
|
block = self.block(name='my_block')
|
|
builder = ir.IRBuilder(block)
|
|
a = ir.Constant(int16, 5)
|
|
|
|
with self.assertRaises(TypeError) as raises:
|
|
builder.convert_to_fp16(a, name='b')
|
|
self.assertIn(
|
|
"expected a float type, got i16",
|
|
str(raises.exception))
|
|
|
|
def test_convert_from_fp16_f32(self):
|
|
block = self.block(name='my_block')
|
|
builder = ir.IRBuilder(block)
|
|
a = ir.Constant(int16, 5)
|
|
b = builder.convert_from_fp16(a, name='b', to=flt)
|
|
builder.ret(b)
|
|
self.check_block(block, """\
|
|
my_block:
|
|
%"b" = call float @"llvm.convert.from.fp16.f32"(i16 5)
|
|
ret float %"b"
|
|
""")
|
|
|
|
def test_convert_from_fp16_f32_notype(self):
|
|
block = self.block(name='my_block')
|
|
builder = ir.IRBuilder(block)
|
|
a = ir.Constant(flt, 5.5)
|
|
|
|
with self.assertRaises(TypeError) as raises:
|
|
builder.convert_from_fp16(a, name='b')
|
|
self.assertIn(
|
|
"expected a float return type",
|
|
str(raises.exception))
|
|
|
|
def test_convert_from_fp16_f32_wrongtype(self):
|
|
block = self.block(name='my_block')
|
|
builder = ir.IRBuilder(block)
|
|
a = ir.Constant(flt, 5.5)
|
|
|
|
with self.assertRaises(TypeError) as raises:
|
|
builder.convert_from_fp16(a, name='b', to=flt)
|
|
self.assertIn(
|
|
"expected an i16 type, got float",
|
|
str(raises.exception))
|
|
|
|
def test_convert_from_fp16_f32_wrongtype2(self):
|
|
block = self.block(name='my_block')
|
|
builder = ir.IRBuilder(block)
|
|
a = ir.Constant(flt, 5.5)
|
|
|
|
with self.assertRaises(TypeError) as raises:
|
|
builder.convert_from_fp16(a, name='b', to=int16)
|
|
self.assertIn(
|
|
"expected a float type, got i16",
|
|
str(raises.exception))
|
|
|
|
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))
|
|
|
|
def test_fma(self):
|
|
block = self.block(name='my_block')
|
|
builder = ir.IRBuilder(block)
|
|
a = ir.Constant(flt, 5)
|
|
b = ir.Constant(flt, 1)
|
|
c = ir.Constant(flt, 2)
|
|
fma = builder.fma(a, b, c, name='fma')
|
|
builder.ret(fma)
|
|
self.check_block(block, """\
|
|
my_block:
|
|
%"fma" = call float @"llvm.fma.f32"(float 0x4014000000000000, float 0x3ff0000000000000, float 0x4000000000000000)
|
|
ret float %"fma"
|
|
""") # noqa E501
|
|
|
|
def test_fma_wrongtype(self):
|
|
block = self.block(name='my_block')
|
|
builder = ir.IRBuilder(block)
|
|
a = ir.Constant(int32, 5)
|
|
b = ir.Constant(int32, 1)
|
|
c = ir.Constant(int32, 2)
|
|
|
|
with self.assertRaises(TypeError) as raises:
|
|
builder.fma(a, b, c, name='fma')
|
|
self.assertIn(
|
|
"expected an floating point type, got i32",
|
|
str(raises.exception))
|
|
|
|
def test_fma_mixedtypes(self):
|
|
block = self.block(name='my_block')
|
|
builder = ir.IRBuilder(block)
|
|
a = ir.Constant(flt, 5)
|
|
b = ir.Constant(dbl, 1)
|
|
c = ir.Constant(flt, 2)
|
|
|
|
with self.assertRaises(TypeError) as raises:
|
|
builder.fma(a, b, c, name='fma')
|
|
self.assertIn(
|
|
"expected types to be the same, got float, double, float",
|
|
str(raises.exception))
|
|
|
|
def test_arg_attributes(self):
|
|
def gen_code(attr_name):
|
|
fnty = ir.FunctionType(ir.IntType(32), [ir.IntType(32).as_pointer(),
|
|
ir.IntType(32)])
|
|
module = ir.Module()
|
|
|
|
func = ir.Function(module, fnty, name="sum")
|
|
|
|
bb_entry = func.append_basic_block()
|
|
bb_loop = func.append_basic_block()
|
|
bb_exit = func.append_basic_block()
|
|
|
|
builder = ir.IRBuilder()
|
|
builder.position_at_end(bb_entry)
|
|
|
|
builder.branch(bb_loop)
|
|
builder.position_at_end(bb_loop)
|
|
|
|
index = builder.phi(ir.IntType(32))
|
|
index.add_incoming(ir.Constant(index.type, 0), bb_entry)
|
|
accum = builder.phi(ir.IntType(32))
|
|
accum.add_incoming(ir.Constant(accum.type, 0), bb_entry)
|
|
|
|
func.args[0].add_attribute(attr_name)
|
|
ptr = builder.gep(func.args[0], [index])
|
|
value = builder.load(ptr)
|
|
|
|
added = builder.add(accum, value)
|
|
accum.add_incoming(added, bb_loop)
|
|
|
|
indexp1 = builder.add(index, ir.Constant(index.type, 1))
|
|
index.add_incoming(indexp1, bb_loop)
|
|
|
|
cond = builder.icmp_unsigned('<', indexp1, func.args[1])
|
|
builder.cbranch(cond, bb_loop, bb_exit)
|
|
|
|
builder.position_at_end(bb_exit)
|
|
builder.ret(added)
|
|
|
|
return str(module)
|
|
|
|
for attr_name in (
|
|
'byref',
|
|
'byval',
|
|
'elementtype',
|
|
'immarg',
|
|
'inalloca',
|
|
'inreg',
|
|
'nest',
|
|
'noalias',
|
|
'nocapture',
|
|
'nofree',
|
|
'nonnull',
|
|
'noundef',
|
|
'preallocated',
|
|
'returned',
|
|
'signext',
|
|
'swiftasync',
|
|
'swifterror',
|
|
'swiftself',
|
|
'zeroext',
|
|
):
|
|
# If this parses, we emitted the right byval attribute format
|
|
llvm.parse_assembly(gen_code(attr_name))
|
|
# sret doesn't fit this pattern and is tested in test_call_attributes
|
|
|
|
|
|
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)
|
|
self.assertIs(builder.module, block.parent.module)
|
|
self.assertIsInstance(builder.module, ir.Module)
|
|
|
|
def test_goto_block(self):
|
|
block = self.block(name='my_block')
|
|
builder = ir.IRBuilder(block)
|
|
a, b = builder.function.args[:2]
|
|
builder.add(a, b, 'c')
|
|
bb_new = builder.append_basic_block(name='foo')
|
|
with builder.goto_block(bb_new):
|
|
builder.fadd(a, b, 'd')
|
|
with builder.goto_entry_block():
|
|
builder.sub(a, b, 'e')
|
|
builder.fsub(a, b, 'f')
|
|
builder.branch(bb_new)
|
|
builder.mul(a, b, 'g')
|
|
with builder.goto_block(bb_new):
|
|
builder.fmul(a, b, 'h')
|
|
self.check_block(block, """\
|
|
my_block:
|
|
%"c" = add i32 %".1", %".2"
|
|
%"e" = sub i32 %".1", %".2"
|
|
%"g" = mul i32 %".1", %".2"
|
|
""")
|
|
self.check_block(bb_new, """\
|
|
foo:
|
|
%"d" = fadd i32 %".1", %".2"
|
|
%"f" = fsub i32 %".1", %".2"
|
|
%"h" = fmul i32 %".1", %".2"
|
|
br label %"foo"
|
|
""")
|
|
|
|
def test_if_then(self):
|
|
block = self.block(name='one')
|
|
builder = ir.IRBuilder(block)
|
|
z = ir.Constant(int1, 0)
|
|
a = builder.add(z, z, 'a')
|
|
with builder.if_then(a) as bbend:
|
|
builder.add(z, z, 'b')
|
|
# Block will be terminated implicitly
|
|
self.assertIs(builder.block, bbend)
|
|
c = builder.add(z, z, 'c')
|
|
with builder.if_then(c):
|
|
builder.add(z, z, 'd')
|
|
builder.branch(block)
|
|
# No implicit termination
|
|
self.check_func_body(builder.function, """\
|
|
one:
|
|
%"a" = add i1 0, 0
|
|
br i1 %"a", label %"one.if", label %"one.endif"
|
|
one.if:
|
|
%"b" = add i1 0, 0
|
|
br label %"one.endif"
|
|
one.endif:
|
|
%"c" = add i1 0, 0
|
|
br i1 %"c", label %"one.endif.if", label %"one.endif.endif"
|
|
one.endif.if:
|
|
%"d" = add i1 0, 0
|
|
br label %"one"
|
|
one.endif.endif:
|
|
""")
|
|
|
|
def test_if_then_nested(self):
|
|
# Implicit termination in a nested if/then
|
|
block = self.block(name='one')
|
|
builder = ir.IRBuilder(block)
|
|
z = ir.Constant(int1, 0)
|
|
a = builder.add(z, z, 'a')
|
|
with builder.if_then(a):
|
|
b = builder.add(z, z, 'b')
|
|
with builder.if_then(b):
|
|
builder.add(z, z, 'c')
|
|
builder.ret_void()
|
|
self.check_func_body(builder.function, """\
|
|
one:
|
|
%"a" = add i1 0, 0
|
|
br i1 %"a", label %"one.if", label %"one.endif"
|
|
one.if:
|
|
%"b" = add i1 0, 0
|
|
br i1 %"b", label %"one.if.if", label %"one.if.endif"
|
|
one.endif:
|
|
ret void
|
|
one.if.if:
|
|
%"c" = add i1 0, 0
|
|
br label %"one.if.endif"
|
|
one.if.endif:
|
|
br label %"one.endif"
|
|
""")
|
|
|
|
def test_if_then_long_label(self):
|
|
full_label = 'Long' * 20
|
|
block = self.block(name=full_label)
|
|
builder = ir.IRBuilder(block)
|
|
z = ir.Constant(int1, 0)
|
|
a = builder.add(z, z, 'a')
|
|
with builder.if_then(a):
|
|
b = builder.add(z, z, 'b')
|
|
with builder.if_then(b):
|
|
builder.add(z, z, 'c')
|
|
builder.ret_void()
|
|
self.check_func_body(builder.function, """\
|
|
{full_label}:
|
|
%"a" = add i1 0, 0
|
|
br i1 %"a", label %"{label}.if", label %"{label}.endif"
|
|
{label}.if:
|
|
%"b" = add i1 0, 0
|
|
br i1 %"b", label %"{label}.if.if", label %"{label}.if.endif"
|
|
{label}.endif:
|
|
ret void
|
|
{label}.if.if:
|
|
%"c" = add i1 0, 0
|
|
br label %"{label}.if.endif"
|
|
{label}.if.endif:
|
|
br label %"{label}.endif"
|
|
""".format(full_label=full_label, label=full_label[:25] + '..'))
|
|
|
|
def test_if_then_likely(self):
|
|
def check(likely):
|
|
block = self.block(name='one')
|
|
builder = ir.IRBuilder(block)
|
|
z = ir.Constant(int1, 0)
|
|
with builder.if_then(z, likely=likely):
|
|
pass
|
|
self.check_block(block, """\
|
|
one:
|
|
br i1 0, label %"one.if", label %"one.endif", !prof !0
|
|
""")
|
|
return builder
|
|
builder = check(True)
|
|
self.check_metadata(builder.module, """\
|
|
!0 = !{ !"branch_weights", i32 99, i32 1 }
|
|
""")
|
|
builder = check(False)
|
|
self.check_metadata(builder.module, """\
|
|
!0 = !{ !"branch_weights", i32 1, i32 99 }
|
|
""")
|
|
|
|
def test_if_else(self):
|
|
block = self.block(name='one')
|
|
builder = ir.IRBuilder(block)
|
|
z = ir.Constant(int1, 0)
|
|
a = builder.add(z, z, 'a')
|
|
with builder.if_else(a) as (then, otherwise):
|
|
with then:
|
|
builder.add(z, z, 'b')
|
|
with otherwise:
|
|
builder.add(z, z, 'c')
|
|
# Each block will be terminated implicitly
|
|
with builder.if_else(a) as (then, otherwise):
|
|
with then:
|
|
builder.branch(block)
|
|
with otherwise:
|
|
builder.ret_void()
|
|
# No implicit termination
|
|
self.check_func_body(builder.function, """\
|
|
one:
|
|
%"a" = add i1 0, 0
|
|
br i1 %"a", label %"one.if", label %"one.else"
|
|
one.if:
|
|
%"b" = add i1 0, 0
|
|
br label %"one.endif"
|
|
one.else:
|
|
%"c" = add i1 0, 0
|
|
br label %"one.endif"
|
|
one.endif:
|
|
br i1 %"a", label %"one.endif.if", label %"one.endif.else"
|
|
one.endif.if:
|
|
br label %"one"
|
|
one.endif.else:
|
|
ret void
|
|
one.endif.endif:
|
|
""")
|
|
|
|
def test_if_else_likely(self):
|
|
def check(likely):
|
|
block = self.block(name='one')
|
|
builder = ir.IRBuilder(block)
|
|
z = ir.Constant(int1, 0)
|
|
with builder.if_else(z, likely=likely) as (then, otherwise):
|
|
with then:
|
|
builder.branch(block)
|
|
with otherwise:
|
|
builder.ret_void()
|
|
self.check_func_body(builder.function, """\
|
|
one:
|
|
br i1 0, label %"one.if", label %"one.else", !prof !0
|
|
one.if:
|
|
br label %"one"
|
|
one.else:
|
|
ret void
|
|
one.endif:
|
|
""")
|
|
return builder
|
|
builder = check(True)
|
|
self.check_metadata(builder.module, """\
|
|
!0 = !{ !"branch_weights", i32 99, i32 1 }
|
|
""")
|
|
builder = check(False)
|
|
self.check_metadata(builder.module, """\
|
|
!0 = !{ !"branch_weights", i32 1, i32 99 }
|
|
""")
|
|
|
|
def test_positioning(self):
|
|
"""
|
|
Test IRBuilder.position_{before,after,at_start,at_end}.
|
|
"""
|
|
func = self.function()
|
|
builder = ir.IRBuilder()
|
|
z = ir.Constant(int32, 0)
|
|
bb_one = func.append_basic_block(name='one')
|
|
bb_two = func.append_basic_block(name='two')
|
|
bb_three = func.append_basic_block(name='three')
|
|
# .at_start(empty block)
|
|
builder.position_at_start(bb_one)
|
|
builder.add(z, z, 'a')
|
|
# .at_end(empty block)
|
|
builder.position_at_end(bb_two)
|
|
builder.add(z, z, 'm')
|
|
builder.add(z, z, 'n')
|
|
# .at_start(block)
|
|
builder.position_at_start(bb_two)
|
|
o = builder.add(z, z, 'o')
|
|
builder.add(z, z, 'p')
|
|
# .at_end(block)
|
|
builder.position_at_end(bb_one)
|
|
b = builder.add(z, z, 'b')
|
|
# .after(instr)
|
|
builder.position_after(o)
|
|
builder.add(z, z, 'q')
|
|
# .before(instr)
|
|
builder.position_before(b)
|
|
builder.add(z, z, 'c')
|
|
self.check_block(bb_one, """\
|
|
one:
|
|
%"a" = add i32 0, 0
|
|
%"c" = add i32 0, 0
|
|
%"b" = add i32 0, 0
|
|
""")
|
|
self.check_block(bb_two, """\
|
|
two:
|
|
%"o" = add i32 0, 0
|
|
%"q" = add i32 0, 0
|
|
%"p" = add i32 0, 0
|
|
%"m" = add i32 0, 0
|
|
%"n" = add i32 0, 0
|
|
""")
|
|
self.check_block(bb_three, """\
|
|
three:
|
|
""")
|
|
|
|
def test_instruction_removal(self):
|
|
func = self.function()
|
|
builder = ir.IRBuilder()
|
|
blk = func.append_basic_block(name='entry')
|
|
builder.position_at_end(blk)
|
|
k = ir.Constant(int32, 1234)
|
|
a = builder.add(k, k, 'a')
|
|
retvoid = builder.ret_void()
|
|
self.assertTrue(blk.is_terminated)
|
|
builder.remove(retvoid)
|
|
self.assertFalse(blk.is_terminated)
|
|
b = builder.mul(a, a, 'b')
|
|
c = builder.add(b, b, 'c')
|
|
builder.remove(c)
|
|
builder.ret_void()
|
|
self.assertTrue(blk.is_terminated)
|
|
self.check_block(blk, """\
|
|
entry:
|
|
%"a" = add i32 1234, 1234
|
|
%"b" = mul i32 %"a", %"a"
|
|
ret void
|
|
""")
|
|
|
|
def test_metadata(self):
|
|
block = self.block(name='my_block')
|
|
builder = ir.IRBuilder(block)
|
|
builder.debug_metadata = builder.module.add_metadata([])
|
|
builder.alloca(ir.PointerType(int32), name='c')
|
|
self.check_block(block, """\
|
|
my_block:
|
|
%"c" = alloca i32*, !dbg !0
|
|
""")
|
|
|
|
|
|
class TestTypes(TestBase):
|
|
|
|
def has_logical_equality(self, ty):
|
|
while isinstance(ty, ir.PointerType):
|
|
ty = ty.pointee
|
|
return not isinstance(ty, ir.LabelType)
|
|
|
|
def assorted_types(self):
|
|
"""
|
|
A bunch of mutually unequal types
|
|
"""
|
|
# Avoid polluting the namespace
|
|
context = ir.Context()
|
|
types = [
|
|
ir.LabelType(), ir.VoidType(),
|
|
ir.FunctionType(int1, (int8, int8)), ir.FunctionType(int1, (int8,)),
|
|
ir.FunctionType(int1, (int8,), var_arg=True),
|
|
ir.FunctionType(int8, (int8,)),
|
|
int1, int8, int32, flt, dbl,
|
|
ir.ArrayType(flt, 5), ir.ArrayType(dbl, 5), ir.ArrayType(dbl, 4),
|
|
ir.LiteralStructType((int1, int8)), ir.LiteralStructType((int8,
|
|
int1)),
|
|
context.get_identified_type("MyType1"),
|
|
context.get_identified_type("MyType2"),
|
|
]
|
|
types += [ir.PointerType(tp) for tp in types
|
|
if not isinstance(tp, (ir.VoidType, ir.LabelType))]
|
|
|
|
return types
|
|
|
|
def test_pickling(self):
|
|
types = self.assorted_types()
|
|
for ty in types:
|
|
newty = self.assert_pickle_correctly(ty)
|
|
if self.has_logical_equality(ty):
|
|
self.assertEqual(newty, ty)
|
|
|
|
def test_comparisons(self):
|
|
types = self.assorted_types()
|
|
for a, b in itertools.product(types, types):
|
|
if a is not b:
|
|
self.assertFalse(a == b, (a, b))
|
|
self.assertTrue(a != b, (a, b))
|
|
# We assume copy.copy() works fine here...
|
|
for tp in types:
|
|
other = copy.copy(tp)
|
|
if self.has_logical_equality(tp):
|
|
self.assertTrue(tp == other, (tp, other))
|
|
self.assertFalse(tp != other, (tp, other))
|
|
else:
|
|
self.assertFalse(tp == other, (tp, other))
|
|
self.assertTrue(tp != other, (tp, other))
|
|
|
|
def test_str(self):
|
|
"""
|
|
Test the string representation of types.
|
|
"""
|
|
self.assertEqual(str(int1), 'i1')
|
|
self.assertEqual(str(ir.IntType(29)), 'i29')
|
|
self.assertEqual(str(flt), 'float')
|
|
self.assertEqual(str(dbl), 'double')
|
|
self.assertEqual(str(ir.VoidType()), 'void')
|
|
self.assertEqual(str(ir.FunctionType(int1, ())), 'i1 ()')
|
|
self.assertEqual(str(ir.FunctionType(int1, (flt,))), 'i1 (float)')
|
|
self.assertEqual(str(ir.FunctionType(int1, (flt, dbl))),
|
|
'i1 (float, double)')
|
|
self.assertEqual(str(ir.FunctionType(int1, (), var_arg=True)),
|
|
'i1 (...)')
|
|
self.assertEqual(str(ir.FunctionType(int1, (flt,), var_arg=True)),
|
|
'i1 (float, ...)')
|
|
self.assertEqual(str(ir.FunctionType(int1, (flt, dbl), var_arg=True)),
|
|
'i1 (float, double, ...)')
|
|
self.assertEqual(str(ir.PointerType(int32)), 'i32*')
|
|
self.assertEqual(str(ir.PointerType(ir.PointerType(int32))), 'i32**')
|
|
self.assertEqual(str(ir.ArrayType(int1, 5)), '[5 x i1]')
|
|
self.assertEqual(str(ir.ArrayType(ir.PointerType(int1), 5)),
|
|
'[5 x i1*]')
|
|
self.assertEqual(str(ir.PointerType(ir.ArrayType(int1, 5))),
|
|
'[5 x i1]*')
|
|
self.assertEqual(str(ir.LiteralStructType((int1,))), '{i1}')
|
|
self.assertEqual(str(ir.LiteralStructType((int1, flt))), '{i1, float}')
|
|
self.assertEqual(str(ir.LiteralStructType((
|
|
ir.PointerType(int1), ir.LiteralStructType((int32, int8))))),
|
|
'{i1*, {i32, i8}}')
|
|
self.assertEqual(str(ir.LiteralStructType((int1,), packed=True)),
|
|
'<{i1}>')
|
|
self.assertEqual(str(ir.LiteralStructType((int1, flt), packed=True)),
|
|
'<{i1, float}>')
|
|
|
|
# Avoid polluting the namespace
|
|
context = ir.Context()
|
|
mytype = context.get_identified_type("MyType")
|
|
self.assertEqual(str(mytype), "%\"MyType\"")
|
|
mytype1 = context.get_identified_type("MyType\\")
|
|
self.assertEqual(str(mytype1), "%\"MyType\\5c\"")
|
|
mytype2 = context.get_identified_type("MyType\"")
|
|
self.assertEqual(str(mytype2), "%\"MyType\\22\"")
|
|
|
|
def test_hash(self):
|
|
for typ in filter(self.has_logical_equality, self.assorted_types()):
|
|
self.assertEqual(hash(typ), hash(copy.copy(typ)))
|
|
|
|
def test_gep(self):
|
|
def check_constant(tp, i, expected):
|
|
actual = tp.gep(ir.Constant(int32, i))
|
|
self.assertEqual(actual, expected)
|
|
|
|
def check_index_type(tp):
|
|
index = ir.Constant(dbl, 1.0)
|
|
with self.assertRaises(TypeError):
|
|
tp.gep(index)
|
|
|
|
tp = ir.PointerType(dbl)
|
|
for i in range(5):
|
|
check_constant(tp, i, dbl)
|
|
check_index_type(tp)
|
|
|
|
tp = ir.ArrayType(int1, 3)
|
|
for i in range(3):
|
|
check_constant(tp, i, int1)
|
|
check_index_type(tp)
|
|
|
|
tp = ir.LiteralStructType((dbl, ir.LiteralStructType((int1, int8))))
|
|
check_constant(tp, 0, dbl)
|
|
check_constant(tp, 1, ir.LiteralStructType((int1, int8)))
|
|
with self.assertRaises(IndexError):
|
|
tp.gep(ir.Constant(int32, 2))
|
|
check_index_type(tp)
|
|
|
|
context = ir.Context()
|
|
tp = ir.IdentifiedStructType(context, "MyType")
|
|
tp.set_body(dbl, ir.LiteralStructType((int1, int8)))
|
|
check_constant(tp, 0, dbl)
|
|
check_constant(tp, 1, ir.LiteralStructType((int1, int8)))
|
|
with self.assertRaises(IndexError):
|
|
tp.gep(ir.Constant(int32, 2))
|
|
check_index_type(tp)
|
|
|
|
def test_abi_size(self):
|
|
td = llvm.create_target_data("e-m:e-i64:64-f80:128-n8:16:32:64-S128")
|
|
|
|
def check(tp, expected):
|
|
self.assertEqual(tp.get_abi_size(td), expected)
|
|
check(int8, 1)
|
|
check(int32, 4)
|
|
check(int64, 8)
|
|
check(ir.ArrayType(int8, 5), 5)
|
|
check(ir.ArrayType(int32, 5), 20)
|
|
check(ir.LiteralStructType((dbl, flt, flt)), 16)
|
|
|
|
def test_abi_alignment(self):
|
|
td = llvm.create_target_data("e-m:e-i64:64-f80:128-n8:16:32:64-S128")
|
|
|
|
def check(tp, expected):
|
|
self.assertIn(tp.get_abi_alignment(td), expected)
|
|
check(int8, (1, 2, 4))
|
|
check(int32, (4,))
|
|
check(int64, (8,))
|
|
check(ir.ArrayType(int8, 5), (1, 2, 4))
|
|
check(ir.ArrayType(int32, 5), (4,))
|
|
check(ir.LiteralStructType((dbl, flt, flt)), (8,))
|
|
|
|
def test_identified_struct(self):
|
|
context = ir.Context()
|
|
mytype = context.get_identified_type("MyType")
|
|
module = ir.Module(context=context)
|
|
self.assertTrue(mytype.is_opaque)
|
|
self.assert_valid_ir(module)
|
|
oldstr = str(module)
|
|
mytype.set_body(ir.IntType(32), ir.IntType(64), ir.FloatType())
|
|
self.assertFalse(mytype.is_opaque)
|
|
self.assert_valid_ir(module)
|
|
self.assertNotEqual(oldstr, str(module))
|
|
|
|
def test_target_data_non_default_context(self):
|
|
context = ir.Context()
|
|
mytype = context.get_identified_type("MyType")
|
|
mytype.elements = [ir.IntType(32)]
|
|
td = llvm.create_target_data("e-m:e-i64:64-f80:128-n8:16:32:64-S128")
|
|
self.assertEqual(mytype.get_abi_size(td, context=context), 4)
|
|
|
|
def test_vector(self):
|
|
vecty = ir.VectorType(ir.IntType(32), 8)
|
|
self.assertEqual(str(vecty), "<8 x i32>")
|
|
|
|
|
|
def c32(i):
|
|
return ir.Constant(int32, i)
|
|
|
|
|
|
class TestConstant(TestBase):
|
|
|
|
def test_integers(self):
|
|
c = ir.Constant(int32, 42)
|
|
self.assertEqual(str(c), 'i32 42')
|
|
c = ir.Constant(int1, 1)
|
|
self.assertEqual(str(c), 'i1 1')
|
|
c = ir.Constant(int1, 0)
|
|
self.assertEqual(str(c), 'i1 0')
|
|
c = ir.Constant(int1, True)
|
|
self.assertEqual(str(c), 'i1 true')
|
|
c = ir.Constant(int1, False)
|
|
self.assertEqual(str(c), 'i1 false')
|
|
c = ir.Constant(int1, ir.Undefined)
|
|
self.assertEqual(str(c), 'i1 undef')
|
|
c = ir.Constant(int1, None)
|
|
self.assertEqual(str(c), 'i1 0')
|
|
|
|
def test_reals(self):
|
|
# XXX Test NaNs and infs
|
|
c = ir.Constant(flt, 1.5)
|
|
self.assertEqual(str(c), 'float 0x3ff8000000000000')
|
|
c = ir.Constant(flt, -1.5)
|
|
self.assertEqual(str(c), 'float 0xbff8000000000000')
|
|
c = ir.Constant(dbl, 1.5)
|
|
self.assertEqual(str(c), 'double 0x3ff8000000000000')
|
|
c = ir.Constant(dbl, -1.5)
|
|
self.assertEqual(str(c), 'double 0xbff8000000000000')
|
|
c = ir.Constant(dbl, ir.Undefined)
|
|
self.assertEqual(str(c), 'double undef')
|
|
c = ir.Constant(dbl, None)
|
|
self.assertEqual(str(c), 'double 0.0')
|
|
|
|
def test_arrays(self):
|
|
c = ir.Constant(ir.ArrayType(int32, 3), (c32(5), c32(6), c32(4)))
|
|
self.assertEqual(str(c), '[3 x i32] [i32 5, i32 6, i32 4]')
|
|
c = ir.Constant(ir.ArrayType(int32, 2), (c32(5), c32(ir.Undefined)))
|
|
self.assertEqual(str(c), '[2 x i32] [i32 5, i32 undef]')
|
|
|
|
c = ir.Constant.literal_array((c32(5), c32(6), c32(ir.Undefined)))
|
|
self.assertEqual(str(c), '[3 x i32] [i32 5, i32 6, i32 undef]')
|
|
with self.assertRaises(TypeError) as raises:
|
|
ir.Constant.literal_array((c32(5), ir.Constant(flt, 1.5)))
|
|
self.assertEqual(str(raises.exception),
|
|
"all elements must have the same type")
|
|
|
|
c = ir.Constant(ir.ArrayType(int32, 2), ir.Undefined)
|
|
self.assertEqual(str(c), '[2 x i32] undef')
|
|
c = ir.Constant(ir.ArrayType(int32, 2), None)
|
|
self.assertEqual(str(c), '[2 x i32] zeroinitializer')
|
|
# Raw array syntax
|
|
c = ir.Constant(ir.ArrayType(int8, 11), bytearray(b"foobar_123\x80"))
|
|
self.assertEqual(str(c), r'[11 x i8] c"foobar_123\80"')
|
|
c = ir.Constant(ir.ArrayType(int8, 4), bytearray(b"\x00\x01\x04\xff"))
|
|
self.assertEqual(str(c), r'[4 x i8] c"\00\01\04\ff"')
|
|
# Recursive instantiation of inner constants
|
|
c = ir.Constant(ir.ArrayType(int32, 3), (5, ir.Undefined, 6))
|
|
self.assertEqual(str(c), '[3 x i32] [i32 5, i32 undef, i32 6]')
|
|
# Invalid number of args
|
|
with self.assertRaises(ValueError):
|
|
ir.Constant(ir.ArrayType(int32, 3), (5, 6))
|
|
|
|
def test_vector(self):
|
|
vecty = ir.VectorType(ir.IntType(32), 8)
|
|
vals = [1, 2, 4, 3, 8, 6, 9, 7]
|
|
vec = ir.Constant(vecty, vals)
|
|
vec_repr = "<8 x i32> <{}>".format(
|
|
', '.join(map('i32 {}'.format, vals)))
|
|
self.assertEqual(str(vec), vec_repr)
|
|
|
|
def test_non_nullable_int(self):
|
|
constant = ir.Constant(ir.IntType(32), None).constant
|
|
self.assertEqual(constant, 0)
|
|
|
|
def test_structs(self):
|
|
st1 = ir.LiteralStructType((flt, int1))
|
|
st2 = ir.LiteralStructType((int32, st1))
|
|
c = ir.Constant(st1, (ir.Constant(ir.FloatType(), 1.5),
|
|
ir.Constant(int1, True)))
|
|
self.assertEqual(str(c),
|
|
'{float, i1} {float 0x3ff8000000000000, i1 true}')
|
|
c = ir.Constant.literal_struct((ir.Constant(ir.FloatType(), 1.5),
|
|
ir.Constant(int1, True)))
|
|
self.assertEqual(c.type, st1)
|
|
self.assertEqual(str(c),
|
|
'{float, i1} {float 0x3ff8000000000000, i1 true}')
|
|
c = ir.Constant.literal_struct((ir.Constant(ir.FloatType(), 1.5),
|
|
ir.Constant(int1, ir.Undefined)))
|
|
self.assertEqual(c.type, st1)
|
|
self.assertEqual(str(c),
|
|
'{float, i1} {float 0x3ff8000000000000, i1 undef}')
|
|
c = ir.Constant(st1, ir.Undefined)
|
|
self.assertEqual(str(c), '{float, i1} undef')
|
|
c = ir.Constant(st1, None)
|
|
self.assertEqual(str(c), '{float, i1} zeroinitializer')
|
|
# Recursive instantiation of inner constants
|
|
c1 = ir.Constant(st1, (1.5, True))
|
|
self.assertEqual(str(c1),
|
|
'{float, i1} {float 0x3ff8000000000000, i1 true}')
|
|
c2 = ir.Constant(st2, (42, c1))
|
|
self.assertEqual(str(c2), ('{i32, {float, i1}} {i32 42, {float, i1} '
|
|
'{float 0x3ff8000000000000, i1 true}}'))
|
|
c3 = ir.Constant(st2, (42, (1.5, True)))
|
|
self.assertEqual(str(c3), str(c2))
|
|
# Invalid number of args
|
|
with self.assertRaises(ValueError):
|
|
ir.Constant(st2, (4, 5, 6))
|
|
|
|
def test_undefined_literal_struct_pickling(self):
|
|
i8 = ir.IntType(8)
|
|
st = ir.Constant(ir.LiteralStructType([i8, i8]), ir.Undefined)
|
|
self.assert_pickle_correctly(st)
|
|
|
|
def test_type_instantiaton(self):
|
|
"""
|
|
Instantiating a type should create a constant.
|
|
"""
|
|
c = int8(42)
|
|
self.assertIsInstance(c, ir.Constant)
|
|
self.assertEqual(str(c), 'i8 42')
|
|
c = int1(True)
|
|
self.assertIsInstance(c, ir.Constant)
|
|
self.assertEqual(str(c), 'i1 true')
|
|
# Arrays
|
|
at = ir.ArrayType(int32, 3)
|
|
c = at([c32(4), c32(5), c32(6)])
|
|
self.assertEqual(str(c), '[3 x i32] [i32 4, i32 5, i32 6]')
|
|
c = at([4, 5, 6])
|
|
self.assertEqual(str(c), '[3 x i32] [i32 4, i32 5, i32 6]')
|
|
c = at(None)
|
|
self.assertEqual(str(c), '[3 x i32] zeroinitializer')
|
|
with self.assertRaises(ValueError):
|
|
at([4, 5, 6, 7])
|
|
# Structs
|
|
st1 = ir.LiteralStructType((flt, int1))
|
|
st2 = ir.LiteralStructType((int32, st1))
|
|
c = st1((1.5, True))
|
|
self.assertEqual(str(c), ('{float, i1} {float 0x3ff8000000000000, i1 '
|
|
'true}'))
|
|
c = st2((42, (1.5, True)))
|
|
self.assertEqual(str(c), ('{i32, {float, i1}} {i32 42, {float, i1} '
|
|
'{float 0x3ff8000000000000, i1 true}}'))
|
|
|
|
def test_repr(self):
|
|
"""
|
|
Constants should have a useful repr().
|
|
"""
|
|
c = int32(42)
|
|
self.assertEqual(repr(c), "<ir.Constant type='i32' value=42>")
|
|
|
|
def test_encoding_problem(self):
|
|
c = ir.Constant(ir.ArrayType(ir.IntType(8), 256),
|
|
bytearray(range(256)))
|
|
m = self.module()
|
|
gv = ir.GlobalVariable(m, c.type, "myconstant")
|
|
gv.global_constant = True
|
|
gv.initializer = c
|
|
# With utf-8, the following will cause:
|
|
# UnicodeDecodeError: 'utf-8' codec can't decode byte 0xe0 in position
|
|
# 136: invalid continuation byte
|
|
parsed = llvm.parse_assembly(str(m))
|
|
# Make sure the encoding does not modify the IR
|
|
reparsed = llvm.parse_assembly(str(parsed))
|
|
self.assertEqual(str(parsed), str(reparsed))
|
|
|
|
def test_gep(self):
|
|
m = self.module()
|
|
tp = ir.LiteralStructType((flt, int1))
|
|
gv = ir.GlobalVariable(m, tp, "myconstant")
|
|
c = gv.gep([ir.Constant(int32, x) for x in (0, 1)])
|
|
self.assertEqual(str(c),
|
|
'getelementptr ({float, i1}, {float, i1}* @"myconstant", i32 0, i32 1)') # noqa E501
|
|
self.assertEqual(c.type, ir.PointerType(int1))
|
|
|
|
const = ir.Constant(tp, None)
|
|
with self.assertRaises(TypeError):
|
|
const.gep([ir.Constant(int32, 0)])
|
|
|
|
const_ptr = ir.Constant(tp.as_pointer(), None)
|
|
c2 = const_ptr.gep([ir.Constant(int32, 0)])
|
|
self.assertEqual(str(c2),
|
|
'getelementptr ({float, i1}, {float, i1}* null, i32 0)') # noqa E501
|
|
self.assertEqual(c.type, ir.PointerType(int1))
|
|
|
|
def test_gep_addrspace_globalvar(self):
|
|
m = self.module()
|
|
tp = ir.LiteralStructType((flt, int1))
|
|
addrspace = 4
|
|
|
|
gv = ir.GlobalVariable(m, tp, "myconstant", addrspace=addrspace)
|
|
self.assertEqual(gv.addrspace, addrspace)
|
|
c = gv.gep([ir.Constant(int32, x) for x in (0, 1)])
|
|
self.assertEqual(c.type.addrspace, addrspace)
|
|
self.assertEqual(str(c),
|
|
('getelementptr ({float, i1}, {float, i1} '
|
|
'addrspace(4)* @"myconstant", i32 0, i32 1)'))
|
|
self.assertEqual(c.type, ir.PointerType(int1, addrspace=addrspace))
|
|
|
|
def test_trunc(self):
|
|
c = ir.Constant(int64, 1).trunc(int32)
|
|
self.assertEqual(str(c), 'trunc (i64 1 to i32)')
|
|
|
|
def test_zext(self):
|
|
c = ir.Constant(int32, 1).zext(int64)
|
|
self.assertEqual(str(c), 'zext (i32 1 to i64)')
|
|
|
|
def test_sext(self):
|
|
c = ir.Constant(int32, -1).sext(int64)
|
|
self.assertEqual(str(c), 'sext (i32 -1 to i64)')
|
|
|
|
def test_fptrunc(self):
|
|
c = ir.Constant(flt, 1).fptrunc(hlf)
|
|
self.assertEqual(str(c), 'fptrunc (float 0x3ff0000000000000 to half)')
|
|
|
|
def test_fpext(self):
|
|
c = ir.Constant(flt, 1).fpext(dbl)
|
|
self.assertEqual(str(c), 'fpext (float 0x3ff0000000000000 to double)')
|
|
|
|
def test_bitcast(self):
|
|
m = self.module()
|
|
gv = ir.GlobalVariable(m, int32, "myconstant")
|
|
c = gv.bitcast(int64.as_pointer())
|
|
self.assertEqual(str(c), 'bitcast (i32* @"myconstant" to i64*)')
|
|
|
|
def test_fptoui(self):
|
|
c = ir.Constant(flt, 1).fptoui(int32)
|
|
self.assertEqual(str(c), 'fptoui (float 0x3ff0000000000000 to i32)')
|
|
|
|
def test_uitofp(self):
|
|
c = ir.Constant(int32, 1).uitofp(flt)
|
|
self.assertEqual(str(c), 'uitofp (i32 1 to float)')
|
|
|
|
def test_fptosi(self):
|
|
c = ir.Constant(flt, 1).fptosi(int32)
|
|
self.assertEqual(str(c), 'fptosi (float 0x3ff0000000000000 to i32)')
|
|
|
|
def test_sitofp(self):
|
|
c = ir.Constant(int32, 1).sitofp(flt)
|
|
self.assertEqual(str(c), 'sitofp (i32 1 to float)')
|
|
|
|
def test_ptrtoint_1(self):
|
|
ptr = ir.Constant(int64.as_pointer(), None)
|
|
one = ir.Constant(int32, 1)
|
|
c = ptr.ptrtoint(int32)
|
|
|
|
self.assertRaises(TypeError, one.ptrtoint, int64)
|
|
self.assertRaises(TypeError, ptr.ptrtoint, flt)
|
|
self.assertEqual(str(c), 'ptrtoint (i64* null to i32)')
|
|
|
|
def test_ptrtoint_2(self):
|
|
m = self.module()
|
|
gv = ir.GlobalVariable(m, int32, "myconstant")
|
|
c = gv.ptrtoint(int64)
|
|
self.assertEqual(str(c), 'ptrtoint (i32* @"myconstant" to i64)')
|
|
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
r"can only ptrtoint\(\) to integer type, not 'i64\*'",
|
|
gv.ptrtoint,
|
|
int64.as_pointer())
|
|
|
|
c2 = ir.Constant(int32, 0)
|
|
self.assertRaisesRegex(
|
|
TypeError,
|
|
r"can only call ptrtoint\(\) on pointer type, not 'i32'",
|
|
c2.ptrtoint,
|
|
int64)
|
|
|
|
def test_inttoptr(self):
|
|
one = ir.Constant(int32, 1)
|
|
pi = ir.Constant(flt, 3.14)
|
|
c = one.inttoptr(int64.as_pointer())
|
|
|
|
self.assertRaises(TypeError, one.inttoptr, int64)
|
|
self.assertRaises(TypeError, pi.inttoptr, int64.as_pointer())
|
|
self.assertEqual(str(c), 'inttoptr (i32 1 to i64*)')
|
|
|
|
def test_neg(self):
|
|
one = ir.Constant(int32, 1)
|
|
self.assertEqual(str(one.neg()), 'sub (i32 0, i32 1)')
|
|
|
|
def test_not(self):
|
|
one = ir.Constant(int32, 1)
|
|
self.assertEqual(str(one.not_()), 'xor (i32 1, i32 -1)')
|
|
|
|
def test_fneg(self):
|
|
one = ir.Constant(flt, 1)
|
|
self.assertEqual(str(one.fneg()), 'fneg (float 0x3ff0000000000000)')
|
|
|
|
def test_int_binops(self):
|
|
one = ir.Constant(int32, 1)
|
|
two = ir.Constant(int32, 2)
|
|
|
|
oracle = {one.shl: 'shl', one.lshr: 'lshr', one.ashr: 'ashr',
|
|
one.add: 'add', one.sub: 'sub', one.mul: 'mul',
|
|
one.udiv: 'udiv', one.sdiv: 'sdiv', one.urem: 'urem',
|
|
one.srem: 'srem', one.or_: 'or', one.and_: 'and',
|
|
one.xor: 'xor'}
|
|
for fn, irop in oracle.items():
|
|
self.assertEqual(str(fn(two)), irop + ' (i32 1, i32 2)')
|
|
|
|
# unsigned integer compare
|
|
oracle = {'==': 'eq', '!=': 'ne', '>':
|
|
'ugt', '>=': 'uge', '<': 'ult', '<=': 'ule'}
|
|
for cop, cond in oracle.items():
|
|
actual = str(one.icmp_unsigned(cop, two))
|
|
expected = 'icmp ' + cond + ' (i32 1, i32 2)'
|
|
self.assertEqual(actual, expected)
|
|
|
|
# signed integer compare
|
|
oracle = {'==': 'eq', '!=': 'ne',
|
|
'>': 'sgt', '>=': 'sge', '<': 'slt', '<=': 'sle'}
|
|
for cop, cond in oracle.items():
|
|
actual = str(one.icmp_signed(cop, two))
|
|
expected = 'icmp ' + cond + ' (i32 1, i32 2)'
|
|
self.assertEqual(actual, expected)
|
|
|
|
def test_flt_binops(self):
|
|
one = ir.Constant(flt, 1)
|
|
two = ir.Constant(flt, 2)
|
|
|
|
oracle = {one.fadd: 'fadd', one.fsub: 'fsub', one.fmul: 'fmul',
|
|
one.fdiv: 'fdiv', one.frem: 'frem'}
|
|
for fn, irop in oracle.items():
|
|
actual = str(fn(two))
|
|
expected = irop + (' (float 0x3ff0000000000000,'
|
|
' float 0x4000000000000000)')
|
|
self.assertEqual(actual, expected)
|
|
|
|
# ordered float compare
|
|
oracle = {'==': 'oeq', '!=': 'one', '>': 'ogt', '>=': 'oge',
|
|
'<': 'olt', '<=': 'ole'}
|
|
for cop, cond in oracle.items():
|
|
actual = str(one.fcmp_ordered(cop, two))
|
|
expected = 'fcmp ' + cond + (' (float 0x3ff0000000000000,'
|
|
' float 0x4000000000000000)')
|
|
self.assertEqual(actual, expected)
|
|
|
|
# unordered float compare
|
|
oracle = {'==': 'ueq', '!=': 'une', '>': 'ugt', '>=': 'uge',
|
|
'<': 'ult', '<=': 'ule'}
|
|
for cop, cond in oracle.items():
|
|
actual = str(one.fcmp_unordered(cop, two))
|
|
expected = 'fcmp ' + cond + (' (float 0x3ff0000000000000,'
|
|
' float 0x4000000000000000)')
|
|
self.assertEqual(actual, expected)
|
|
|
|
|
|
class TestTransforms(TestBase):
|
|
def test_call_transform(self):
|
|
mod = ir.Module()
|
|
foo = ir.Function(mod, ir.FunctionType(ir.VoidType(), ()), "foo")
|
|
bar = ir.Function(mod, ir.FunctionType(ir.VoidType(), ()), "bar")
|
|
builder = ir.IRBuilder()
|
|
builder.position_at_end(foo.append_basic_block())
|
|
call = builder.call(foo, ())
|
|
self.assertEqual(call.callee, foo)
|
|
modified = ir.replace_all_calls(mod, foo, bar)
|
|
self.assertIn(call, modified)
|
|
self.assertNotEqual(call.callee, foo)
|
|
self.assertEqual(call.callee, bar)
|
|
|
|
|
|
class TestSingleton(TestBase):
|
|
def test_undefined(self):
|
|
self.assertIs(ir.Undefined, ir.values._Undefined())
|
|
self.assertIs(ir.Undefined, copy.copy(ir.Undefined))
|
|
self.assertIs(ir.Undefined, copy.deepcopy(ir.Undefined))
|
|
self.assert_pickle_correctly(ir.Undefined)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|