This commit is contained in:
2026-04-10 15:06:59 +02:00
parent 3031b7153b
commit e5a4711004
7806 changed files with 1918528 additions and 335 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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()

View File

@@ -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}")

View File

@@ -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)

View File

@@ -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

View File

@@ -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