|
""" |
|
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(['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) |
|
|
|
|
|
|
|
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) |
|
|
|
|
|
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 |
|
|
|
if fnty.var_arg: |
|
ty = fnty |
|
|
|
else: |
|
|
|
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): |
|
|
|
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=''): |
|
super(LoadInstr, self).__init__(parent, ptr.type.pointee, "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( |
|
val.type.pointee, |
|
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=''): |
|
super(LoadAtomicInstr, self).__init__(parent, ptr.type.pointee, |
|
"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( |
|
val.type.pointee, |
|
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.align = None |
|
|
|
def descr(self, buf): |
|
buf.append("{0} {1}".format(self.opname, self.type.pointee)) |
|
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): |
|
typ = ptr.type |
|
lasttyp = None |
|
lastaddrspace = 0 |
|
for i in indices: |
|
lasttyp, typ = typ, typ.gep(i) |
|
|
|
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) |
|
|
|
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.pointer.type.pointee, |
|
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}"\n' |
|
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}") |
|
|