Spaces:
Running
Running
| """ | |
| 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 = {} | |
| def function(self): | |
| return self.parent.function | |
| 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) | |
| def callee(self): | |
| return self.operands[0] | |
| def callee(self, newcallee): | |
| self.operands[0] = newcallee | |
| 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 | |
| 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) | |
| 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 = [] | |
| 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 = [] | |
| 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) | |
| def cond(self): | |
| return self.operands[0] | |
| def lhs(self): | |
| return self.operands[1] | |
| 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}") | |