|
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 |
|
self.data_layout = "" |
|
self.scope = _utils.NameScope() |
|
self.triple = 'unknown-unknown-unknown' |
|
self.globals = collections.OrderedDict() |
|
|
|
self.metadata = [] |
|
|
|
self.namedmetadata = {} |
|
|
|
self._metadatacache = {} |
|
|
|
def _fix_metadata_operands(self, operands): |
|
fixed_ops = [] |
|
for op in operands: |
|
if op is None: |
|
|
|
op = types.MetaDataType()(None) |
|
elif isinstance(op, str): |
|
|
|
op = values.MetaDataString(self, op) |
|
elif isinstance(op, (list, tuple)): |
|
|
|
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)): |
|
|
|
op = self.add_metadata(op) |
|
fixed_ops.append((name, op)) |
|
return fixed_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()))) |
|
key = (kind, operands, 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: |
|
|
|
pass |
|
|
|
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): |
|
|
|
lines = [it.get_declaration() |
|
for it in self.get_identified_types().values()] |
|
|
|
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): |
|
|
|
return "\n".join(self._get_body_lines()) |
|
|
|
def _stringify_metadata(self): |
|
|
|
return "\n".join(self._get_metadata_lines()) |
|
|
|
def __repr__(self): |
|
lines = [] |
|
|
|
lines += [ |
|
'; ModuleID = "%s"' % (self.name,), |
|
'target triple = "%s"' % (self.triple,), |
|
'target datalayout = "%s"' % (self.data_layout,), |
|
''] |
|
|
|
lines += self._get_body_lines() |
|
|
|
lines += self._get_metadata_lines() |
|
|
|
return "\n".join(lines) |
|
|