Videre
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
"""
|
||||
This subpackage implements the LLVM IR classes in pure python
|
||||
"""
|
||||
|
||||
from .types import *
|
||||
from .values import *
|
||||
from .module import *
|
||||
from .builder import *
|
||||
from .instructions import *
|
||||
from .transforms import *
|
||||
from .context import Context, global_context
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,80 @@
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
class DuplicatedNameError(NameError):
|
||||
pass
|
||||
|
||||
|
||||
class NameScope(object):
|
||||
def __init__(self):
|
||||
self._useset = set([''])
|
||||
self._basenamemap = defaultdict(int)
|
||||
|
||||
def is_used(self, name):
|
||||
return name in self._useset
|
||||
|
||||
def register(self, name, deduplicate=False):
|
||||
if deduplicate:
|
||||
name = self.deduplicate(name)
|
||||
elif self.is_used(name):
|
||||
raise DuplicatedNameError(name)
|
||||
self._useset.add(name)
|
||||
return name
|
||||
|
||||
def deduplicate(self, name):
|
||||
basename = name
|
||||
while self.is_used(name):
|
||||
ident = self._basenamemap[basename] + 1
|
||||
self._basenamemap[basename] = ident
|
||||
name = "{0}.{1}".format(basename, ident)
|
||||
return name
|
||||
|
||||
def get_child(self):
|
||||
return type(self)(parent=self)
|
||||
|
||||
|
||||
class _StrCaching(object):
|
||||
|
||||
def _clear_string_cache(self):
|
||||
try:
|
||||
del self.__cached_str
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
try:
|
||||
return self.__cached_str
|
||||
except AttributeError:
|
||||
s = self.__cached_str = self._to_string()
|
||||
return s
|
||||
|
||||
|
||||
class _StringReferenceCaching(object):
|
||||
|
||||
def get_reference(self):
|
||||
try:
|
||||
return self.__cached_refstr
|
||||
except AttributeError:
|
||||
s = self.__cached_refstr = self._get_reference()
|
||||
return s
|
||||
|
||||
|
||||
class _HasMetadata(object):
|
||||
|
||||
def set_metadata(self, name, node):
|
||||
"""
|
||||
Attach unnamed metadata *node* to the metadata slot *name* of this
|
||||
value.
|
||||
"""
|
||||
self.metadata[name] = node
|
||||
|
||||
def _stringify_metadata(self, leading_comma=False):
|
||||
if self.metadata:
|
||||
buf = []
|
||||
if leading_comma:
|
||||
buf.append("")
|
||||
buf += ["!{0} {1}".format(k, v.get_reference())
|
||||
for k, v in self.metadata.items()]
|
||||
return ', '.join(buf)
|
||||
else:
|
||||
return ''
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,20 @@
|
||||
from llvmlite.ir import _utils
|
||||
from llvmlite.ir import types
|
||||
|
||||
|
||||
class Context(object):
|
||||
def __init__(self):
|
||||
self.scope = _utils.NameScope()
|
||||
self.identified_types = {}
|
||||
|
||||
def get_identified_type(self, name, packed=False):
|
||||
if name not in self.identified_types:
|
||||
self.scope.register(name)
|
||||
ty = types.IdentifiedStructType(self, name, packed)
|
||||
self.identified_types[name] = ty
|
||||
else:
|
||||
ty = self.identified_types[name]
|
||||
return ty
|
||||
|
||||
|
||||
global_context = Context()
|
||||
@@ -0,0 +1,920 @@
|
||||
"""
|
||||
Implementation of LLVM IR instructions.
|
||||
"""
|
||||
|
||||
from llvmlite.ir import types
|
||||
from llvmlite.ir.values import (Block, Function, Value, NamedValue, Constant,
|
||||
MetaDataArgument, MetaDataString, AttributeSet,
|
||||
Undefined, ArgumentAttributes)
|
||||
from llvmlite.ir._utils import _HasMetadata
|
||||
|
||||
|
||||
class Instruction(NamedValue, _HasMetadata):
|
||||
def __init__(self, parent, typ, opname, operands, name='', flags=()):
|
||||
super(Instruction, self).__init__(parent, typ, name=name)
|
||||
assert isinstance(parent, Block)
|
||||
assert isinstance(flags, (tuple, list))
|
||||
self.opname = opname
|
||||
self.operands = operands
|
||||
self.flags = list(flags)
|
||||
self.metadata = {}
|
||||
|
||||
@property
|
||||
def function(self):
|
||||
return self.parent.function
|
||||
|
||||
@property
|
||||
def module(self):
|
||||
return self.parent.function.module
|
||||
|
||||
def descr(self, buf):
|
||||
opname = self.opname
|
||||
if self.flags:
|
||||
opname = ' '.join([opname] + self.flags)
|
||||
operands = ', '.join([op.get_reference() for op in self.operands])
|
||||
typ = self.type
|
||||
metadata = self._stringify_metadata(leading_comma=True)
|
||||
buf.append("{0} {1} {2}{3}\n"
|
||||
.format(opname, typ, operands, metadata))
|
||||
|
||||
def replace_usage(self, old, new):
|
||||
if old in self.operands:
|
||||
ops = []
|
||||
for op in self.operands:
|
||||
ops.append(new if op is old else op)
|
||||
self.operands = tuple(ops)
|
||||
self._clear_string_cache()
|
||||
|
||||
def __repr__(self):
|
||||
return "<ir.%s %r of type '%s', opname %r, operands %r>" % (
|
||||
self.__class__.__name__, self.name, self.type,
|
||||
self.opname, self.operands)
|
||||
|
||||
|
||||
class CallInstrAttributes(AttributeSet):
|
||||
_known = frozenset(['convergent', 'noreturn', 'nounwind', 'readonly',
|
||||
'readnone', 'noinline', 'alwaysinline'])
|
||||
|
||||
|
||||
TailMarkerOptions = frozenset(['tail', 'musttail', 'notail'])
|
||||
|
||||
|
||||
class FastMathFlags(AttributeSet):
|
||||
_known = frozenset(['fast', 'nnan', 'ninf', 'nsz', 'arcp', 'contract',
|
||||
'afn', 'reassoc'])
|
||||
|
||||
|
||||
class CallInstr(Instruction):
|
||||
def __init__(self, parent, func, args, name='', cconv=None, tail=None,
|
||||
fastmath=(), attrs=(), arg_attrs=None):
|
||||
self.cconv = (func.calling_convention
|
||||
if cconv is None and isinstance(func, Function)
|
||||
else cconv)
|
||||
|
||||
# For backwards compatibility with previous API of accepting a "truthy"
|
||||
# value for a hint to the optimizer to potentially tail optimize.
|
||||
if isinstance(tail, str) and tail in TailMarkerOptions:
|
||||
pass
|
||||
elif tail:
|
||||
tail = "tail"
|
||||
else:
|
||||
tail = ""
|
||||
|
||||
self.tail = tail
|
||||
self.fastmath = FastMathFlags(fastmath)
|
||||
self.attributes = CallInstrAttributes(attrs)
|
||||
self.arg_attributes = {}
|
||||
if arg_attrs:
|
||||
for idx, attrs in arg_attrs.items():
|
||||
if not (0 <= idx < len(args)):
|
||||
raise ValueError("Invalid argument index {}"
|
||||
.format(idx))
|
||||
self.arg_attributes[idx] = ArgumentAttributes(attrs)
|
||||
|
||||
# Fix and validate arguments
|
||||
args = list(args)
|
||||
for i in range(len(func.function_type.args)):
|
||||
arg = args[i]
|
||||
expected_type = func.function_type.args[i]
|
||||
if (isinstance(expected_type, types.MetaDataType) and
|
||||
arg.type != expected_type):
|
||||
arg = MetaDataArgument(arg)
|
||||
if arg.type != expected_type:
|
||||
msg = ("Type of #{0} arg mismatch: {1} != {2}"
|
||||
.format(1 + i, expected_type, arg.type))
|
||||
raise TypeError(msg)
|
||||
args[i] = arg
|
||||
|
||||
super(CallInstr, self).__init__(parent, func.function_type.return_type,
|
||||
"call", [func] + list(args), name=name)
|
||||
|
||||
@property
|
||||
def callee(self):
|
||||
return self.operands[0]
|
||||
|
||||
@callee.setter
|
||||
def callee(self, newcallee):
|
||||
self.operands[0] = newcallee
|
||||
|
||||
@property
|
||||
def args(self):
|
||||
return self.operands[1:]
|
||||
|
||||
def replace_callee(self, newfunc):
|
||||
if newfunc.function_type != self.callee.function_type:
|
||||
raise TypeError("New function has incompatible type")
|
||||
self.callee = newfunc
|
||||
|
||||
@property
|
||||
def called_function(self):
|
||||
"""The callee function"""
|
||||
return self.callee
|
||||
|
||||
def _descr(self, buf, add_metadata):
|
||||
def descr_arg(i, a):
|
||||
if i in self.arg_attributes:
|
||||
attrs = ' '.join(self.arg_attributes[i]._to_list(a.type)) + ' '
|
||||
else:
|
||||
attrs = ''
|
||||
return '{0} {1}{2}'.format(a.type, attrs, a.get_reference())
|
||||
args = ', '.join([descr_arg(i, a) for i, a in enumerate(self.args)])
|
||||
|
||||
fnty = self.callee.function_type
|
||||
# Only print function type if variable-argument
|
||||
if fnty.var_arg:
|
||||
ty = fnty
|
||||
# Otherwise, just print the return type.
|
||||
else:
|
||||
# Fastmath flag work only in this case
|
||||
ty = fnty.return_type
|
||||
callee_ref = "{0} {1}".format(ty, self.callee.get_reference())
|
||||
if self.cconv:
|
||||
callee_ref = "{0} {1}".format(self.cconv, callee_ref)
|
||||
|
||||
tail_marker = ""
|
||||
if self.tail:
|
||||
tail_marker = "{0} ".format(self.tail)
|
||||
|
||||
fn_attrs = ' ' + ' '.join(self.attributes._to_list(fnty.return_type))\
|
||||
if self.attributes else ''
|
||||
|
||||
fm_attrs = ' ' + ' '.join(self.fastmath._to_list(fnty.return_type))\
|
||||
if self.fastmath else ''
|
||||
|
||||
buf.append("{tail}{op}{fastmath} {callee}({args}){attr}{meta}\n".format(
|
||||
tail=tail_marker,
|
||||
op=self.opname,
|
||||
callee=callee_ref,
|
||||
fastmath=fm_attrs,
|
||||
args=args,
|
||||
attr=fn_attrs,
|
||||
meta=(self._stringify_metadata(leading_comma=True)
|
||||
if add_metadata else ""),
|
||||
))
|
||||
|
||||
def descr(self, buf):
|
||||
self._descr(buf, add_metadata=True)
|
||||
|
||||
|
||||
class InvokeInstr(CallInstr):
|
||||
def __init__(self, parent, func, args, normal_to, unwind_to, name='',
|
||||
cconv=None, fastmath=(), attrs=(), arg_attrs=None):
|
||||
assert isinstance(normal_to, Block)
|
||||
assert isinstance(unwind_to, Block)
|
||||
super(InvokeInstr, self).__init__(parent, func, args, name, cconv,
|
||||
tail=False, fastmath=fastmath,
|
||||
attrs=attrs, arg_attrs=arg_attrs)
|
||||
self.opname = "invoke"
|
||||
self.normal_to = normal_to
|
||||
self.unwind_to = unwind_to
|
||||
|
||||
def descr(self, buf):
|
||||
super(InvokeInstr, self)._descr(buf, add_metadata=False)
|
||||
buf.append(" to label {0} unwind label {1}{metadata}\n".format(
|
||||
self.normal_to.get_reference(),
|
||||
self.unwind_to.get_reference(),
|
||||
metadata=self._stringify_metadata(leading_comma=True),
|
||||
))
|
||||
|
||||
|
||||
class Terminator(Instruction):
|
||||
def __init__(self, parent, opname, operands):
|
||||
super(Terminator, self).__init__(parent, types.VoidType(), opname,
|
||||
operands)
|
||||
|
||||
def descr(self, buf):
|
||||
opname = self.opname
|
||||
operands = ', '.join(["{0} {1}".format(op.type, op.get_reference())
|
||||
for op in self.operands])
|
||||
metadata = self._stringify_metadata(leading_comma=True)
|
||||
buf.append("{0} {1}{2}".format(opname, operands, metadata))
|
||||
|
||||
|
||||
class PredictableInstr(Instruction):
|
||||
|
||||
def set_weights(self, weights):
|
||||
operands = [MetaDataString(self.module, "branch_weights")]
|
||||
for w in weights:
|
||||
if w < 0:
|
||||
raise ValueError("branch weight must be a positive integer")
|
||||
operands.append(Constant(types.IntType(32), w))
|
||||
md = self.module.add_metadata(operands)
|
||||
self.set_metadata("prof", md)
|
||||
|
||||
|
||||
class Ret(Terminator):
|
||||
def __init__(self, parent, opname, return_value=None):
|
||||
operands = [return_value] if return_value is not None else []
|
||||
super(Ret, self).__init__(parent, opname, operands)
|
||||
|
||||
@property
|
||||
def return_value(self):
|
||||
if self.operands:
|
||||
return self.operands[0]
|
||||
else:
|
||||
return None
|
||||
|
||||
def descr(self, buf):
|
||||
return_value = self.return_value
|
||||
metadata = self._stringify_metadata(leading_comma=True)
|
||||
if return_value is not None:
|
||||
buf.append("{0} {1} {2}{3}\n"
|
||||
.format(self.opname, return_value.type,
|
||||
return_value.get_reference(),
|
||||
metadata))
|
||||
else:
|
||||
buf.append("{0}{1}\n".format(self.opname, metadata))
|
||||
|
||||
|
||||
class Branch(Terminator):
|
||||
pass
|
||||
|
||||
|
||||
class ConditionalBranch(PredictableInstr, Terminator):
|
||||
pass
|
||||
|
||||
|
||||
class IndirectBranch(PredictableInstr, Terminator):
|
||||
def __init__(self, parent, opname, addr):
|
||||
super(IndirectBranch, self).__init__(parent, opname, [addr])
|
||||
self.destinations = []
|
||||
|
||||
@property
|
||||
def address(self):
|
||||
return self.operands[0]
|
||||
|
||||
def add_destination(self, block):
|
||||
assert isinstance(block, Block)
|
||||
self.destinations.append(block)
|
||||
|
||||
def descr(self, buf):
|
||||
destinations = ["label {0}".format(blk.get_reference())
|
||||
for blk in self.destinations]
|
||||
buf.append("indirectbr {0} {1}, [{2}] {3}\n".format(
|
||||
self.address.type,
|
||||
self.address.get_reference(),
|
||||
', '.join(destinations),
|
||||
self._stringify_metadata(leading_comma=True),
|
||||
))
|
||||
|
||||
|
||||
class SwitchInstr(PredictableInstr, Terminator):
|
||||
|
||||
def __init__(self, parent, opname, val, default):
|
||||
super(SwitchInstr, self).__init__(parent, opname, [val])
|
||||
self.default = default
|
||||
self.cases = []
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
return self.operands[0]
|
||||
|
||||
def add_case(self, val, block):
|
||||
assert isinstance(block, Block)
|
||||
if not isinstance(val, Value):
|
||||
val = Constant(self.value.type, val)
|
||||
self.cases.append((val, block))
|
||||
|
||||
def descr(self, buf):
|
||||
cases = ["{0} {1}, label {2}".format(val.type, val.get_reference(),
|
||||
blk.get_reference())
|
||||
for val, blk in self.cases]
|
||||
buf.append("switch {0} {1}, label {2} [{3}] {4}\n".format(
|
||||
self.value.type,
|
||||
self.value.get_reference(),
|
||||
self.default.get_reference(),
|
||||
' '.join(cases),
|
||||
self._stringify_metadata(leading_comma=True),
|
||||
))
|
||||
|
||||
|
||||
class Resume(Terminator):
|
||||
pass
|
||||
|
||||
|
||||
class SelectInstr(Instruction):
|
||||
def __init__(self, parent, cond, lhs, rhs, name='', flags=()):
|
||||
assert lhs.type == rhs.type
|
||||
super(SelectInstr, self).__init__(parent, lhs.type, "select",
|
||||
[cond, lhs, rhs], name=name,
|
||||
flags=flags)
|
||||
|
||||
@property
|
||||
def cond(self):
|
||||
return self.operands[0]
|
||||
|
||||
@property
|
||||
def lhs(self):
|
||||
return self.operands[1]
|
||||
|
||||
@property
|
||||
def rhs(self):
|
||||
return self.operands[2]
|
||||
|
||||
def descr(self, buf):
|
||||
buf.append("select {0} {1} {2}, {3} {4}, {5} {6} {7}\n".format(
|
||||
' '.join(self.flags),
|
||||
self.cond.type, self.cond.get_reference(),
|
||||
self.lhs.type, self.lhs.get_reference(),
|
||||
self.rhs.type, self.rhs.get_reference(),
|
||||
self._stringify_metadata(leading_comma=True),
|
||||
))
|
||||
|
||||
|
||||
class CompareInstr(Instruction):
|
||||
# Define the following in subclasses
|
||||
OPNAME = 'invalid-compare'
|
||||
VALID_OP = {}
|
||||
|
||||
def __init__(self, parent, op, lhs, rhs, name='', flags=[]):
|
||||
if op not in self.VALID_OP:
|
||||
raise ValueError("invalid comparison %r for %s" % (op, self.OPNAME))
|
||||
for flag in flags:
|
||||
if flag not in self.VALID_FLAG:
|
||||
raise ValueError("invalid flag %r for %s" % (flag, self.OPNAME))
|
||||
opname = self.OPNAME
|
||||
if isinstance(lhs.type, types.VectorType):
|
||||
typ = types.VectorType(types.IntType(1), lhs.type.count)
|
||||
else:
|
||||
typ = types.IntType(1)
|
||||
super(CompareInstr, self).__init__(parent, typ,
|
||||
opname, [lhs, rhs], flags=flags,
|
||||
name=name)
|
||||
self.op = op
|
||||
|
||||
def descr(self, buf):
|
||||
buf.append("{opname}{flags} {op} {ty} {lhs}, {rhs} {meta}\n".format(
|
||||
opname=self.opname,
|
||||
flags=''.join(' ' + it for it in self.flags),
|
||||
op=self.op,
|
||||
ty=self.operands[0].type,
|
||||
lhs=self.operands[0].get_reference(),
|
||||
rhs=self.operands[1].get_reference(),
|
||||
meta=self._stringify_metadata(leading_comma=True),
|
||||
))
|
||||
|
||||
|
||||
class ICMPInstr(CompareInstr):
|
||||
OPNAME = 'icmp'
|
||||
VALID_OP = {
|
||||
'eq': 'equal',
|
||||
'ne': 'not equal',
|
||||
'ugt': 'unsigned greater than',
|
||||
'uge': 'unsigned greater or equal',
|
||||
'ult': 'unsigned less than',
|
||||
'ule': 'unsigned less or equal',
|
||||
'sgt': 'signed greater than',
|
||||
'sge': 'signed greater or equal',
|
||||
'slt': 'signed less than',
|
||||
'sle': 'signed less or equal',
|
||||
}
|
||||
VALID_FLAG = set()
|
||||
|
||||
|
||||
class FCMPInstr(CompareInstr):
|
||||
OPNAME = 'fcmp'
|
||||
VALID_OP = {
|
||||
'false': 'no comparison, always returns false',
|
||||
'oeq': 'ordered and equal',
|
||||
'ogt': 'ordered and greater than',
|
||||
'oge': 'ordered and greater than or equal',
|
||||
'olt': 'ordered and less than',
|
||||
'ole': 'ordered and less than or equal',
|
||||
'one': 'ordered and not equal',
|
||||
'ord': 'ordered (no nans)',
|
||||
'ueq': 'unordered or equal',
|
||||
'ugt': 'unordered or greater than',
|
||||
'uge': 'unordered or greater than or equal',
|
||||
'ult': 'unordered or less than',
|
||||
'ule': 'unordered or less than or equal',
|
||||
'une': 'unordered or not equal',
|
||||
'uno': 'unordered (either nans)',
|
||||
'true': 'no comparison, always returns true',
|
||||
}
|
||||
VALID_FLAG = {'nnan', 'ninf', 'nsz', 'arcp', 'contract', 'afn', 'reassoc',
|
||||
'fast'}
|
||||
|
||||
|
||||
class CastInstr(Instruction):
|
||||
def __init__(self, parent, op, val, typ, name=''):
|
||||
super(CastInstr, self).__init__(parent, typ, op, [val], name=name)
|
||||
|
||||
def descr(self, buf):
|
||||
buf.append("{0} {1} {2} to {3} {4}\n".format(
|
||||
self.opname,
|
||||
self.operands[0].type,
|
||||
self.operands[0].get_reference(),
|
||||
self.type,
|
||||
self._stringify_metadata(leading_comma=True),
|
||||
))
|
||||
|
||||
|
||||
class LoadInstr(Instruction):
|
||||
|
||||
def __init__(self, parent, ptr, name='', typ=None):
|
||||
if typ is None:
|
||||
if isinstance(ptr, AllocaInstr):
|
||||
typ = ptr.allocated_type
|
||||
# For compatibility with typed pointers. Eventually this should
|
||||
# probably be removed (when typed pointers are fully removed).
|
||||
elif not ptr.type.is_opaque:
|
||||
typ = ptr.type.pointee
|
||||
else:
|
||||
raise ValueError("Load lacks type.")
|
||||
super(LoadInstr, self).__init__(parent, typ, "load", [ptr], name=name)
|
||||
self.align = None
|
||||
|
||||
def descr(self, buf):
|
||||
[val] = self.operands
|
||||
if self.align is not None:
|
||||
align = ', align %d' % (self.align)
|
||||
else:
|
||||
align = ''
|
||||
buf.append("load {0}, {1} {2}{3}{4}\n".format(
|
||||
self.type,
|
||||
val.type,
|
||||
val.get_reference(),
|
||||
align,
|
||||
self._stringify_metadata(leading_comma=True),
|
||||
))
|
||||
|
||||
|
||||
class StoreInstr(Instruction):
|
||||
def __init__(self, parent, val, ptr):
|
||||
super(StoreInstr, self).__init__(parent, types.VoidType(), "store",
|
||||
[val, ptr])
|
||||
|
||||
def descr(self, buf):
|
||||
val, ptr = self.operands
|
||||
if self.align is not None:
|
||||
align = ', align %d' % (self.align)
|
||||
else:
|
||||
align = ''
|
||||
buf.append("store {0} {1}, {2} {3}{4}{5}\n".format(
|
||||
val.type,
|
||||
val.get_reference(),
|
||||
ptr.type,
|
||||
ptr.get_reference(),
|
||||
align,
|
||||
self._stringify_metadata(leading_comma=True),
|
||||
))
|
||||
|
||||
|
||||
class LoadAtomicInstr(Instruction):
|
||||
def __init__(self, parent, ptr, ordering, align, name='', typ=None):
|
||||
if typ is None:
|
||||
if isinstance(ptr, AllocaInstr):
|
||||
typ = ptr.allocated_type
|
||||
# For compatibility with typed pointers. Eventually this should
|
||||
# probably be removed (when typed pointers are fully removed).
|
||||
elif not ptr.type.is_opaque:
|
||||
typ = ptr.type.pointee
|
||||
else:
|
||||
raise ValueError("Load atomic lacks type.")
|
||||
super(LoadAtomicInstr, self).__init__(parent, typ, "load atomic",
|
||||
[ptr], name=name)
|
||||
self.ordering = ordering
|
||||
self.align = align
|
||||
|
||||
def descr(self, buf):
|
||||
[val] = self.operands
|
||||
buf.append("load atomic {0}, {1} {2} {3}, align {4}{5}\n".format(
|
||||
self.type,
|
||||
val.type,
|
||||
val.get_reference(),
|
||||
self.ordering,
|
||||
self.align,
|
||||
self._stringify_metadata(leading_comma=True),
|
||||
))
|
||||
|
||||
|
||||
class StoreAtomicInstr(Instruction):
|
||||
def __init__(self, parent, val, ptr, ordering, align):
|
||||
super(StoreAtomicInstr, self).__init__(parent, types.VoidType(),
|
||||
"store atomic", [val, ptr])
|
||||
self.ordering = ordering
|
||||
self.align = align
|
||||
|
||||
def descr(self, buf):
|
||||
val, ptr = self.operands
|
||||
buf.append("store atomic {0} {1}, {2} {3} {4}, align {5}{6}\n".format(
|
||||
val.type,
|
||||
val.get_reference(),
|
||||
ptr.type,
|
||||
ptr.get_reference(),
|
||||
self.ordering,
|
||||
self.align,
|
||||
self._stringify_metadata(leading_comma=True),
|
||||
))
|
||||
|
||||
|
||||
class AllocaInstr(Instruction):
|
||||
def __init__(self, parent, typ, count, name):
|
||||
operands = [count] if count else ()
|
||||
super(AllocaInstr, self).__init__(parent, typ.as_pointer(), "alloca",
|
||||
operands, name)
|
||||
self.allocated_type = typ
|
||||
self.align = None
|
||||
|
||||
def descr(self, buf):
|
||||
buf.append("{0} {1}".format(self.opname, self.allocated_type))
|
||||
if self.operands:
|
||||
op, = self.operands
|
||||
buf.append(", {0} {1}".format(op.type, op.get_reference()))
|
||||
if self.align is not None:
|
||||
buf.append(", align {0}".format(self.align))
|
||||
if self.metadata:
|
||||
buf.append(self._stringify_metadata(leading_comma=True))
|
||||
|
||||
|
||||
class GEPInstr(Instruction):
|
||||
def __init__(self, parent, ptr, indices, inbounds, name,
|
||||
source_etype=None):
|
||||
if source_etype is not None:
|
||||
typ = ptr.type
|
||||
self.source_etype = source_etype
|
||||
# For compatibility with typed pointers. Eventually this should
|
||||
# probably be removed (when typed pointers are fully removed).
|
||||
elif not ptr.type.is_opaque:
|
||||
typ = ptr.type
|
||||
lasttyp = None
|
||||
lastaddrspace = 0
|
||||
for i in indices:
|
||||
lasttyp, typ = typ, typ.gep(i)
|
||||
# inherit the addrspace from the last seen pointer
|
||||
if isinstance(lasttyp, types.PointerType):
|
||||
lastaddrspace = lasttyp.addrspace
|
||||
|
||||
if (not isinstance(typ, types.PointerType) and
|
||||
isinstance(lasttyp, types.PointerType)):
|
||||
typ = lasttyp
|
||||
else:
|
||||
typ = typ.as_pointer(lastaddrspace)
|
||||
self.source_etype = ptr.type.pointee
|
||||
else:
|
||||
raise ValueError("GEP lacks type.")
|
||||
super(GEPInstr, self).__init__(parent, typ, "getelementptr",
|
||||
[ptr] + list(indices), name=name)
|
||||
self.pointer = ptr
|
||||
self.indices = indices
|
||||
self.inbounds = inbounds
|
||||
|
||||
def descr(self, buf):
|
||||
indices = ['{0} {1}'.format(i.type, i.get_reference())
|
||||
for i in self.indices]
|
||||
op = "getelementptr inbounds" if self.inbounds else "getelementptr"
|
||||
buf.append("{0} {1}, {2} {3}, {4} {5}\n".format(
|
||||
op,
|
||||
self.source_etype,
|
||||
self.pointer.type,
|
||||
self.pointer.get_reference(),
|
||||
', '.join(indices),
|
||||
self._stringify_metadata(leading_comma=True),
|
||||
))
|
||||
|
||||
|
||||
class PhiInstr(Instruction):
|
||||
def __init__(self, parent, typ, name, flags=()):
|
||||
super(PhiInstr, self).__init__(parent, typ, "phi", (), name=name,
|
||||
flags=flags)
|
||||
self.incomings = []
|
||||
|
||||
def descr(self, buf):
|
||||
incs = ', '.join('[{0}, {1}]'.format(v.get_reference(),
|
||||
b.get_reference())
|
||||
for v, b in self.incomings)
|
||||
buf.append("phi {0} {1} {2} {3}\n".format(
|
||||
' '.join(self.flags),
|
||||
self.type,
|
||||
incs,
|
||||
self._stringify_metadata(leading_comma=True),
|
||||
))
|
||||
|
||||
def add_incoming(self, value, block):
|
||||
assert isinstance(block, Block)
|
||||
self.incomings.append((value, block))
|
||||
|
||||
def replace_usage(self, old, new):
|
||||
self.incomings = [((new if val is old else val), blk)
|
||||
for (val, blk) in self.incomings]
|
||||
|
||||
|
||||
class ExtractElement(Instruction):
|
||||
def __init__(self, parent, vector, index, name=''):
|
||||
if not isinstance(vector.type, types.VectorType):
|
||||
raise TypeError("vector needs to be of VectorType.")
|
||||
if not isinstance(index.type, types.IntType):
|
||||
raise TypeError("index needs to be of IntType.")
|
||||
typ = vector.type.element
|
||||
super(ExtractElement, self).__init__(parent, typ, "extractelement",
|
||||
[vector, index], name=name)
|
||||
|
||||
def descr(self, buf):
|
||||
operands = ", ".join("{0} {1}".format(
|
||||
op.type, op.get_reference()) for op in self.operands)
|
||||
buf.append("{opname} {operands}\n".format(
|
||||
opname=self.opname, operands=operands))
|
||||
|
||||
|
||||
class InsertElement(Instruction):
|
||||
def __init__(self, parent, vector, value, index, name=''):
|
||||
if not isinstance(vector.type, types.VectorType):
|
||||
raise TypeError("vector needs to be of VectorType.")
|
||||
if not value.type == vector.type.element:
|
||||
raise TypeError(
|
||||
"value needs to be of type {} not {}.".format(
|
||||
vector.type.element, value.type))
|
||||
if not isinstance(index.type, types.IntType):
|
||||
raise TypeError("index needs to be of IntType.")
|
||||
typ = vector.type
|
||||
super(InsertElement, self).__init__(parent, typ, "insertelement",
|
||||
[vector, value, index], name=name)
|
||||
|
||||
def descr(self, buf):
|
||||
operands = ", ".join("{0} {1}".format(
|
||||
op.type, op.get_reference()) for op in self.operands)
|
||||
buf.append("{opname} {operands}\n".format(
|
||||
opname=self.opname, operands=operands))
|
||||
|
||||
|
||||
class ShuffleVector(Instruction):
|
||||
def __init__(self, parent, vector1, vector2, mask, name=''):
|
||||
if not isinstance(vector1.type, types.VectorType):
|
||||
raise TypeError("vector1 needs to be of VectorType.")
|
||||
if vector2 != Undefined:
|
||||
if vector2.type != vector1.type:
|
||||
raise TypeError("vector2 needs to be " +
|
||||
"Undefined or of the same type as vector1.")
|
||||
if (not isinstance(mask, Constant) or
|
||||
not isinstance(mask.type, types.VectorType) or
|
||||
not (isinstance(mask.type.element, types.IntType) and
|
||||
mask.type.element.width == 32)):
|
||||
raise TypeError("mask needs to be a constant i32 vector.")
|
||||
typ = types.VectorType(vector1.type.element, mask.type.count)
|
||||
index_range = range(vector1.type.count
|
||||
if vector2 == Undefined
|
||||
else 2 * vector1.type.count)
|
||||
if not all(ii.constant in index_range for ii in mask.constant):
|
||||
raise IndexError(
|
||||
"mask values need to be in {0}".format(index_range),
|
||||
)
|
||||
super(ShuffleVector, self).__init__(parent, typ, "shufflevector",
|
||||
[vector1, vector2, mask], name=name)
|
||||
|
||||
def descr(self, buf):
|
||||
buf.append("shufflevector {0} {1}\n".format(
|
||||
", ".join("{0} {1}".format(op.type, op.get_reference())
|
||||
for op in self.operands),
|
||||
self._stringify_metadata(leading_comma=True),
|
||||
))
|
||||
|
||||
|
||||
class ExtractValue(Instruction):
|
||||
def __init__(self, parent, agg, indices, name=''):
|
||||
typ = agg.type
|
||||
try:
|
||||
for i in indices:
|
||||
typ = typ.elements[i]
|
||||
except (AttributeError, IndexError):
|
||||
raise TypeError("Can't index at %r in %s"
|
||||
% (list(indices), agg.type))
|
||||
|
||||
super(ExtractValue, self).__init__(parent, typ, "extractvalue",
|
||||
[agg], name=name)
|
||||
|
||||
self.aggregate = agg
|
||||
self.indices = indices
|
||||
|
||||
def descr(self, buf):
|
||||
indices = [str(i) for i in self.indices]
|
||||
|
||||
buf.append("extractvalue {0} {1}, {2} {3}\n".format(
|
||||
self.aggregate.type,
|
||||
self.aggregate.get_reference(),
|
||||
', '.join(indices),
|
||||
self._stringify_metadata(leading_comma=True),
|
||||
))
|
||||
|
||||
|
||||
class InsertValue(Instruction):
|
||||
def __init__(self, parent, agg, elem, indices, name=''):
|
||||
typ = agg.type
|
||||
try:
|
||||
for i in indices:
|
||||
typ = typ.elements[i]
|
||||
except (AttributeError, IndexError):
|
||||
raise TypeError("Can't index at %r in %s"
|
||||
% (list(indices), agg.type))
|
||||
if elem.type != typ:
|
||||
raise TypeError("Can only insert %s at %r in %s: got %s"
|
||||
% (typ, list(indices), agg.type, elem.type))
|
||||
super(InsertValue, self).__init__(parent, agg.type, "insertvalue",
|
||||
[agg, elem], name=name)
|
||||
|
||||
self.aggregate = agg
|
||||
self.value = elem
|
||||
self.indices = indices
|
||||
|
||||
def descr(self, buf):
|
||||
indices = [str(i) for i in self.indices]
|
||||
|
||||
buf.append("insertvalue {0} {1}, {2} {3}, {4} {5}\n".format(
|
||||
self.aggregate.type, self.aggregate.get_reference(),
|
||||
self.value.type, self.value.get_reference(),
|
||||
', '.join(indices),
|
||||
self._stringify_metadata(leading_comma=True),
|
||||
))
|
||||
|
||||
|
||||
class Unreachable(Instruction):
|
||||
def __init__(self, parent):
|
||||
super(Unreachable, self).__init__(parent, types.VoidType(),
|
||||
"unreachable", (), name='')
|
||||
|
||||
def descr(self, buf):
|
||||
buf += (self.opname, "\n")
|
||||
|
||||
|
||||
class InlineAsm(object):
|
||||
def __init__(self, ftype, asm, constraint, side_effect=False):
|
||||
self.type = ftype.return_type
|
||||
self.function_type = ftype
|
||||
self.asm = asm
|
||||
self.constraint = constraint
|
||||
self.side_effect = side_effect
|
||||
|
||||
def descr(self, buf):
|
||||
sideeffect = 'sideeffect' if self.side_effect else ''
|
||||
fmt = 'asm {sideeffect} "{asm}", "{constraint}"'
|
||||
buf.append(fmt.format(sideeffect=sideeffect, asm=self.asm,
|
||||
constraint=self.constraint))
|
||||
|
||||
def get_reference(self):
|
||||
buf = []
|
||||
self.descr(buf)
|
||||
return "".join(buf)
|
||||
|
||||
def __str__(self):
|
||||
return "{0} {1}".format(self.type, self.get_reference())
|
||||
|
||||
|
||||
class AtomicRMW(Instruction):
|
||||
def __init__(self, parent, op, ptr, val, ordering, name):
|
||||
super(AtomicRMW, self).__init__(parent, val.type, "atomicrmw",
|
||||
(ptr, val), name=name)
|
||||
self.operation = op
|
||||
self.ordering = ordering
|
||||
|
||||
def descr(self, buf):
|
||||
ptr, val = self.operands
|
||||
fmt = ("atomicrmw {op} {ptrty} {ptr}, {valty} {val} {ordering} "
|
||||
"{metadata}\n")
|
||||
buf.append(fmt.format(op=self.operation,
|
||||
ptrty=ptr.type,
|
||||
ptr=ptr.get_reference(),
|
||||
valty=val.type,
|
||||
val=val.get_reference(),
|
||||
ordering=self.ordering,
|
||||
metadata=self._stringify_metadata(
|
||||
leading_comma=True),
|
||||
))
|
||||
|
||||
|
||||
class CmpXchg(Instruction):
|
||||
"""This instruction has changed since llvm3.5. It is not compatible with
|
||||
older llvm versions.
|
||||
"""
|
||||
|
||||
def __init__(self, parent, ptr, cmp, val, ordering, failordering, name):
|
||||
outtype = types.LiteralStructType([val.type, types.IntType(1)])
|
||||
super(CmpXchg, self).__init__(parent, outtype, "cmpxchg",
|
||||
(ptr, cmp, val), name=name)
|
||||
self.ordering = ordering
|
||||
self.failordering = failordering
|
||||
|
||||
def descr(self, buf):
|
||||
ptr, cmpval, val = self.operands
|
||||
fmt = "cmpxchg {ptrty} {ptr}, {ty} {cmp}, {ty} {val} {ordering} " \
|
||||
"{failordering} {metadata}\n"
|
||||
buf.append(fmt.format(ptrty=ptr.type,
|
||||
ptr=ptr.get_reference(),
|
||||
ty=cmpval.type,
|
||||
cmp=cmpval.get_reference(),
|
||||
val=val.get_reference(),
|
||||
ordering=self.ordering,
|
||||
failordering=self.failordering,
|
||||
metadata=self._stringify_metadata(
|
||||
leading_comma=True),
|
||||
))
|
||||
|
||||
|
||||
class _LandingPadClause(object):
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
|
||||
def __str__(self):
|
||||
return "{kind} {type} {value}".format(
|
||||
kind=self.kind,
|
||||
type=self.value.type,
|
||||
value=self.value.get_reference())
|
||||
|
||||
|
||||
class CatchClause(_LandingPadClause):
|
||||
kind = 'catch'
|
||||
|
||||
|
||||
class FilterClause(_LandingPadClause):
|
||||
kind = 'filter'
|
||||
|
||||
def __init__(self, value):
|
||||
assert isinstance(value, Constant)
|
||||
assert isinstance(value.type, types.ArrayType)
|
||||
super(FilterClause, self).__init__(value)
|
||||
|
||||
|
||||
class LandingPadInstr(Instruction):
|
||||
def __init__(self, parent, typ, name='', cleanup=False):
|
||||
super(LandingPadInstr, self).__init__(parent, typ, "landingpad", [],
|
||||
name=name)
|
||||
self.cleanup = cleanup
|
||||
self.clauses = []
|
||||
|
||||
def add_clause(self, clause):
|
||||
assert isinstance(clause, _LandingPadClause)
|
||||
self.clauses.append(clause)
|
||||
|
||||
def descr(self, buf):
|
||||
fmt = "landingpad {type}{cleanup}{clauses}\n"
|
||||
buf.append(fmt.format(type=self.type,
|
||||
cleanup=' cleanup' if self.cleanup else '',
|
||||
clauses=''.join(["\n {0}".format(clause)
|
||||
for clause in self.clauses]),
|
||||
))
|
||||
|
||||
|
||||
class Fence(Instruction):
|
||||
"""
|
||||
The `fence` instruction.
|
||||
|
||||
As of LLVM 5.0.1:
|
||||
|
||||
fence [syncscope("<target-scope>")] <ordering> ; yields void
|
||||
"""
|
||||
|
||||
VALID_FENCE_ORDERINGS = {"acquire", "release", "acq_rel", "seq_cst"}
|
||||
|
||||
def __init__(self, parent, ordering, targetscope=None, name=''):
|
||||
super(Fence, self).__init__(parent, types.VoidType(), "fence", (),
|
||||
name=name)
|
||||
if ordering not in self.VALID_FENCE_ORDERINGS:
|
||||
msg = "Invalid fence ordering \"{0}\"! Should be one of {1}."
|
||||
raise ValueError(msg .format(ordering,
|
||||
", ".join(self.VALID_FENCE_ORDERINGS)))
|
||||
self.ordering = ordering
|
||||
self.targetscope = targetscope
|
||||
|
||||
def descr(self, buf):
|
||||
if self.targetscope is None:
|
||||
syncscope = ""
|
||||
else:
|
||||
syncscope = 'syncscope("{0}") '.format(self.targetscope)
|
||||
|
||||
fmt = "fence {syncscope}{ordering}\n"
|
||||
buf.append(fmt.format(syncscope=syncscope,
|
||||
ordering=self.ordering,
|
||||
))
|
||||
|
||||
|
||||
class Comment(Instruction):
|
||||
"""
|
||||
A line comment.
|
||||
"""
|
||||
|
||||
def __init__(self, parent, text):
|
||||
super(Comment, self).__init__(parent, types.VoidType(), ";", (),
|
||||
name='')
|
||||
assert "\n" not in text, "Comment cannot contain new line"
|
||||
self.text = text
|
||||
|
||||
def descr(self, buf):
|
||||
buf.append(f"; {self.text}")
|
||||
@@ -0,0 +1,256 @@
|
||||
import collections
|
||||
|
||||
from llvmlite.ir import context, values, types, _utils
|
||||
|
||||
|
||||
class Module(object):
|
||||
def __init__(self, name='', context=context.global_context):
|
||||
self.context = context
|
||||
self.name = name # name is for debugging/informational
|
||||
self.data_layout = ""
|
||||
self.scope = _utils.NameScope()
|
||||
self.triple = 'unknown-unknown-unknown'
|
||||
self.globals = collections.OrderedDict()
|
||||
# Innamed metadata nodes.
|
||||
self.metadata = []
|
||||
# Named metadata nodes
|
||||
self.namedmetadata = {}
|
||||
# Cache for metadata node deduplication
|
||||
self._metadatacache = {}
|
||||
|
||||
def _fix_metadata_operands(self, operands):
|
||||
fixed_ops = []
|
||||
for op in operands:
|
||||
if op is None:
|
||||
# A literal None creates a null metadata value
|
||||
op = types.MetaDataType()(None)
|
||||
elif isinstance(op, str):
|
||||
# A literal string creates a metadata string value
|
||||
op = values.MetaDataString(self, op)
|
||||
elif isinstance(op, (list, tuple)):
|
||||
# A sequence creates a metadata node reference
|
||||
op = self.add_metadata(op)
|
||||
fixed_ops.append(op)
|
||||
return fixed_ops
|
||||
|
||||
def _fix_di_operands(self, operands):
|
||||
fixed_ops = []
|
||||
for name, op in operands:
|
||||
if isinstance(op, (list, tuple)):
|
||||
# A sequence creates a metadata node reference
|
||||
op = self.add_metadata(op)
|
||||
fixed_ops.append((name, op))
|
||||
return fixed_ops
|
||||
|
||||
def str_ditok_operands(self, operands):
|
||||
str_ops = []
|
||||
for name, op in operands:
|
||||
if name == 'encoding' and isinstance(op, values.DIToken):
|
||||
# use string value instead of address of object
|
||||
op = op.value
|
||||
str_ops.append((name, op))
|
||||
return str_ops
|
||||
|
||||
def add_metadata(self, operands):
|
||||
"""
|
||||
Add an unnamed metadata to the module with the given *operands*
|
||||
(a sequence of values) or return a previous equivalent metadata.
|
||||
A MDValue instance is returned, it can then be associated to
|
||||
e.g. an instruction.
|
||||
"""
|
||||
if not isinstance(operands, (list, tuple)):
|
||||
raise TypeError("expected a list or tuple of metadata values, "
|
||||
"got %r" % (operands,))
|
||||
operands = self._fix_metadata_operands(operands)
|
||||
key = tuple(operands)
|
||||
if key not in self._metadatacache:
|
||||
n = len(self.metadata)
|
||||
md = values.MDValue(self, operands, name=str(n))
|
||||
self._metadatacache[key] = md
|
||||
else:
|
||||
md = self._metadatacache[key]
|
||||
return md
|
||||
|
||||
def add_debug_info(self, kind, operands, is_distinct=False):
|
||||
"""
|
||||
Add debug information metadata to the module with the given
|
||||
*operands* (a dict of values with string keys) or return
|
||||
a previous equivalent metadata. *kind* is a string of the
|
||||
debug information kind (e.g. "DICompileUnit").
|
||||
|
||||
A DIValue instance is returned, it can then be associated to e.g.
|
||||
an instruction.
|
||||
"""
|
||||
operands = tuple(sorted(self._fix_di_operands(operands.items())))
|
||||
str_op_key = tuple(sorted(self.str_ditok_operands(operands)))
|
||||
key = (kind, str_op_key, is_distinct)
|
||||
if key not in self._metadatacache:
|
||||
n = len(self.metadata)
|
||||
di = values.DIValue(self, is_distinct, kind, operands, name=str(n))
|
||||
self._metadatacache[key] = di
|
||||
else:
|
||||
di = self._metadatacache[key]
|
||||
return di
|
||||
|
||||
def add_named_metadata(self, name, element=None):
|
||||
"""
|
||||
Add a named metadata node to the module, if it doesn't exist,
|
||||
or return the existing node.
|
||||
If *element* is given, it will append a new element to
|
||||
the named metadata node. If *element* is a sequence of values
|
||||
(rather than a metadata value), a new unnamed node will first be
|
||||
created.
|
||||
|
||||
Example::
|
||||
module.add_named_metadata("llvm.ident", ["llvmlite/1.0"])
|
||||
"""
|
||||
if name in self.namedmetadata:
|
||||
nmd = self.namedmetadata[name]
|
||||
else:
|
||||
nmd = self.namedmetadata[name] = values.NamedMetaData(self)
|
||||
if element is not None:
|
||||
if not isinstance(element, values.Value):
|
||||
element = self.add_metadata(element)
|
||||
if not isinstance(element.type, types.MetaDataType):
|
||||
raise TypeError("wrong type for metadata element: got %r"
|
||||
% (element,))
|
||||
nmd.add(element)
|
||||
return nmd
|
||||
|
||||
def get_named_metadata(self, name):
|
||||
"""
|
||||
Return the metadata node with the given *name*. KeyError is raised
|
||||
if no such node exists (contrast with add_named_metadata()).
|
||||
"""
|
||||
return self.namedmetadata[name]
|
||||
|
||||
@property
|
||||
def functions(self):
|
||||
"""
|
||||
A list of functions declared or defined in this module.
|
||||
"""
|
||||
return [v for v in self.globals.values()
|
||||
if isinstance(v, values.Function)]
|
||||
|
||||
@property
|
||||
def global_values(self):
|
||||
"""
|
||||
An iterable of global values in this module.
|
||||
"""
|
||||
return self.globals.values()
|
||||
|
||||
def get_global(self, name):
|
||||
"""
|
||||
Get a global value by name.
|
||||
"""
|
||||
return self.globals[name]
|
||||
|
||||
def add_global(self, globalvalue):
|
||||
"""
|
||||
Add a new global value.
|
||||
"""
|
||||
assert globalvalue.name not in self.globals
|
||||
self.globals[globalvalue.name] = globalvalue
|
||||
|
||||
def get_unique_name(self, name=''):
|
||||
"""
|
||||
Get a unique global name with the following *name* hint.
|
||||
"""
|
||||
return self.scope.deduplicate(name)
|
||||
|
||||
def declare_intrinsic(self, intrinsic, tys=(), fnty=None):
|
||||
def _error():
|
||||
raise NotImplementedError("unknown intrinsic %r with %d types"
|
||||
% (intrinsic, len(tys)))
|
||||
|
||||
if intrinsic in {'llvm.cttz', 'llvm.ctlz', 'llvm.fma'}:
|
||||
suffixes = [tys[0].intrinsic_name]
|
||||
else:
|
||||
suffixes = [t.intrinsic_name for t in tys]
|
||||
name = '.'.join([intrinsic] + suffixes)
|
||||
if name in self.globals:
|
||||
return self.globals[name]
|
||||
|
||||
if fnty is not None:
|
||||
# General case: function type is given
|
||||
pass
|
||||
# Compute function type if omitted for common cases
|
||||
elif len(tys) == 0 and intrinsic == 'llvm.assume':
|
||||
fnty = types.FunctionType(types.VoidType(), [types.IntType(1)])
|
||||
elif len(tys) == 1:
|
||||
if intrinsic == 'llvm.powi':
|
||||
fnty = types.FunctionType(tys[0], [tys[0], types.IntType(32)])
|
||||
elif intrinsic == 'llvm.pow':
|
||||
fnty = types.FunctionType(tys[0], tys * 2)
|
||||
elif intrinsic == 'llvm.convert.from.fp16':
|
||||
fnty = types.FunctionType(tys[0], [types.IntType(16)])
|
||||
elif intrinsic == 'llvm.convert.to.fp16':
|
||||
fnty = types.FunctionType(types.IntType(16), tys)
|
||||
else:
|
||||
fnty = types.FunctionType(tys[0], tys)
|
||||
elif len(tys) == 2:
|
||||
if intrinsic == 'llvm.memset':
|
||||
tys = [tys[0], types.IntType(8), tys[1],
|
||||
types.IntType(1)]
|
||||
fnty = types.FunctionType(types.VoidType(), tys)
|
||||
elif intrinsic in {'llvm.cttz', 'llvm.ctlz'}:
|
||||
tys = [tys[0], types.IntType(1)]
|
||||
fnty = types.FunctionType(tys[0], tys)
|
||||
else:
|
||||
_error()
|
||||
elif len(tys) == 3:
|
||||
if intrinsic in ('llvm.memcpy', 'llvm.memmove'):
|
||||
tys = tys + [types.IntType(1)]
|
||||
fnty = types.FunctionType(types.VoidType(), tys)
|
||||
elif intrinsic == 'llvm.fma':
|
||||
tys = [tys[0]] * 3
|
||||
fnty = types.FunctionType(tys[0], tys)
|
||||
else:
|
||||
_error()
|
||||
else:
|
||||
_error()
|
||||
return values.Function(self, fnty, name=name)
|
||||
|
||||
def get_identified_types(self):
|
||||
return self.context.identified_types
|
||||
|
||||
def _get_body_lines(self):
|
||||
# Type declarations
|
||||
lines = [it.get_declaration()
|
||||
for it in self.get_identified_types().values()]
|
||||
# Global values (including function definitions)
|
||||
lines += [str(v) for v in self.globals.values()]
|
||||
return lines
|
||||
|
||||
def _get_metadata_lines(self):
|
||||
mdbuf = []
|
||||
for k, v in self.namedmetadata.items():
|
||||
mdbuf.append("!{name} = !{{ {operands} }}".format(
|
||||
name=k, operands=', '.join(i.get_reference()
|
||||
for i in v.operands)))
|
||||
for md in self.metadata:
|
||||
mdbuf.append(str(md))
|
||||
return mdbuf
|
||||
|
||||
def _stringify_body(self):
|
||||
# For testing
|
||||
return "\n".join(self._get_body_lines())
|
||||
|
||||
def _stringify_metadata(self):
|
||||
# For testing
|
||||
return "\n".join(self._get_metadata_lines())
|
||||
|
||||
def __repr__(self):
|
||||
lines = []
|
||||
# Header
|
||||
lines += [
|
||||
'; ModuleID = "%s"' % (self.name,),
|
||||
'target triple = "%s"' % (self.triple,),
|
||||
'target datalayout = "%s"' % (self.data_layout,),
|
||||
'']
|
||||
# Body
|
||||
lines += self._get_body_lines()
|
||||
# Metadata
|
||||
lines += self._get_metadata_lines()
|
||||
|
||||
return "\n".join(lines)
|
||||
@@ -0,0 +1,64 @@
|
||||
from llvmlite.ir import CallInstr
|
||||
|
||||
|
||||
class Visitor(object):
|
||||
def visit(self, module):
|
||||
self._module = module
|
||||
for func in module.functions:
|
||||
self.visit_Function(func)
|
||||
|
||||
def visit_Function(self, func):
|
||||
self._function = func
|
||||
for bb in func.blocks:
|
||||
self.visit_BasicBlock(bb)
|
||||
|
||||
def visit_BasicBlock(self, bb):
|
||||
self._basic_block = bb
|
||||
for instr in bb.instructions:
|
||||
self.visit_Instruction(instr)
|
||||
|
||||
def visit_Instruction(self, instr):
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def module(self):
|
||||
return self._module
|
||||
|
||||
@property
|
||||
def function(self):
|
||||
return self._function
|
||||
|
||||
@property
|
||||
def basic_block(self):
|
||||
return self._basic_block
|
||||
|
||||
|
||||
class CallVisitor(Visitor):
|
||||
def visit_Instruction(self, instr):
|
||||
if isinstance(instr, CallInstr):
|
||||
self.visit_Call(instr)
|
||||
|
||||
def visit_Call(self, instr):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class ReplaceCalls(CallVisitor):
|
||||
def __init__(self, orig, repl):
|
||||
super(ReplaceCalls, self).__init__()
|
||||
self.orig = orig
|
||||
self.repl = repl
|
||||
self.calls = []
|
||||
|
||||
def visit_Call(self, instr):
|
||||
if instr.callee == self.orig:
|
||||
instr.replace_callee(self.repl)
|
||||
self.calls.append(instr)
|
||||
|
||||
|
||||
def replace_all_calls(mod, orig, repl):
|
||||
"""Replace all calls to `orig` to `repl` in module `mod`.
|
||||
Returns the references to the returned calls
|
||||
"""
|
||||
rc = ReplaceCalls(orig, repl)
|
||||
rc.visit(mod)
|
||||
return rc.calls
|
||||
@@ -0,0 +1,732 @@
|
||||
"""
|
||||
Classes that are LLVM types
|
||||
"""
|
||||
|
||||
import struct
|
||||
|
||||
from llvmlite import ir_layer_typed_pointers_enabled
|
||||
from llvmlite.ir._utils import _StrCaching
|
||||
|
||||
|
||||
def _wrapname(x):
|
||||
return '"{0}"'.format(x.replace('\\', '\\5c').replace('"', '\\22'))
|
||||
|
||||
|
||||
class Type(_StrCaching):
|
||||
"""
|
||||
The base class for all LLVM types.
|
||||
"""
|
||||
is_pointer = False
|
||||
null = 'zeroinitializer'
|
||||
|
||||
def __repr__(self):
|
||||
return "<%s %s>" % (type(self), str(self))
|
||||
|
||||
def _to_string(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def as_pointer(self, addrspace=0):
|
||||
return PointerType(self, addrspace)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not (self == other)
|
||||
|
||||
def _get_ll_global_value_type(self, target_data, context=None):
|
||||
"""
|
||||
Convert this type object to an LLVM type.
|
||||
"""
|
||||
from llvmlite.ir import Module, GlobalVariable
|
||||
from llvmlite.binding import parse_assembly
|
||||
|
||||
if context is None:
|
||||
m = Module()
|
||||
else:
|
||||
m = Module(context=context)
|
||||
foo = GlobalVariable(m, self, name="foo")
|
||||
with parse_assembly(str(m)) as llmod:
|
||||
return llmod.get_global_variable(foo.name).global_value_type
|
||||
|
||||
def get_abi_size(self, target_data, context=None):
|
||||
"""
|
||||
Get the ABI size of this type according to data layout *target_data*.
|
||||
"""
|
||||
llty = self._get_ll_global_value_type(target_data, context)
|
||||
return target_data.get_abi_size(llty)
|
||||
|
||||
def get_element_offset(self, target_data, ndx, context=None):
|
||||
llty = self._get_ll_global_value_type(target_data, context)
|
||||
return target_data.get_element_offset(llty, ndx)
|
||||
|
||||
def get_abi_alignment(self, target_data, context=None):
|
||||
"""
|
||||
Get the minimum ABI alignment of this type according to data layout
|
||||
*target_data*.
|
||||
"""
|
||||
llty = self._get_ll_global_value_type(target_data, context)
|
||||
return target_data.get_abi_alignment(llty)
|
||||
|
||||
def format_constant(self, value):
|
||||
"""
|
||||
Format constant *value* of this type. This method may be overriden
|
||||
by subclasses.
|
||||
"""
|
||||
return str(value)
|
||||
|
||||
def wrap_constant_value(self, value):
|
||||
"""
|
||||
Wrap constant *value* if necessary. This method may be overriden
|
||||
by subclasses (especially aggregate types).
|
||||
"""
|
||||
return value
|
||||
|
||||
def __call__(self, value):
|
||||
"""
|
||||
Create a LLVM constant of this type with the given Python value.
|
||||
"""
|
||||
from llvmlite.ir import Constant
|
||||
return Constant(self, value)
|
||||
|
||||
|
||||
class MetaDataType(Type):
|
||||
|
||||
def _to_string(self):
|
||||
return "metadata"
|
||||
|
||||
def as_pointer(self):
|
||||
raise TypeError
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, MetaDataType)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(MetaDataType)
|
||||
|
||||
|
||||
class LabelType(Type):
|
||||
"""
|
||||
The label type is the type of e.g. basic blocks.
|
||||
"""
|
||||
|
||||
def _to_string(self):
|
||||
return "label"
|
||||
|
||||
|
||||
class PointerType(Type):
|
||||
"""
|
||||
The type of all pointer values.
|
||||
By default (without specialisation) represents an opaque pointer.
|
||||
"""
|
||||
is_opaque = True
|
||||
is_pointer = True
|
||||
null = 'null'
|
||||
|
||||
# Factory to create typed or opaque pointers based on `pointee'.
|
||||
def __new__(cls, pointee=None, addrspace=0):
|
||||
if cls is PointerType and pointee is not None and \
|
||||
type(pointee) is not PointerType:
|
||||
return super().__new__(_TypedPointerType)
|
||||
return super(PointerType, cls).__new__(cls)
|
||||
|
||||
def __init__(self, pointee=None, addrspace=0):
|
||||
assert pointee is None or type(pointee) is PointerType
|
||||
self.addrspace = addrspace
|
||||
|
||||
def _to_string(self):
|
||||
if self.addrspace != 0:
|
||||
return "ptr addrspace({0})".format(self.addrspace)
|
||||
else:
|
||||
return "ptr"
|
||||
|
||||
def __eq__(self, other):
|
||||
return (isinstance(other, PointerType) and
|
||||
self.addrspace == other.addrspace)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(PointerType)
|
||||
|
||||
@property
|
||||
def intrinsic_name(self):
|
||||
return 'p%d' % self.addrspace
|
||||
|
||||
@classmethod
|
||||
def from_llvm(cls, typeref, ir_ctx):
|
||||
"""
|
||||
Create from a llvmlite.binding.TypeRef
|
||||
"""
|
||||
return cls()
|
||||
|
||||
|
||||
class _TypedPointerType(PointerType):
|
||||
"""
|
||||
The type of typed pointer values. To be removed eventually.
|
||||
"""
|
||||
|
||||
def __init__(self, pointee, addrspace=0):
|
||||
super(_TypedPointerType, self).__init__(None, addrspace)
|
||||
assert pointee is not None and type(pointee) is not PointerType
|
||||
assert not isinstance(pointee, VoidType)
|
||||
self.pointee = pointee
|
||||
self.is_opaque = False
|
||||
|
||||
def _to_string(self):
|
||||
if ir_layer_typed_pointers_enabled:
|
||||
return "{0}*".format(self.pointee) if self.addrspace == 0 else \
|
||||
"{0} addrspace({1})*".format(self.pointee, self.addrspace)
|
||||
return super(_TypedPointerType, self)._to_string()
|
||||
|
||||
# This implements ``isOpaqueOrPointeeTypeEquals''.
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, _TypedPointerType):
|
||||
return (self.pointee, self.addrspace) == (other.pointee,
|
||||
other.addrspace)
|
||||
return (isinstance(other, PointerType) and
|
||||
self.addrspace == other.addrspace)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(_TypedPointerType)
|
||||
|
||||
def gep(self, i):
|
||||
"""
|
||||
Resolve the type of the i-th element (for getelementptr lookups).
|
||||
"""
|
||||
if not isinstance(i.type, IntType):
|
||||
raise TypeError(i.type)
|
||||
return self.pointee
|
||||
|
||||
@property
|
||||
def intrinsic_name(self):
|
||||
if ir_layer_typed_pointers_enabled:
|
||||
return 'p%d%s' % (self.addrspace, self.pointee.intrinsic_name)
|
||||
return super(_TypedPointerType, self).intrinsic_name
|
||||
|
||||
|
||||
class VoidType(Type):
|
||||
"""
|
||||
The type for empty values (e.g. a function returning no value).
|
||||
"""
|
||||
|
||||
def _to_string(self):
|
||||
return 'void'
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, VoidType)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(VoidType)
|
||||
|
||||
@classmethod
|
||||
def from_llvm(cls, typeref, ir_ctx):
|
||||
"""
|
||||
Create from a llvmlite.binding.TypeRef
|
||||
"""
|
||||
return cls()
|
||||
|
||||
|
||||
class FunctionType(Type):
|
||||
"""
|
||||
The type for functions.
|
||||
"""
|
||||
|
||||
def __init__(self, return_type, args, var_arg=False):
|
||||
self.return_type = return_type
|
||||
self.args = tuple(args)
|
||||
self.var_arg = var_arg
|
||||
|
||||
def _to_string(self):
|
||||
if self.args:
|
||||
strargs = ', '.join([str(a) for a in self.args])
|
||||
if self.var_arg:
|
||||
return '{0} ({1}, ...)'.format(self.return_type, strargs)
|
||||
else:
|
||||
return '{0} ({1})'.format(self.return_type, strargs)
|
||||
elif self.var_arg:
|
||||
return '{0} (...)'.format(self.return_type)
|
||||
else:
|
||||
return '{0} ()'.format(self.return_type)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, FunctionType):
|
||||
return (self.return_type == other.return_type and
|
||||
self.args == other.args and self.var_arg == other.var_arg)
|
||||
else:
|
||||
return False
|
||||
|
||||
def __hash__(self):
|
||||
return hash(FunctionType)
|
||||
|
||||
@classmethod
|
||||
def from_llvm(cls, typeref, ir_ctx):
|
||||
"""
|
||||
Create from a llvmlite.binding.TypeRef
|
||||
"""
|
||||
params = tuple(x.as_ir(ir_ctx=ir_ctx)
|
||||
for x in typeref.get_function_parameters())
|
||||
ret = typeref.get_function_return().as_ir(ir_ctx=ir_ctx)
|
||||
is_vararg = typeref.is_function_vararg
|
||||
return cls(ret, params, is_vararg)
|
||||
|
||||
|
||||
class IntType(Type):
|
||||
"""
|
||||
The type for integers.
|
||||
"""
|
||||
null = '0'
|
||||
_instance_cache = {}
|
||||
width: int
|
||||
|
||||
def __new__(cls, bits):
|
||||
# Cache all common integer types
|
||||
if 0 <= bits <= 128:
|
||||
try:
|
||||
return cls._instance_cache[bits]
|
||||
except KeyError:
|
||||
inst = cls._instance_cache[bits] = cls.__new(bits)
|
||||
return inst
|
||||
return cls.__new(bits)
|
||||
|
||||
@classmethod
|
||||
def __new(cls, bits):
|
||||
assert isinstance(bits, int) and bits >= 0
|
||||
self = super(IntType, cls).__new__(cls)
|
||||
self.width = bits
|
||||
return self
|
||||
|
||||
def __getnewargs__(self):
|
||||
return self.width,
|
||||
|
||||
def __copy__(self):
|
||||
return self
|
||||
|
||||
def _to_string(self):
|
||||
return 'i%u' % (self.width,)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, IntType):
|
||||
return self.width == other.width
|
||||
else:
|
||||
return False
|
||||
|
||||
def __hash__(self):
|
||||
return hash(IntType)
|
||||
|
||||
def format_constant(self, val):
|
||||
if isinstance(val, bool):
|
||||
return str(val).lower()
|
||||
else:
|
||||
return str(val)
|
||||
|
||||
def wrap_constant_value(self, val):
|
||||
if val is None:
|
||||
return 0
|
||||
return val
|
||||
|
||||
@property
|
||||
def intrinsic_name(self):
|
||||
return str(self)
|
||||
|
||||
@classmethod
|
||||
def from_llvm(cls, typeref, ir_ctx):
|
||||
"""
|
||||
Create from a llvmlite.binding.TypeRef
|
||||
"""
|
||||
return IntType(typeref.type_width)
|
||||
|
||||
|
||||
def _as_float(value):
|
||||
"""
|
||||
Truncate to single-precision float.
|
||||
"""
|
||||
return struct.unpack('f', struct.pack('f', value))[0]
|
||||
|
||||
|
||||
def _as_half(value):
|
||||
"""
|
||||
Truncate to half-precision float.
|
||||
"""
|
||||
try:
|
||||
return struct.unpack('e', struct.pack('e', value))[0]
|
||||
except struct.error:
|
||||
# 'e' only added in Python 3.6+
|
||||
return _as_float(value)
|
||||
|
||||
|
||||
def _format_float_as_hex(value, packfmt, unpackfmt, numdigits):
|
||||
raw = struct.pack(packfmt, float(value))
|
||||
intrep = struct.unpack(unpackfmt, raw)[0]
|
||||
out = '{{0:#{0}x}}'.format(numdigits).format(intrep)
|
||||
return out
|
||||
|
||||
|
||||
def _format_double(value):
|
||||
"""
|
||||
Format *value* as a hexadecimal string of its IEEE double precision
|
||||
representation.
|
||||
"""
|
||||
return _format_float_as_hex(value, 'd', 'Q', 16)
|
||||
|
||||
|
||||
class _BaseFloatType(Type):
|
||||
|
||||
def __new__(cls):
|
||||
return cls._instance_cache
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, type(self))
|
||||
|
||||
def __hash__(self):
|
||||
return hash(type(self))
|
||||
|
||||
@classmethod
|
||||
def _create_instance(cls):
|
||||
cls._instance_cache = super(_BaseFloatType, cls).__new__(cls)
|
||||
|
||||
@classmethod
|
||||
def from_llvm(cls, typeref, ir_ctx):
|
||||
"""
|
||||
Create from a llvmlite.binding.TypeRef
|
||||
"""
|
||||
return cls()
|
||||
|
||||
|
||||
class HalfType(_BaseFloatType):
|
||||
"""
|
||||
The type for single-precision floats.
|
||||
"""
|
||||
null = '0.0'
|
||||
intrinsic_name = 'f16'
|
||||
|
||||
def __str__(self):
|
||||
return 'half'
|
||||
|
||||
def format_constant(self, value):
|
||||
return _format_double(_as_half(value))
|
||||
|
||||
|
||||
class FloatType(_BaseFloatType):
|
||||
"""
|
||||
The type for single-precision floats.
|
||||
"""
|
||||
null = '0.0'
|
||||
intrinsic_name = 'f32'
|
||||
|
||||
def __str__(self):
|
||||
return 'float'
|
||||
|
||||
def format_constant(self, value):
|
||||
return _format_double(_as_float(value))
|
||||
|
||||
|
||||
class DoubleType(_BaseFloatType):
|
||||
"""
|
||||
The type for double-precision floats.
|
||||
"""
|
||||
null = '0.0'
|
||||
intrinsic_name = 'f64'
|
||||
|
||||
def __str__(self):
|
||||
return 'double'
|
||||
|
||||
def format_constant(self, value):
|
||||
return _format_double(value)
|
||||
|
||||
|
||||
for _cls in (HalfType, FloatType, DoubleType):
|
||||
_cls._create_instance()
|
||||
|
||||
|
||||
class _Repeat(object):
|
||||
def __init__(self, value, size):
|
||||
self.value = value
|
||||
self.size = size
|
||||
|
||||
def __len__(self):
|
||||
return self.size
|
||||
|
||||
def __getitem__(self, item):
|
||||
if 0 <= item < self.size:
|
||||
return self.value
|
||||
else:
|
||||
raise IndexError(item)
|
||||
|
||||
|
||||
class VectorType(Type):
|
||||
"""
|
||||
The type for vectors of primitive data items (e.g. "<f32 x 4>").
|
||||
"""
|
||||
|
||||
def __init__(self, element, count):
|
||||
self.element = element
|
||||
self.count = count
|
||||
|
||||
@property
|
||||
def elements(self):
|
||||
return _Repeat(self.element, self.count)
|
||||
|
||||
def __len__(self):
|
||||
return self.count
|
||||
|
||||
def _to_string(self):
|
||||
return "<%d x %s>" % (self.count, self.element)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, VectorType):
|
||||
return self.element == other.element and self.count == other.count
|
||||
|
||||
def __hash__(self):
|
||||
# TODO: why does this not take self.element/self.count into account?
|
||||
return hash(VectorType)
|
||||
|
||||
def __copy__(self):
|
||||
return self
|
||||
|
||||
def format_constant(self, value):
|
||||
itemstring = ", " .join(["{0} {1}".format(x.type, x.get_reference())
|
||||
for x in value])
|
||||
return "<{0}>".format(itemstring)
|
||||
|
||||
def wrap_constant_value(self, values):
|
||||
from . import Value, Constant
|
||||
if not isinstance(values, (list, tuple)):
|
||||
if isinstance(values, Constant):
|
||||
if values.type != self.element:
|
||||
raise TypeError("expected {} for {}".format(
|
||||
self.element, values.type))
|
||||
return (values, ) * self.count
|
||||
return (Constant(self.element, values), ) * self.count
|
||||
if len(values) != len(self):
|
||||
raise ValueError("wrong constant size for %s: got %d elements"
|
||||
% (self, len(values)))
|
||||
return [Constant(ty, val) if not isinstance(val, Value) else val
|
||||
for ty, val in zip(self.elements, values)]
|
||||
|
||||
@classmethod
|
||||
def from_llvm(cls, typeref, ir_ctx):
|
||||
"""
|
||||
Create from a llvmlite.binding.TypeRef
|
||||
"""
|
||||
[elemtyperef] = typeref.elements
|
||||
elemty = elemtyperef.as_ir(ir_ctx=ir_ctx)
|
||||
count = typeref.element_count
|
||||
return cls(elemty, count)
|
||||
|
||||
|
||||
class Aggregate(Type):
|
||||
"""
|
||||
Base class for aggregate types.
|
||||
See http://llvm.org/docs/LangRef.html#t-aggregate
|
||||
"""
|
||||
|
||||
def wrap_constant_value(self, values):
|
||||
from . import Value, Constant
|
||||
|
||||
if not isinstance(values, (list, tuple)):
|
||||
return values
|
||||
if len(values) != len(self):
|
||||
raise ValueError("wrong constant size for %s: got %d elements"
|
||||
% (self, len(values)))
|
||||
return [Constant(ty, val) if not isinstance(val, Value) else val
|
||||
for ty, val in zip(self.elements, values)]
|
||||
|
||||
|
||||
class ArrayType(Aggregate):
|
||||
"""
|
||||
The type for fixed-size homogenous arrays (e.g. "[f32 x 3]").
|
||||
"""
|
||||
|
||||
def __init__(self, element, count):
|
||||
self.element = element
|
||||
self.count = count
|
||||
|
||||
@property
|
||||
def elements(self):
|
||||
return _Repeat(self.element, self.count)
|
||||
|
||||
def __len__(self):
|
||||
return self.count
|
||||
|
||||
def _to_string(self):
|
||||
return "[%d x %s]" % (self.count, self.element)
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, ArrayType):
|
||||
return self.element == other.element and self.count == other.count
|
||||
|
||||
def __hash__(self):
|
||||
return hash(ArrayType)
|
||||
|
||||
def gep(self, i):
|
||||
"""
|
||||
Resolve the type of the i-th element (for getelementptr lookups).
|
||||
"""
|
||||
if not isinstance(i.type, IntType):
|
||||
raise TypeError(i.type)
|
||||
return self.element
|
||||
|
||||
def format_constant(self, value):
|
||||
itemstring = ", " .join(["{0} {1}".format(x.type, x.get_reference())
|
||||
for x in value])
|
||||
return "[{0}]".format(itemstring)
|
||||
|
||||
@classmethod
|
||||
def from_llvm(cls, typeref, ir_ctx):
|
||||
"""
|
||||
Create from a llvmlite.binding.TypeRef
|
||||
"""
|
||||
[elemtyperef] = typeref.elements
|
||||
elemty = elemtyperef.as_ir(ir_ctx=ir_ctx)
|
||||
count = typeref.element_count
|
||||
return cls(elemty, count)
|
||||
|
||||
|
||||
class BaseStructType(Aggregate):
|
||||
"""
|
||||
The base type for heterogenous struct types.
|
||||
"""
|
||||
_packed = False
|
||||
|
||||
@property
|
||||
def packed(self):
|
||||
"""
|
||||
A boolean attribute that indicates whether the structure uses
|
||||
packed layout.
|
||||
"""
|
||||
return self._packed
|
||||
|
||||
@packed.setter
|
||||
def packed(self, val):
|
||||
self._packed = bool(val)
|
||||
|
||||
def __len__(self):
|
||||
assert self.elements is not None
|
||||
return len(self.elements)
|
||||
|
||||
def __iter__(self):
|
||||
assert self.elements is not None
|
||||
return iter(self.elements)
|
||||
|
||||
@property
|
||||
def is_opaque(self):
|
||||
return self.elements is None
|
||||
|
||||
def structure_repr(self):
|
||||
"""
|
||||
Return the LLVM IR for the structure representation
|
||||
"""
|
||||
ret = '{%s}' % ', '.join([str(x) for x in self.elements])
|
||||
return self._wrap_packed(ret)
|
||||
|
||||
def format_constant(self, value):
|
||||
itemstring = ", " .join(["{0} {1}".format(x.type, x.get_reference())
|
||||
for x in value])
|
||||
ret = "{{{0}}}".format(itemstring)
|
||||
return self._wrap_packed(ret)
|
||||
|
||||
def gep(self, i):
|
||||
"""
|
||||
Resolve the type of the i-th element (for getelementptr lookups).
|
||||
|
||||
*i* needs to be a LLVM constant, so that the type can be determined
|
||||
at compile-time.
|
||||
"""
|
||||
if not isinstance(i.type, IntType):
|
||||
raise TypeError(i.type)
|
||||
return self.elements[i.constant]
|
||||
|
||||
def _wrap_packed(self, textrepr):
|
||||
"""
|
||||
Internal helper to wrap textual repr of struct type into packed struct
|
||||
"""
|
||||
if self.packed:
|
||||
return '<{}>'.format(textrepr)
|
||||
else:
|
||||
return textrepr
|
||||
|
||||
@classmethod
|
||||
def from_llvm(cls, typeref, ir_ctx):
|
||||
"""
|
||||
Create from a llvmlite.binding.TypeRef
|
||||
"""
|
||||
if typeref.is_literal_struct:
|
||||
elems = [el.as_ir(ir_ctx=ir_ctx) for el in typeref.elements]
|
||||
return cls(elems, typeref.is_packed_struct)
|
||||
else:
|
||||
return ir_ctx.get_identified_type(typeref.name)
|
||||
|
||||
|
||||
class LiteralStructType(BaseStructType):
|
||||
"""
|
||||
The type of "literal" structs, i.e. structs with a literally-defined
|
||||
type (by contrast with IdentifiedStructType).
|
||||
"""
|
||||
|
||||
null = 'zeroinitializer'
|
||||
|
||||
def __init__(self, elems, packed=False):
|
||||
"""
|
||||
*elems* is a sequence of types to be used as members.
|
||||
*packed* controls the use of packed layout.
|
||||
"""
|
||||
self.elements = tuple(elems)
|
||||
self.packed = packed
|
||||
|
||||
def _to_string(self):
|
||||
return self.structure_repr()
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, LiteralStructType):
|
||||
return (self.elements == other.elements
|
||||
and self.packed == other.packed)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(LiteralStructType)
|
||||
|
||||
|
||||
class IdentifiedStructType(BaseStructType):
|
||||
"""
|
||||
A type which is a named alias for another struct type, akin to a typedef.
|
||||
While literal struct types can be structurally equal (see
|
||||
LiteralStructType), identified struct types are compared by name.
|
||||
|
||||
Do not use this directly.
|
||||
"""
|
||||
null = 'zeroinitializer'
|
||||
|
||||
def __init__(self, context, name, packed=False):
|
||||
"""
|
||||
*context* is a llvmlite.ir.Context.
|
||||
*name* is the identifier for the new struct type.
|
||||
*packed* controls the use of packed layout.
|
||||
"""
|
||||
assert name
|
||||
self.context = context
|
||||
self.name = name
|
||||
self.elements = None
|
||||
self.packed = packed
|
||||
|
||||
def _to_string(self):
|
||||
return "%{name}".format(name=_wrapname(self.name))
|
||||
|
||||
def get_declaration(self):
|
||||
"""
|
||||
Returns the string for the declaration of the type
|
||||
"""
|
||||
if self.is_opaque:
|
||||
out = "{strrep} = type opaque".format(strrep=str(self))
|
||||
else:
|
||||
out = "{strrep} = type {struct}".format(
|
||||
strrep=str(self), struct=self.structure_repr())
|
||||
return out
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, IdentifiedStructType):
|
||||
return (self.name == other.name
|
||||
and self.packed == other.packed)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(IdentifiedStructType)
|
||||
|
||||
def set_body(self, *elems):
|
||||
if not self.is_opaque:
|
||||
raise RuntimeError("{name} is already defined".format(
|
||||
name=self.name))
|
||||
self.elements = tuple(elems)
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user