Spaces:
Running
Running
| """ | |
| Classes that are LLVM values: Value, Constant... | |
| Instructions are in the instructions module. | |
| """ | |
| import functools | |
| import string | |
| import re | |
| from types import MappingProxyType | |
| from llvmlite.ir import values, types, _utils | |
| from llvmlite.ir._utils import (_StrCaching, _StringReferenceCaching, | |
| _HasMetadata) | |
| _VALID_CHARS = (frozenset(map(ord, string.ascii_letters)) | | |
| frozenset(map(ord, string.digits)) | | |
| frozenset(map(ord, ' !#$%&\'()*+,-./:;<=>?@[]^_`{|}~'))) | |
| _SIMPLE_IDENTIFIER_RE = re.compile(r"[-a-zA-Z$._][-a-zA-Z$._0-9]*$") | |
| _CMP_MAP = { | |
| '>': 'gt', | |
| '<': 'lt', | |
| '==': 'eq', | |
| '!=': 'ne', | |
| '>=': 'ge', | |
| '<=': 'le', | |
| } | |
| def _escape_string(text, _map={}): | |
| """ | |
| Escape the given bytestring for safe use as a LLVM array constant. | |
| Any unicode string input is first encoded with utf8 into bytes. | |
| """ | |
| if isinstance(text, str): | |
| text = text.encode() | |
| assert isinstance(text, (bytes, bytearray)) | |
| if not _map: | |
| for ch in range(256): | |
| if ch in _VALID_CHARS: | |
| _map[ch] = chr(ch) | |
| else: | |
| _map[ch] = '\\%02x' % ch | |
| buf = [_map[ch] for ch in text] | |
| return ''.join(buf) | |
| def _binop(opname): | |
| def wrap(fn): | |
| def wrapped(lhs, rhs): | |
| if lhs.type != rhs.type: | |
| raise ValueError("Operands must be the same type, got (%s, %s)" | |
| % (lhs.type, rhs.type)) | |
| fmt = "{0} ({1} {2}, {3} {4})".format(opname, | |
| lhs.type, lhs.get_reference(), | |
| rhs.type, rhs.get_reference()) | |
| return FormattedConstant(lhs.type, fmt) | |
| return wrapped | |
| return wrap | |
| def _castop(opname): | |
| def wrap(fn): | |
| def wrapped(self, typ): | |
| fn(self, typ) | |
| if typ == self.type: | |
| return self | |
| op = "{0} ({1} {2} to {3})".format(opname, self.type, | |
| self.get_reference(), typ) | |
| return FormattedConstant(typ, op) | |
| return wrapped | |
| return wrap | |
| class _ConstOpMixin(object): | |
| """ | |
| A mixin defining constant operations, for use in constant-like classes. | |
| """ | |
| # | |
| # Arithmetic APIs | |
| # | |
| def shl(self, other): | |
| """ | |
| Left integer shift: | |
| lhs << rhs | |
| """ | |
| def lshr(self, other): | |
| """ | |
| Logical (unsigned) right integer shift: | |
| lhs >> rhs | |
| """ | |
| def ashr(self, other): | |
| """ | |
| Arithmetic (signed) right integer shift: | |
| lhs >> rhs | |
| """ | |
| def add(self, other): | |
| """ | |
| Integer addition: | |
| lhs + rhs | |
| """ | |
| def fadd(self, other): | |
| """ | |
| Floating-point addition: | |
| lhs + rhs | |
| """ | |
| def sub(self, other): | |
| """ | |
| Integer subtraction: | |
| lhs - rhs | |
| """ | |
| def fsub(self, other): | |
| """ | |
| Floating-point subtraction: | |
| lhs - rhs | |
| """ | |
| def mul(self, other): | |
| """ | |
| Integer multiplication: | |
| lhs * rhs | |
| """ | |
| def fmul(self, other): | |
| """ | |
| Floating-point multiplication: | |
| lhs * rhs | |
| """ | |
| def udiv(self, other): | |
| """ | |
| Unsigned integer division: | |
| lhs / rhs | |
| """ | |
| def sdiv(self, other): | |
| """ | |
| Signed integer division: | |
| lhs / rhs | |
| """ | |
| def fdiv(self, other): | |
| """ | |
| Floating-point division: | |
| lhs / rhs | |
| """ | |
| def urem(self, other): | |
| """ | |
| Unsigned integer remainder: | |
| lhs % rhs | |
| """ | |
| def srem(self, other): | |
| """ | |
| Signed integer remainder: | |
| lhs % rhs | |
| """ | |
| def frem(self, other): | |
| """ | |
| Floating-point remainder: | |
| lhs % rhs | |
| """ | |
| def or_(self, other): | |
| """ | |
| Bitwise integer OR: | |
| lhs | rhs | |
| """ | |
| def and_(self, other): | |
| """ | |
| Bitwise integer AND: | |
| lhs & rhs | |
| """ | |
| def xor(self, other): | |
| """ | |
| Bitwise integer XOR: | |
| lhs ^ rhs | |
| """ | |
| def _cmp(self, prefix, sign, cmpop, other): | |
| ins = prefix + 'cmp' | |
| try: | |
| op = _CMP_MAP[cmpop] | |
| except KeyError: | |
| raise ValueError("invalid comparison %r for %s" % (cmpop, ins)) | |
| if not (prefix == 'i' and cmpop in ('==', '!=')): | |
| op = sign + op | |
| if self.type != other.type: | |
| raise ValueError("Operands must be the same type, got (%s, %s)" | |
| % (self.type, other.type)) | |
| fmt = "{0} {1} ({2} {3}, {4} {5})".format( | |
| ins, op, | |
| self.type, self.get_reference(), | |
| other.type, other.get_reference()) | |
| return FormattedConstant(types.IntType(1), fmt) | |
| def icmp_signed(self, cmpop, other): | |
| """ | |
| Signed integer comparison: | |
| lhs <cmpop> rhs | |
| where cmpop can be '==', '!=', '<', '<=', '>', '>=' | |
| """ | |
| return self._cmp('i', 's', cmpop, other) | |
| def icmp_unsigned(self, cmpop, other): | |
| """ | |
| Unsigned integer (or pointer) comparison: | |
| lhs <cmpop> rhs | |
| where cmpop can be '==', '!=', '<', '<=', '>', '>=' | |
| """ | |
| return self._cmp('i', 'u', cmpop, other) | |
| def fcmp_ordered(self, cmpop, other): | |
| """ | |
| Floating-point ordered comparison: | |
| lhs <cmpop> rhs | |
| where cmpop can be '==', '!=', '<', '<=', '>', '>=', 'ord', 'uno' | |
| """ | |
| return self._cmp('f', 'o', cmpop, other) | |
| def fcmp_unordered(self, cmpop, other): | |
| """ | |
| Floating-point unordered comparison: | |
| lhs <cmpop> rhs | |
| where cmpop can be '==', '!=', '<', '<=', '>', '>=', 'ord', 'uno' | |
| """ | |
| return self._cmp('f', 'u', cmpop, other) | |
| # | |
| # Unary APIs | |
| # | |
| def not_(self): | |
| """ | |
| Bitwise integer complement: | |
| ~value | |
| """ | |
| if isinstance(self.type, types.VectorType): | |
| rhs = values.Constant(self.type, (-1,) * self.type.count) | |
| else: | |
| rhs = values.Constant(self.type, -1) | |
| return self.xor(rhs) | |
| def neg(self): | |
| """ | |
| Integer negative: | |
| -value | |
| """ | |
| zero = values.Constant(self.type, 0) | |
| return zero.sub(self) | |
| def fneg(self): | |
| """ | |
| Floating-point negative: | |
| -value | |
| """ | |
| fmt = "fneg ({0} {1})".format(self.type, self.get_reference()) | |
| return FormattedConstant(self.type, fmt) | |
| # | |
| # Cast APIs | |
| # | |
| def trunc(self, typ): | |
| """ | |
| Truncating integer downcast to a smaller type. | |
| """ | |
| def zext(self, typ): | |
| """ | |
| Zero-extending integer upcast to a larger type | |
| """ | |
| def sext(self, typ): | |
| """ | |
| Sign-extending integer upcast to a larger type. | |
| """ | |
| def fptrunc(self, typ): | |
| """ | |
| Floating-point downcast to a less precise type. | |
| """ | |
| def fpext(self, typ): | |
| """ | |
| Floating-point upcast to a more precise type. | |
| """ | |
| def bitcast(self, typ): | |
| """ | |
| Pointer cast to a different pointer type. | |
| """ | |
| def fptoui(self, typ): | |
| """ | |
| Convert floating-point to unsigned integer. | |
| """ | |
| def uitofp(self, typ): | |
| """ | |
| Convert unsigned integer to floating-point. | |
| """ | |
| def fptosi(self, typ): | |
| """ | |
| Convert floating-point to signed integer. | |
| """ | |
| def sitofp(self, typ): | |
| """ | |
| Convert signed integer to floating-point. | |
| """ | |
| def ptrtoint(self, typ): | |
| """ | |
| Cast pointer to integer. | |
| """ | |
| if not isinstance(self.type, types.PointerType): | |
| msg = "can only call ptrtoint() on pointer type, not '%s'" | |
| raise TypeError(msg % (self.type,)) | |
| if not isinstance(typ, types.IntType): | |
| raise TypeError("can only ptrtoint() to integer type, not '%s'" | |
| % (typ,)) | |
| def inttoptr(self, typ): | |
| """ | |
| Cast integer to pointer. | |
| """ | |
| if not isinstance(self.type, types.IntType): | |
| msg = "can only call inttoptr() on integer constants, not '%s'" | |
| raise TypeError(msg % (self.type,)) | |
| if not isinstance(typ, types.PointerType): | |
| raise TypeError("can only inttoptr() to pointer type, not '%s'" | |
| % (typ,)) | |
| def gep(self, indices): | |
| """ | |
| Call getelementptr on this pointer constant. | |
| """ | |
| if not isinstance(self.type, types.PointerType): | |
| raise TypeError("can only call gep() on pointer constants, not '%s'" | |
| % (self.type,)) | |
| outtype = self.type | |
| for i in indices: | |
| outtype = outtype.gep(i) | |
| strindices = ["{0} {1}".format(idx.type, idx.get_reference()) | |
| for idx in indices] | |
| op = "getelementptr ({0}, {1} {2}, {3})".format( | |
| self.type.pointee, self.type, | |
| self.get_reference(), ', '.join(strindices)) | |
| return FormattedConstant(outtype.as_pointer(self.addrspace), op) | |
| class Value(object): | |
| """ | |
| The base class for all values. | |
| """ | |
| def __repr__(self): | |
| return "<ir.%s type='%s' ...>" % (self.__class__.__name__, self.type,) | |
| class _Undefined(object): | |
| """ | |
| 'undef': a value for undefined values. | |
| """ | |
| def __new__(cls): | |
| try: | |
| return Undefined | |
| except NameError: | |
| return object.__new__(_Undefined) | |
| Undefined = _Undefined() | |
| class Constant(_StrCaching, _StringReferenceCaching, _ConstOpMixin, Value): | |
| """ | |
| A constant LLVM value. | |
| """ | |
| def __init__(self, typ, constant): | |
| assert isinstance(typ, types.Type) | |
| assert not isinstance(typ, types.VoidType) | |
| self.type = typ | |
| constant = typ.wrap_constant_value(constant) | |
| self.constant = constant | |
| def _to_string(self): | |
| return '{0} {1}'.format(self.type, self.get_reference()) | |
| def _get_reference(self): | |
| if self.constant is None: | |
| val = self.type.null | |
| elif self.constant is Undefined: | |
| val = "undef" | |
| elif isinstance(self.constant, bytearray): | |
| val = 'c"{0}"'.format(_escape_string(self.constant)) | |
| else: | |
| val = self.type.format_constant(self.constant) | |
| return val | |
| def literal_array(cls, elems): | |
| """ | |
| Construct a literal array constant made of the given members. | |
| """ | |
| tys = [el.type for el in elems] | |
| if len(tys) == 0: | |
| raise ValueError("need at least one element") | |
| ty = tys[0] | |
| for other in tys: | |
| if ty != other: | |
| raise TypeError("all elements must have the same type") | |
| return cls(types.ArrayType(ty, len(elems)), elems) | |
| def literal_struct(cls, elems, packed=False): | |
| """ | |
| Construct a literal structure constant made of the given members. | |
| """ | |
| tys = [el.type for el in elems] | |
| return cls(types.LiteralStructType(tys, packed), elems) | |
| def addrspace(self): | |
| if not isinstance(self.type, types.PointerType): | |
| raise TypeError("Only pointer constant have address spaces") | |
| return self.type.addrspace | |
| def __eq__(self, other): | |
| if isinstance(other, Constant): | |
| return str(self) == str(other) | |
| else: | |
| return False | |
| def __ne__(self, other): | |
| return not self.__eq__(other) | |
| def __hash__(self): | |
| return hash(str(self)) | |
| def __repr__(self): | |
| return "<ir.Constant type='%s' value=%r>" % (self.type, self.constant) | |
| class FormattedConstant(Constant): | |
| """ | |
| A constant with an already formatted IR representation. | |
| """ | |
| def __init__(self, typ, constant): | |
| assert isinstance(constant, str) | |
| Constant.__init__(self, typ, constant) | |
| def _to_string(self): | |
| return self.constant | |
| def _get_reference(self): | |
| return self.constant | |
| class NamedValue(_StrCaching, _StringReferenceCaching, Value): | |
| """ | |
| The base class for named values. | |
| """ | |
| name_prefix = '%' | |
| deduplicate_name = True | |
| def __init__(self, parent, type, name): | |
| assert parent is not None | |
| assert isinstance(type, types.Type) | |
| self.parent = parent | |
| self.type = type | |
| self._set_name(name) | |
| def _to_string(self): | |
| buf = [] | |
| if not isinstance(self.type, types.VoidType): | |
| buf.append("{0} = ".format(self.get_reference())) | |
| self.descr(buf) | |
| return "".join(buf).rstrip() | |
| def descr(self, buf): | |
| raise NotImplementedError | |
| def _get_name(self): | |
| return self._name | |
| def _set_name(self, name): | |
| name = self.parent.scope.register(name, | |
| deduplicate=self.deduplicate_name) | |
| self._name = name | |
| name = property(_get_name, _set_name) | |
| def _get_reference(self): | |
| name = self.name | |
| # Quote and escape value name | |
| if '\\' in name or '"' in name: | |
| name = name.replace('\\', '\\5c').replace('"', '\\22') | |
| return '{0}"{1}"'.format(self.name_prefix, name) | |
| def __repr__(self): | |
| return "<ir.%s %r of type '%s'>" % ( | |
| self.__class__.__name__, self.name, self.type) | |
| def function_type(self): | |
| ty = self.type | |
| if isinstance(ty, types.PointerType): | |
| ty = self.type.pointee | |
| if isinstance(ty, types.FunctionType): | |
| return ty | |
| else: | |
| raise TypeError("Not a function: {0}".format(self.type)) | |
| class MetaDataString(NamedValue): | |
| """ | |
| A metadata string, i.e. a constant string used as a value in a metadata | |
| node. | |
| """ | |
| def __init__(self, parent, string): | |
| super(MetaDataString, self).__init__(parent, | |
| types.MetaDataType(), | |
| name="") | |
| self.string = string | |
| def descr(self, buf): | |
| buf += (self.get_reference(), "\n") | |
| def _get_reference(self): | |
| return '!"{0}"'.format(_escape_string(self.string)) | |
| _to_string = _get_reference | |
| def __eq__(self, other): | |
| if isinstance(other, MetaDataString): | |
| return self.string == other.string | |
| else: | |
| return False | |
| def __ne__(self, other): | |
| return not self.__eq__(other) | |
| def __hash__(self): | |
| return hash(self.string) | |
| class MetaDataArgument(_StrCaching, _StringReferenceCaching, Value): | |
| """ | |
| An argument value to a function taking metadata arguments. | |
| This can wrap any other kind of LLVM value. | |
| Do not instantiate directly, Builder.call() will create these | |
| automatically. | |
| """ | |
| def __init__(self, value): | |
| assert isinstance(value, Value) | |
| assert not isinstance(value.type, types.MetaDataType) | |
| self.type = types.MetaDataType() | |
| self.wrapped_value = value | |
| def _get_reference(self): | |
| # e.g. "i32* %2" | |
| return "{0} {1}".format(self.wrapped_value.type, | |
| self.wrapped_value.get_reference()) | |
| _to_string = _get_reference | |
| class NamedMetaData(object): | |
| """ | |
| A named metadata node. | |
| Do not instantiate directly, use Module.add_named_metadata() instead. | |
| """ | |
| def __init__(self, parent): | |
| self.parent = parent | |
| self.operands = [] | |
| def add(self, md): | |
| self.operands.append(md) | |
| class MDValue(NamedValue): | |
| """ | |
| A metadata node's value, consisting of a sequence of elements ("operands"). | |
| Do not instantiate directly, use Module.add_metadata() instead. | |
| """ | |
| name_prefix = '!' | |
| def __init__(self, parent, values, name): | |
| super(MDValue, self).__init__(parent, | |
| types.MetaDataType(), | |
| name=name) | |
| self.operands = tuple(values) | |
| parent.metadata.append(self) | |
| def descr(self, buf): | |
| operands = [] | |
| for op in self.operands: | |
| if isinstance(op.type, types.MetaDataType): | |
| if isinstance(op, Constant) and op.constant is None: | |
| operands.append("null") | |
| else: | |
| operands.append(op.get_reference()) | |
| else: | |
| operands.append("{0} {1}".format(op.type, op.get_reference())) | |
| operands = ', '.join(operands) | |
| buf += ("!{{ {0} }}".format(operands), "\n") | |
| def _get_reference(self): | |
| return self.name_prefix + str(self.name) | |
| def __eq__(self, other): | |
| if isinstance(other, MDValue): | |
| return self.operands == other.operands | |
| else: | |
| return False | |
| def __ne__(self, other): | |
| return not self.__eq__(other) | |
| def __hash__(self): | |
| return hash(self.operands) | |
| class DIToken: | |
| """ | |
| A debug information enumeration value that should appear bare in | |
| the emitted metadata. | |
| Use this to wrap known constants, e.g. the DW_* enumerations. | |
| """ | |
| def __init__(self, value): | |
| self.value = value | |
| class DIValue(NamedValue): | |
| """ | |
| A debug information descriptor, containing key-value pairs. | |
| Do not instantiate directly, use Module.add_debug_info() instead. | |
| """ | |
| name_prefix = '!' | |
| def __init__(self, parent, is_distinct, kind, operands, name): | |
| super(DIValue, self).__init__(parent, | |
| types.MetaDataType(), | |
| name=name) | |
| self.is_distinct = is_distinct | |
| self.kind = kind | |
| self.operands = tuple(operands) | |
| parent.metadata.append(self) | |
| def descr(self, buf): | |
| if self.is_distinct: | |
| buf += ("distinct ",) | |
| operands = [] | |
| for key, value in self.operands: | |
| if value is None: | |
| strvalue = "null" | |
| elif value is True: | |
| strvalue = "true" | |
| elif value is False: | |
| strvalue = "false" | |
| elif isinstance(value, DIToken): | |
| strvalue = value.value | |
| elif isinstance(value, str): | |
| strvalue = '"{}"'.format(_escape_string(value)) | |
| elif isinstance(value, int): | |
| strvalue = str(value) | |
| elif isinstance(value, Constant): | |
| # Support for typed constants (e.g., i32 1, i8 2) | |
| # This calls Constant._to_string() which formats as "type value" | |
| strvalue = str(value) | |
| elif isinstance(value, NamedValue): | |
| strvalue = value.get_reference() | |
| else: | |
| raise TypeError("invalid operand type for debug info: %r" | |
| % (value,)) | |
| operands.append("{0}: {1}".format(key, strvalue)) | |
| operands = ', '.join(operands) | |
| buf += ("!", self.kind, "(", operands, ")\n") | |
| def _get_reference(self): | |
| return self.name_prefix + str(self.name) | |
| def __eq__(self, other): | |
| if isinstance(other, DIValue): | |
| return self.is_distinct == other.is_distinct and \ | |
| self.kind == other.kind and \ | |
| self.operands == other.operands | |
| else: | |
| return False | |
| def __ne__(self, other): | |
| return not self.__eq__(other) | |
| def __hash__(self): | |
| return hash((self.is_distinct, self.kind, self.operands)) | |
| class GlobalValue(NamedValue, _ConstOpMixin, _HasMetadata): | |
| """ | |
| A global value. | |
| """ | |
| name_prefix = '@' | |
| deduplicate_name = False | |
| def __init__(self, *args, **kwargs): | |
| super(GlobalValue, self).__init__(*args, **kwargs) | |
| self.linkage = '' | |
| self.storage_class = '' | |
| self.section = '' | |
| self.metadata = {} | |
| class GlobalVariable(GlobalValue): | |
| """ | |
| A global variable. | |
| """ | |
| def __init__(self, module, typ, name, addrspace=0): | |
| assert isinstance(typ, types.Type) | |
| super(GlobalVariable, self).__init__(module, typ.as_pointer(addrspace), | |
| name=name) | |
| self.value_type = typ | |
| self.initializer = None | |
| self.unnamed_addr = False | |
| self.global_constant = False | |
| self.addrspace = addrspace | |
| self.align = None | |
| self.parent.add_global(self) | |
| def descr(self, buf): | |
| if self.global_constant: | |
| kind = 'constant' | |
| else: | |
| kind = 'global' | |
| if not self.linkage: | |
| # Default to external linkage | |
| linkage = 'external' if self.initializer is None else '' | |
| else: | |
| linkage = self.linkage | |
| if linkage: | |
| buf.append(linkage + " ") | |
| if self.storage_class: | |
| buf.append(self.storage_class + " ") | |
| if self.unnamed_addr: | |
| buf.append("unnamed_addr ") | |
| if self.addrspace != 0: | |
| buf.append('addrspace({0:d}) '.format(self.addrspace)) | |
| buf.append("{kind} {type}" .format(kind=kind, type=self.value_type)) | |
| if self.initializer is not None: | |
| if self.initializer.type != self.value_type: | |
| raise TypeError("got initializer of type %s " | |
| "for global value type %s" | |
| % (self.initializer.type, self.value_type)) | |
| buf.append(" " + self.initializer.get_reference()) | |
| elif linkage not in ('external', 'extern_weak'): | |
| # emit 'undef' for non-external linkage GV | |
| buf.append(" " + self.value_type(Undefined).get_reference()) | |
| if self.section: | |
| buf.append(", section \"%s\"" % (self.section,)) | |
| if self.align is not None: | |
| buf.append(", align %d" % (self.align,)) | |
| if self.metadata: | |
| buf.append(self._stringify_metadata(leading_comma=True)) | |
| buf.append("\n") | |
| class AttributeSet(set): | |
| """A set of string attribute. | |
| Only accept items listed in *_known*. | |
| Properties: | |
| * Iterate in sorted order | |
| """ | |
| _known = () | |
| def __init__(self, args=()): | |
| super().__init__() | |
| if isinstance(args, str): | |
| args = [args] | |
| for name in args: | |
| self.add(name) | |
| def _expand(self, name, typ): | |
| return name | |
| def add(self, name): | |
| if name not in self._known: | |
| raise ValueError('unknown attr {!r} for {}'.format(name, self)) | |
| return super(AttributeSet, self).add(name) | |
| def _to_list(self, typ): | |
| return [self._expand(i, typ) for i in sorted(self)] | |
| class FunctionAttributes(AttributeSet): | |
| _known = frozenset([ | |
| 'argmemonly', 'alwaysinline', 'builtin', 'cold', 'convergent', | |
| 'inaccessiblememonly', 'inaccessiblemem_or_argmemonly', 'inlinehint', | |
| 'jumptable', 'minsize', 'naked', 'nobuiltin', 'noduplicate', | |
| 'noimplicitfloat', 'noinline', 'nonlazybind', 'norecurse', | |
| 'noredzone', 'noreturn', 'nounwind', 'optnone', 'optsize', | |
| 'readnone', 'readonly', 'returns_twice', 'sanitize_address', | |
| 'sanitize_memory', 'sanitize_thread', 'ssp', | |
| 'sspreg', 'sspstrong', 'uwtable']) | |
| def __init__(self, args=()): | |
| self._alignstack = 0 | |
| self._personality = None | |
| super(FunctionAttributes, self).__init__(args) | |
| def add(self, name): | |
| if ((name == 'alwaysinline' and 'noinline' in self) or | |
| (name == 'noinline' and 'alwaysinline' in self)): | |
| raise ValueError("Can't have alwaysinline and noinline") | |
| super().add(name) | |
| def alignstack(self): | |
| return self._alignstack | |
| def alignstack(self, val): | |
| assert val >= 0 | |
| self._alignstack = val | |
| def personality(self): | |
| return self._personality | |
| def personality(self, val): | |
| assert val is None or isinstance(val, GlobalValue) | |
| self._personality = val | |
| def _to_list(self, ret_type): | |
| attrs = super()._to_list(ret_type) | |
| if self.alignstack: | |
| attrs.append('alignstack({0:d})'.format(self.alignstack)) | |
| if self.personality: | |
| attrs.append('personality {persty} {persfn}'.format( | |
| persty=self.personality.type, | |
| persfn=self.personality.get_reference())) | |
| return attrs | |
| class Function(GlobalValue): | |
| """Represent a LLVM Function but does uses a Module as parent. | |
| Global Values are stored as a set of dependencies (attribute `depends`). | |
| """ | |
| def __init__(self, module, ftype, name): | |
| assert isinstance(ftype, types.Type) | |
| super(Function, self).__init__(module, ftype.as_pointer(), name=name) | |
| self.ftype = ftype | |
| self.scope = _utils.NameScope() | |
| self.blocks = [] | |
| self.attributes = FunctionAttributes() | |
| self.args = tuple([Argument(self, t) | |
| for t in ftype.args]) | |
| self.return_value = ReturnValue(self, ftype.return_type) | |
| self.parent.add_global(self) | |
| self.calling_convention = '' | |
| def module(self): | |
| return self.parent | |
| def entry_basic_block(self): | |
| return self.blocks[0] | |
| def basic_blocks(self): | |
| return self.blocks | |
| def append_basic_block(self, name=''): | |
| blk = Block(parent=self, name=name) | |
| self.blocks.append(blk) | |
| return blk | |
| def insert_basic_block(self, before, name=''): | |
| """Insert block before | |
| """ | |
| blk = Block(parent=self, name=name) | |
| self.blocks.insert(before, blk) | |
| return blk | |
| def descr_prototype(self, buf): | |
| """ | |
| Describe the prototype ("head") of the function. | |
| """ | |
| state = "define" if self.blocks else "declare" | |
| ret = self.return_value | |
| args = ", ".join(str(a) for a in self.args) | |
| name = self.get_reference() | |
| attrs = ' ' + ' '.join(self.attributes._to_list( | |
| self.ftype.return_type)) if self.attributes else '' | |
| if any(self.args): | |
| vararg = ', ...' if self.ftype.var_arg else '' | |
| else: | |
| vararg = '...' if self.ftype.var_arg else '' | |
| linkage = self.linkage | |
| cconv = self.calling_convention | |
| prefix = " ".join(str(x) for x in [state, linkage, cconv, ret] if x) | |
| metadata = self._stringify_metadata() | |
| metadata = ' {}'.format(metadata) if metadata else '' | |
| section = ' section "{}"'.format(self.section) if self.section else '' | |
| pt_str = "{prefix} {name}({args}{vararg}){attrs}{section}{metadata}\n" | |
| prototype = pt_str.format(prefix=prefix, name=name, args=args, | |
| vararg=vararg, attrs=attrs, section=section, | |
| metadata=metadata) | |
| buf.append(prototype) | |
| def descr_body(self, buf): | |
| """ | |
| Describe of the body of the function. | |
| """ | |
| for blk in self.blocks: | |
| blk.descr(buf) | |
| def descr(self, buf): | |
| self.descr_prototype(buf) | |
| if self.blocks: | |
| buf.append("{\n") | |
| self.descr_body(buf) | |
| buf.append("}\n") | |
| def __str__(self): | |
| buf = [] | |
| self.descr(buf) | |
| return "".join(buf) | |
| def is_declaration(self): | |
| return len(self.blocks) == 0 | |
| class ArgumentAttributes(AttributeSet): | |
| # List from | |
| # https://releases.llvm.org/14.0.0/docs/LangRef.html#parameter-attributes | |
| _known = MappingProxyType({ | |
| # True (emit type), | |
| # False (emit name only) | |
| 'byref': True, | |
| 'byval': True, | |
| 'elementtype': True, | |
| 'immarg': False, | |
| 'inalloca': True, | |
| 'inreg': False, | |
| 'nest': False, | |
| 'noalias': False, | |
| 'nocapture': False, | |
| 'nofree': False, | |
| 'nonnull': False, | |
| 'noundef': False, | |
| 'preallocated': True, | |
| 'returned': False, | |
| 'signext': False, | |
| 'sret': True, | |
| 'swiftasync': False, | |
| 'swifterror': False, | |
| 'swiftself': False, | |
| 'zeroext': False, | |
| }) | |
| def __init__(self, args=()): | |
| self._align = 0 | |
| self._dereferenceable = 0 | |
| self._dereferenceable_or_null = 0 | |
| super(ArgumentAttributes, self).__init__(args) | |
| def _expand(self, name, typ): | |
| requires_type = self._known.get(name) | |
| if requires_type: | |
| return f"{name}({typ.pointee})" | |
| else: | |
| return name | |
| def align(self): | |
| return self._align | |
| def align(self, val): | |
| assert isinstance(val, int) and val >= 0 | |
| self._align = val | |
| def dereferenceable(self): | |
| return self._dereferenceable | |
| def dereferenceable(self, val): | |
| assert isinstance(val, int) and val >= 0 | |
| self._dereferenceable = val | |
| def dereferenceable_or_null(self): | |
| return self._dereferenceable_or_null | |
| def dereferenceable_or_null(self, val): | |
| assert isinstance(val, int) and val >= 0 | |
| self._dereferenceable_or_null = val | |
| def _to_list(self, typ): | |
| attrs = super()._to_list(typ) | |
| if self.align: | |
| attrs.append('align {0:d}'.format(self.align)) | |
| if self.dereferenceable: | |
| attrs.append('dereferenceable({0:d})'.format(self.dereferenceable)) | |
| if self.dereferenceable_or_null: | |
| dref = 'dereferenceable_or_null({0:d})' | |
| attrs.append(dref.format(self.dereferenceable_or_null)) | |
| return attrs | |
| class _BaseArgument(NamedValue): | |
| def __init__(self, parent, typ, name=''): | |
| assert isinstance(typ, types.Type) | |
| super(_BaseArgument, self).__init__(parent, typ, name=name) | |
| self.parent = parent | |
| self.attributes = ArgumentAttributes() | |
| def __repr__(self): | |
| return "<ir.%s %r of type %s>" % (self.__class__.__name__, self.name, | |
| self.type) | |
| def add_attribute(self, attr): | |
| self.attributes.add(attr) | |
| class Argument(_BaseArgument): | |
| """ | |
| The specification of a function argument. | |
| """ | |
| def __str__(self): | |
| attrs = self.attributes._to_list(self.type) | |
| if attrs: | |
| return "{0} {1} {2}".format(self.type, ' '.join(attrs), | |
| self.get_reference()) | |
| else: | |
| return "{0} {1}".format(self.type, self.get_reference()) | |
| class ReturnValue(_BaseArgument): | |
| """ | |
| The specification of a function's return value. | |
| """ | |
| def __str__(self): | |
| attrs = self.attributes._to_list(self.type) | |
| if attrs: | |
| return "{0} {1}".format(' '.join(attrs), self.type) | |
| else: | |
| return str(self.type) | |
| class Block(NamedValue): | |
| """ | |
| A LLVM IR basic block. A basic block is a sequence of | |
| instructions whose execution always goes from start to end. That | |
| is, a control flow instruction (branch) can only appear as the | |
| last instruction, and incoming branches can only jump to the first | |
| instruction. | |
| """ | |
| def __init__(self, parent, name=''): | |
| super(Block, self).__init__(parent, types.LabelType(), name=name) | |
| self.scope = parent.scope | |
| self.instructions = [] | |
| self.terminator = None | |
| def is_terminated(self): | |
| return self.terminator is not None | |
| def function(self): | |
| return self.parent | |
| def module(self): | |
| return self.parent.module | |
| def descr(self, buf): | |
| buf.append("{0}:\n".format(self._format_name())) | |
| buf += [" {0}\n".format(instr) for instr in self.instructions] | |
| def replace(self, old, new): | |
| """Replace an instruction""" | |
| if old.type != new.type: | |
| raise TypeError("new instruction has a different type") | |
| pos = self.instructions.index(old) | |
| self.instructions.remove(old) | |
| self.instructions.insert(pos, new) | |
| for bb in self.parent.basic_blocks: | |
| for instr in bb.instructions: | |
| instr.replace_usage(old, new) | |
| def _format_name(self): | |
| # Per the LLVM Language Ref on identifiers, names matching the following | |
| # regex do not need to be quoted: [%@][-a-zA-Z$._][-a-zA-Z$._0-9]* | |
| # Otherwise, the identifier must be quoted and escaped. | |
| name = self.name | |
| if not _SIMPLE_IDENTIFIER_RE.match(name): | |
| name = name.replace('\\', '\\5c').replace('"', '\\22') | |
| name = '"{0}"'.format(name) | |
| return name | |
| class BlockAddress(Value): | |
| """ | |
| The address of a basic block. | |
| """ | |
| def __init__(self, function, basic_block): | |
| assert isinstance(function, Function) | |
| assert isinstance(basic_block, Block) | |
| self.type = types.IntType(8).as_pointer() | |
| self.function = function | |
| self.basic_block = basic_block | |
| def __str__(self): | |
| return '{0} {1}'.format(self.type, self.get_reference()) | |
| def get_reference(self): | |
| return "blockaddress({0}, {1})".format( | |
| self.function.get_reference(), | |
| self.basic_block.get_reference()) | |