|
import contextlib |
|
import functools |
|
|
|
from llvmlite.ir import instructions, types, values |
|
|
|
_CMP_MAP = { |
|
'>': 'gt', |
|
'<': 'lt', |
|
'==': 'eq', |
|
'!=': 'ne', |
|
'>=': 'ge', |
|
'<=': 'le', |
|
} |
|
|
|
|
|
def _unop(opname, cls=instructions.Instruction): |
|
def wrap(fn): |
|
@functools.wraps(fn) |
|
def wrapped(self, arg, name='', flags=()): |
|
instr = cls(self.block, arg.type, opname, [arg], name, flags) |
|
self._insert(instr) |
|
return instr |
|
|
|
return wrapped |
|
|
|
return wrap |
|
|
|
|
|
def _binop(opname, cls=instructions.Instruction): |
|
def wrap(fn): |
|
@functools.wraps(fn) |
|
def wrapped(self, lhs, rhs, name='', flags=()): |
|
if lhs.type != rhs.type: |
|
raise ValueError("Operands must be the same type, got (%s, %s)" |
|
% (lhs.type, rhs.type)) |
|
instr = cls(self.block, lhs.type, opname, (lhs, rhs), name, flags) |
|
self._insert(instr) |
|
return instr |
|
|
|
return wrapped |
|
|
|
return wrap |
|
|
|
|
|
def _binop_with_overflow(opname, cls=instructions.Instruction): |
|
def wrap(fn): |
|
@functools.wraps(fn) |
|
def wrapped(self, lhs, rhs, name=''): |
|
if lhs.type != rhs.type: |
|
raise ValueError("Operands must be the same type, got (%s, %s)" |
|
% (lhs.type, rhs.type)) |
|
ty = lhs.type |
|
if not isinstance(ty, types.IntType): |
|
raise TypeError("expected an integer type, got %s" % (ty,)) |
|
bool_ty = types.IntType(1) |
|
|
|
mod = self.module |
|
fnty = types.FunctionType(types.LiteralStructType([ty, bool_ty]), |
|
[ty, ty]) |
|
fn = mod.declare_intrinsic("llvm.%s.with.overflow" % (opname,), |
|
[ty], fnty) |
|
ret = self.call(fn, [lhs, rhs], name=name) |
|
return ret |
|
|
|
return wrapped |
|
|
|
return wrap |
|
|
|
|
|
def _uniop(opname, cls=instructions.Instruction): |
|
def wrap(fn): |
|
@functools.wraps(fn) |
|
def wrapped(self, operand, name=''): |
|
instr = cls(self.block, operand.type, opname, [operand], name) |
|
self._insert(instr) |
|
return instr |
|
|
|
return wrapped |
|
|
|
return wrap |
|
|
|
|
|
def _uniop_intrinsic_int(opname): |
|
def wrap(fn): |
|
@functools.wraps(fn) |
|
def wrapped(self, operand, name=''): |
|
if not isinstance(operand.type, types.IntType): |
|
raise TypeError( |
|
"expected an integer type, got %s" % |
|
operand.type) |
|
fn = self.module.declare_intrinsic(opname, [operand.type]) |
|
return self.call(fn, [operand], name) |
|
|
|
return wrapped |
|
|
|
return wrap |
|
|
|
|
|
def _uniop_intrinsic_float(opname): |
|
def wrap(fn): |
|
@functools.wraps(fn) |
|
def wrapped(self, operand, name=''): |
|
if not isinstance( |
|
operand.type, (types.FloatType, types.DoubleType)): |
|
raise TypeError("expected a float type, got %s" % operand.type) |
|
fn = self.module.declare_intrinsic(opname, [operand.type]) |
|
return self.call(fn, [operand], name) |
|
|
|
return wrapped |
|
|
|
return wrap |
|
|
|
|
|
def _uniop_intrinsic_with_flag(opname): |
|
def wrap(fn): |
|
@functools.wraps(fn) |
|
def wrapped(self, operand, flag, name=''): |
|
if not isinstance(operand.type, types.IntType): |
|
raise TypeError( |
|
"expected an integer type, got %s" % |
|
operand.type) |
|
if not (isinstance(flag.type, types.IntType) and |
|
flag.type.width == 1): |
|
raise TypeError("expected an i1 type, got %s" % flag.type) |
|
fn = self.module.declare_intrinsic( |
|
opname, [operand.type, flag.type]) |
|
return self.call(fn, [operand, flag], name) |
|
|
|
return wrapped |
|
|
|
return wrap |
|
|
|
|
|
def _triop_intrinsic(opname): |
|
def wrap(fn): |
|
@functools.wraps(fn) |
|
def wrapped(self, a, b, c, name=''): |
|
if a.type != b.type or b.type != c.type: |
|
raise TypeError( |
|
"expected types to be the same, got %s, %s, %s" % ( |
|
a.type, |
|
b.type, |
|
c.type)) |
|
elif not isinstance( |
|
a.type, |
|
(types.HalfType, types.FloatType, types.DoubleType)): |
|
raise TypeError( |
|
"expected an floating point type, got %s" % |
|
a.type) |
|
fn = self.module.declare_intrinsic(opname, [a.type, b.type, c.type]) |
|
return self.call(fn, [a, b, c], name) |
|
|
|
return wrapped |
|
|
|
return wrap |
|
|
|
|
|
def _castop(opname, cls=instructions.CastInstr): |
|
def wrap(fn): |
|
@functools.wraps(fn) |
|
def wrapped(self, val, typ, name=''): |
|
if val.type == typ: |
|
return val |
|
instr = cls(self.block, opname, val, typ, name) |
|
self._insert(instr) |
|
return instr |
|
|
|
return wrapped |
|
|
|
return wrap |
|
|
|
|
|
def _label_suffix(label, suffix): |
|
"""Returns (label + suffix) or a truncated version if it's too long. |
|
Parameters |
|
---------- |
|
label : str |
|
Label name |
|
suffix : str |
|
Label suffix |
|
""" |
|
if len(label) > 50: |
|
nhead = 25 |
|
return ''.join([label[:nhead], '..', suffix]) |
|
else: |
|
return label + suffix |
|
|
|
|
|
class IRBuilder(object): |
|
def __init__(self, block=None): |
|
self._block = block |
|
self._anchor = len(block.instructions) if block else 0 |
|
self.debug_metadata = None |
|
|
|
@property |
|
def block(self): |
|
""" |
|
The current basic block. |
|
""" |
|
return self._block |
|
|
|
basic_block = block |
|
|
|
@property |
|
def function(self): |
|
""" |
|
The current function. |
|
""" |
|
return self.block.parent |
|
|
|
@property |
|
def module(self): |
|
""" |
|
The current module. |
|
""" |
|
return self.block.parent.module |
|
|
|
def position_before(self, instr): |
|
""" |
|
Position immediately before the given instruction. The current block |
|
is also changed to the instruction's basic block. |
|
""" |
|
self._block = instr.parent |
|
self._anchor = self._block.instructions.index(instr) |
|
|
|
def position_after(self, instr): |
|
""" |
|
Position immediately after the given instruction. The current block |
|
is also changed to the instruction's basic block. |
|
""" |
|
self._block = instr.parent |
|
self._anchor = self._block.instructions.index(instr) + 1 |
|
|
|
def position_at_start(self, block): |
|
""" |
|
Position at the start of the basic *block*. |
|
""" |
|
self._block = block |
|
self._anchor = 0 |
|
|
|
def position_at_end(self, block): |
|
""" |
|
Position at the end of the basic *block*. |
|
""" |
|
self._block = block |
|
self._anchor = len(block.instructions) |
|
|
|
def append_basic_block(self, name=''): |
|
""" |
|
Append a basic block, with the given optional *name*, to the current |
|
function. The current block is not changed. The new block is returned. |
|
""" |
|
return self.function.append_basic_block(name) |
|
|
|
def remove(self, instr): |
|
"""Remove the given instruction.""" |
|
idx = self._block.instructions.index(instr) |
|
del self._block.instructions[idx] |
|
if self._block.terminator == instr: |
|
self._block.terminator = None |
|
if self._anchor > idx: |
|
self._anchor -= 1 |
|
|
|
@contextlib.contextmanager |
|
def goto_block(self, block): |
|
""" |
|
A context manager which temporarily positions the builder at the end |
|
of basic block *bb* (but before any terminator). |
|
""" |
|
old_block = self.basic_block |
|
term = block.terminator |
|
if term is not None: |
|
self.position_before(term) |
|
else: |
|
self.position_at_end(block) |
|
try: |
|
yield |
|
finally: |
|
self.position_at_end(old_block) |
|
|
|
@contextlib.contextmanager |
|
def goto_entry_block(self): |
|
""" |
|
A context manager which temporarily positions the builder at the |
|
end of the function's entry block. |
|
""" |
|
with self.goto_block(self.function.entry_basic_block): |
|
yield |
|
|
|
@contextlib.contextmanager |
|
def _branch_helper(self, bbenter, bbexit): |
|
self.position_at_end(bbenter) |
|
yield bbexit |
|
if self.basic_block.terminator is None: |
|
self.branch(bbexit) |
|
|
|
@contextlib.contextmanager |
|
def if_then(self, pred, likely=None): |
|
""" |
|
A context manager which sets up a conditional basic block based |
|
on the given predicate (a i1 value). If the conditional block |
|
is not explicitly terminated, a branch will be added to the next |
|
block. |
|
If *likely* is given, its boolean value indicates whether the |
|
predicate is likely to be true or not, and metadata is issued |
|
for LLVM's optimizers to account for that. |
|
""" |
|
bb = self.basic_block |
|
bbif = self.append_basic_block(name=_label_suffix(bb.name, '.if')) |
|
bbend = self.append_basic_block(name=_label_suffix(bb.name, '.endif')) |
|
br = self.cbranch(pred, bbif, bbend) |
|
if likely is not None: |
|
br.set_weights([99, 1] if likely else [1, 99]) |
|
|
|
with self._branch_helper(bbif, bbend): |
|
yield bbend |
|
|
|
self.position_at_end(bbend) |
|
|
|
@contextlib.contextmanager |
|
def if_else(self, pred, likely=None): |
|
""" |
|
A context manager which sets up two conditional basic blocks based |
|
on the given predicate (a i1 value). |
|
A tuple of context managers is yield'ed. Each context manager |
|
acts as a if_then() block. |
|
*likely* has the same meaning as in if_then(). |
|
|
|
Typical use:: |
|
with builder.if_else(pred) as (then, otherwise): |
|
with then: |
|
# emit instructions for when the predicate is true |
|
with otherwise: |
|
# emit instructions for when the predicate is false |
|
""" |
|
bb = self.basic_block |
|
bbif = self.append_basic_block(name=_label_suffix(bb.name, '.if')) |
|
bbelse = self.append_basic_block(name=_label_suffix(bb.name, '.else')) |
|
bbend = self.append_basic_block(name=_label_suffix(bb.name, '.endif')) |
|
br = self.cbranch(pred, bbif, bbelse) |
|
if likely is not None: |
|
br.set_weights([99, 1] if likely else [1, 99]) |
|
|
|
then = self._branch_helper(bbif, bbend) |
|
otherwise = self._branch_helper(bbelse, bbend) |
|
|
|
yield then, otherwise |
|
|
|
self.position_at_end(bbend) |
|
|
|
def _insert(self, instr): |
|
if self.debug_metadata is not None and 'dbg' not in instr.metadata: |
|
instr.metadata['dbg'] = self.debug_metadata |
|
self._block.instructions.insert(self._anchor, instr) |
|
self._anchor += 1 |
|
|
|
def _set_terminator(self, term): |
|
assert not self.block.is_terminated |
|
self._insert(term) |
|
self.block.terminator = term |
|
return term |
|
|
|
|
|
|
|
|
|
|
|
@_binop('shl') |
|
def shl(self, lhs, rhs, name=''): |
|
""" |
|
Left integer shift: |
|
name = lhs << rhs |
|
""" |
|
|
|
@_binop('lshr') |
|
def lshr(self, lhs, rhs, name=''): |
|
""" |
|
Logical (unsigned) right integer shift: |
|
name = lhs >> rhs |
|
""" |
|
|
|
@_binop('ashr') |
|
def ashr(self, lhs, rhs, name=''): |
|
""" |
|
Arithmetic (signed) right integer shift: |
|
name = lhs >> rhs |
|
""" |
|
|
|
@_binop('add') |
|
def add(self, lhs, rhs, name=''): |
|
""" |
|
Integer addition: |
|
name = lhs + rhs |
|
""" |
|
|
|
@_binop('fadd') |
|
def fadd(self, lhs, rhs, name=''): |
|
""" |
|
Floating-point addition: |
|
name = lhs + rhs |
|
""" |
|
|
|
@_binop('sub') |
|
def sub(self, lhs, rhs, name=''): |
|
""" |
|
Integer subtraction: |
|
name = lhs - rhs |
|
""" |
|
|
|
@_binop('fsub') |
|
def fsub(self, lhs, rhs, name=''): |
|
""" |
|
Floating-point subtraction: |
|
name = lhs - rhs |
|
""" |
|
|
|
@_binop('mul') |
|
def mul(self, lhs, rhs, name=''): |
|
""" |
|
Integer multiplication: |
|
name = lhs * rhs |
|
""" |
|
|
|
@_binop('fmul') |
|
def fmul(self, lhs, rhs, name=''): |
|
""" |
|
Floating-point multiplication: |
|
name = lhs * rhs |
|
""" |
|
|
|
@_binop('udiv') |
|
def udiv(self, lhs, rhs, name=''): |
|
""" |
|
Unsigned integer division: |
|
name = lhs / rhs |
|
""" |
|
|
|
@_binop('sdiv') |
|
def sdiv(self, lhs, rhs, name=''): |
|
""" |
|
Signed integer division: |
|
name = lhs / rhs |
|
""" |
|
|
|
@_binop('fdiv') |
|
def fdiv(self, lhs, rhs, name=''): |
|
""" |
|
Floating-point division: |
|
name = lhs / rhs |
|
""" |
|
|
|
@_binop('urem') |
|
def urem(self, lhs, rhs, name=''): |
|
""" |
|
Unsigned integer remainder: |
|
name = lhs % rhs |
|
""" |
|
|
|
@_binop('srem') |
|
def srem(self, lhs, rhs, name=''): |
|
""" |
|
Signed integer remainder: |
|
name = lhs % rhs |
|
""" |
|
|
|
@_binop('frem') |
|
def frem(self, lhs, rhs, name=''): |
|
""" |
|
Floating-point remainder: |
|
name = lhs % rhs |
|
""" |
|
|
|
@_binop('or') |
|
def or_(self, lhs, rhs, name=''): |
|
""" |
|
Bitwise integer OR: |
|
name = lhs | rhs |
|
""" |
|
|
|
@_binop('and') |
|
def and_(self, lhs, rhs, name=''): |
|
""" |
|
Bitwise integer AND: |
|
name = lhs & rhs |
|
""" |
|
|
|
@_binop('xor') |
|
def xor(self, lhs, rhs, name=''): |
|
""" |
|
Bitwise integer XOR: |
|
name = lhs ^ rhs |
|
""" |
|
|
|
@_binop_with_overflow('sadd') |
|
def sadd_with_overflow(self, lhs, rhs, name=''): |
|
""" |
|
Signed integer addition with overflow: |
|
name = {result, overflow bit} = lhs + rhs |
|
""" |
|
|
|
@_binop_with_overflow('smul') |
|
def smul_with_overflow(self, lhs, rhs, name=''): |
|
""" |
|
Signed integer multiplication with overflow: |
|
name = {result, overflow bit} = lhs * rhs |
|
""" |
|
|
|
@_binop_with_overflow('ssub') |
|
def ssub_with_overflow(self, lhs, rhs, name=''): |
|
""" |
|
Signed integer subtraction with overflow: |
|
name = {result, overflow bit} = lhs - rhs |
|
""" |
|
|
|
@_binop_with_overflow('uadd') |
|
def uadd_with_overflow(self, lhs, rhs, name=''): |
|
""" |
|
Unsigned integer addition with overflow: |
|
name = {result, overflow bit} = lhs + rhs |
|
""" |
|
|
|
@_binop_with_overflow('umul') |
|
def umul_with_overflow(self, lhs, rhs, name=''): |
|
""" |
|
Unsigned integer multiplication with overflow: |
|
name = {result, overflow bit} = lhs * rhs |
|
""" |
|
|
|
@_binop_with_overflow('usub') |
|
def usub_with_overflow(self, lhs, rhs, name=''): |
|
""" |
|
Unsigned integer subtraction with overflow: |
|
name = {result, overflow bit} = lhs - rhs |
|
""" |
|
|
|
|
|
|
|
|
|
|
|
def not_(self, value, name=''): |
|
""" |
|
Bitwise integer complement: |
|
name = ~value |
|
""" |
|
if isinstance(value.type, types.VectorType): |
|
rhs = values.Constant(value.type, (-1,) * value.type.count) |
|
else: |
|
rhs = values.Constant(value.type, -1) |
|
return self.xor(value, rhs, name=name) |
|
|
|
def neg(self, value, name=''): |
|
""" |
|
Integer negative: |
|
name = -value |
|
""" |
|
return self.sub(values.Constant(value.type, 0), value, name=name) |
|
|
|
@_unop('fneg') |
|
def fneg(self, arg, name='', flags=()): |
|
""" |
|
Floating-point negative: |
|
name = -arg |
|
""" |
|
|
|
|
|
|
|
|
|
|
|
def _icmp(self, prefix, cmpop, lhs, rhs, name): |
|
try: |
|
op = _CMP_MAP[cmpop] |
|
except KeyError: |
|
raise ValueError("invalid comparison %r for icmp" % (cmpop,)) |
|
if cmpop not in ('==', '!='): |
|
op = prefix + op |
|
instr = instructions.ICMPInstr(self.block, op, lhs, rhs, name=name) |
|
self._insert(instr) |
|
return instr |
|
|
|
def icmp_signed(self, cmpop, lhs, rhs, name=''): |
|
""" |
|
Signed integer comparison: |
|
name = lhs <cmpop> rhs |
|
|
|
where cmpop can be '==', '!=', '<', '<=', '>', '>=' |
|
""" |
|
return self._icmp('s', cmpop, lhs, rhs, name) |
|
|
|
def icmp_unsigned(self, cmpop, lhs, rhs, name=''): |
|
""" |
|
Unsigned integer (or pointer) comparison: |
|
name = lhs <cmpop> rhs |
|
|
|
where cmpop can be '==', '!=', '<', '<=', '>', '>=' |
|
""" |
|
return self._icmp('u', cmpop, lhs, rhs, name) |
|
|
|
def fcmp_ordered(self, cmpop, lhs, rhs, name='', flags=()): |
|
""" |
|
Floating-point ordered comparison: |
|
name = lhs <cmpop> rhs |
|
|
|
where cmpop can be '==', '!=', '<', '<=', '>', '>=', 'ord', 'uno' |
|
""" |
|
if cmpop in _CMP_MAP: |
|
op = 'o' + _CMP_MAP[cmpop] |
|
else: |
|
op = cmpop |
|
instr = instructions.FCMPInstr( |
|
self.block, op, lhs, rhs, name=name, flags=flags) |
|
self._insert(instr) |
|
return instr |
|
|
|
def fcmp_unordered(self, cmpop, lhs, rhs, name='', flags=()): |
|
""" |
|
Floating-point unordered comparison: |
|
name = lhs <cmpop> rhs |
|
|
|
where cmpop can be '==', '!=', '<', '<=', '>', '>=', 'ord', 'uno' |
|
""" |
|
if cmpop in _CMP_MAP: |
|
op = 'u' + _CMP_MAP[cmpop] |
|
else: |
|
op = cmpop |
|
instr = instructions.FCMPInstr( |
|
self.block, op, lhs, rhs, name=name, flags=flags) |
|
self._insert(instr) |
|
return instr |
|
|
|
def select(self, cond, lhs, rhs, name='', flags=()): |
|
""" |
|
Ternary select operator: |
|
name = cond ? lhs : rhs |
|
""" |
|
instr = instructions.SelectInstr(self.block, cond, lhs, rhs, name=name, |
|
flags=flags) |
|
self._insert(instr) |
|
return instr |
|
|
|
|
|
|
|
|
|
|
|
@_castop('trunc') |
|
def trunc(self, value, typ, name=''): |
|
""" |
|
Truncating integer downcast to a smaller type: |
|
name = (typ) value |
|
""" |
|
|
|
@_castop('zext') |
|
def zext(self, value, typ, name=''): |
|
""" |
|
Zero-extending integer upcast to a larger type: |
|
name = (typ) value |
|
""" |
|
|
|
@_castop('sext') |
|
def sext(self, value, typ, name=''): |
|
""" |
|
Sign-extending integer upcast to a larger type: |
|
name = (typ) value |
|
""" |
|
|
|
@_castop('fptrunc') |
|
def fptrunc(self, value, typ, name=''): |
|
""" |
|
Floating-point downcast to a less precise type: |
|
name = (typ) value |
|
""" |
|
|
|
@_castop('fpext') |
|
def fpext(self, value, typ, name=''): |
|
""" |
|
Floating-point upcast to a more precise type: |
|
name = (typ) value |
|
""" |
|
|
|
@_castop('bitcast') |
|
def bitcast(self, value, typ, name=''): |
|
""" |
|
Pointer cast to a different pointer type: |
|
name = (typ) value |
|
""" |
|
|
|
@_castop('addrspacecast') |
|
def addrspacecast(self, value, typ, name=''): |
|
""" |
|
Pointer cast to a different address space: |
|
name = (typ) value |
|
""" |
|
|
|
@_castop('fptoui') |
|
def fptoui(self, value, typ, name=''): |
|
""" |
|
Convert floating-point to unsigned integer: |
|
name = (typ) value |
|
""" |
|
|
|
@_castop('uitofp') |
|
def uitofp(self, value, typ, name=''): |
|
""" |
|
Convert unsigned integer to floating-point: |
|
name = (typ) value |
|
""" |
|
|
|
@_castop('fptosi') |
|
def fptosi(self, value, typ, name=''): |
|
""" |
|
Convert floating-point to signed integer: |
|
name = (typ) value |
|
""" |
|
|
|
@_castop('sitofp') |
|
def sitofp(self, value, typ, name=''): |
|
""" |
|
Convert signed integer to floating-point: |
|
name = (typ) value |
|
""" |
|
|
|
@_castop('ptrtoint') |
|
def ptrtoint(self, value, typ, name=''): |
|
""" |
|
Cast pointer to integer: |
|
name = (typ) value |
|
""" |
|
|
|
@_castop('inttoptr') |
|
def inttoptr(self, value, typ, name=''): |
|
""" |
|
Cast integer to pointer: |
|
name = (typ) value |
|
""" |
|
|
|
|
|
|
|
|
|
|
|
def alloca(self, typ, size=None, name=''): |
|
""" |
|
Stack-allocate a slot for *size* elements of the given type. |
|
(default one element) |
|
""" |
|
if size is None: |
|
pass |
|
elif isinstance(size, (values.Value, values.Constant)): |
|
assert isinstance(size.type, types.IntType) |
|
else: |
|
|
|
|
|
size = values.Constant(types.IntType(32), size) |
|
|
|
al = instructions.AllocaInstr(self.block, typ, size, name) |
|
self._insert(al) |
|
return al |
|
|
|
def load(self, ptr, name='', align=None): |
|
""" |
|
Load value from pointer, with optional guaranteed alignment: |
|
name = *ptr |
|
""" |
|
if not isinstance(ptr.type, types.PointerType): |
|
msg = "cannot load from value of type %s (%r): not a pointer" |
|
raise TypeError(msg % (ptr.type, str(ptr))) |
|
ld = instructions.LoadInstr(self.block, ptr, name) |
|
ld.align = align |
|
self._insert(ld) |
|
return ld |
|
|
|
def store(self, value, ptr, align=None): |
|
""" |
|
Store value to pointer, with optional guaranteed alignment: |
|
*ptr = name |
|
""" |
|
if not isinstance(ptr.type, types.PointerType): |
|
msg = "cannot store to value of type %s (%r): not a pointer" |
|
raise TypeError(msg % (ptr.type, str(ptr))) |
|
if ptr.type.pointee != value.type: |
|
raise TypeError("cannot store %s to %s: mismatching types" |
|
% (value.type, ptr.type)) |
|
st = instructions.StoreInstr(self.block, value, ptr) |
|
st.align = align |
|
self._insert(st) |
|
return st |
|
|
|
def load_atomic(self, ptr, ordering, align, name=''): |
|
""" |
|
Load value from pointer, with optional guaranteed alignment: |
|
name = *ptr |
|
""" |
|
if not isinstance(ptr.type, types.PointerType): |
|
msg = "cannot load from value of type %s (%r): not a pointer" |
|
raise TypeError(msg % (ptr.type, str(ptr))) |
|
ld = instructions.LoadAtomicInstr( |
|
self.block, ptr, ordering, align, name) |
|
self._insert(ld) |
|
return ld |
|
|
|
def store_atomic(self, value, ptr, ordering, align): |
|
""" |
|
Store value to pointer, with optional guaranteed alignment: |
|
*ptr = name |
|
""" |
|
if not isinstance(ptr.type, types.PointerType): |
|
msg = "cannot store to value of type %s (%r): not a pointer" |
|
raise TypeError(msg % (ptr.type, str(ptr))) |
|
if ptr.type.pointee != value.type: |
|
raise TypeError("cannot store %s to %s: mismatching types" |
|
% (value.type, ptr.type)) |
|
st = instructions.StoreAtomicInstr( |
|
self.block, value, ptr, ordering, align) |
|
self._insert(st) |
|
return st |
|
|
|
|
|
|
|
|
|
|
|
def switch(self, value, default): |
|
""" |
|
Create a switch-case with a single *default* target. |
|
""" |
|
swt = instructions.SwitchInstr(self.block, 'switch', value, default) |
|
self._set_terminator(swt) |
|
return swt |
|
|
|
def branch(self, target): |
|
""" |
|
Unconditional branch to *target*. |
|
""" |
|
br = instructions.Branch(self.block, "br", [target]) |
|
self._set_terminator(br) |
|
return br |
|
|
|
def cbranch(self, cond, truebr, falsebr): |
|
""" |
|
Conditional branch to *truebr* if *cond* is true, else to *falsebr*. |
|
""" |
|
br = instructions.ConditionalBranch(self.block, "br", |
|
[cond, truebr, falsebr]) |
|
self._set_terminator(br) |
|
return br |
|
|
|
def branch_indirect(self, addr): |
|
""" |
|
Indirect branch to target *addr*. |
|
""" |
|
br = instructions.IndirectBranch(self.block, "indirectbr", addr) |
|
self._set_terminator(br) |
|
return br |
|
|
|
def ret_void(self): |
|
""" |
|
Return from function without a value. |
|
""" |
|
return self._set_terminator( |
|
instructions.Ret(self.block, "ret void")) |
|
|
|
def ret(self, value): |
|
""" |
|
Return from function with the given *value*. |
|
""" |
|
return self._set_terminator( |
|
instructions.Ret(self.block, "ret", value)) |
|
|
|
def resume(self, landingpad): |
|
""" |
|
Resume an in-flight exception. |
|
""" |
|
br = instructions.Branch(self.block, "resume", [landingpad]) |
|
self._set_terminator(br) |
|
return br |
|
|
|
|
|
|
|
def call(self, fn, args, name='', cconv=None, tail=False, fastmath=(), |
|
attrs=(), arg_attrs=None): |
|
""" |
|
Call function *fn* with *args*: |
|
name = fn(args...) |
|
""" |
|
inst = instructions.CallInstr(self.block, fn, args, name=name, |
|
cconv=cconv, tail=tail, fastmath=fastmath, |
|
attrs=attrs, arg_attrs=arg_attrs) |
|
self._insert(inst) |
|
return inst |
|
|
|
def asm(self, ftype, asm, constraint, args, side_effect, name=''): |
|
""" |
|
Inline assembler. |
|
""" |
|
asm = instructions.InlineAsm(ftype, asm, constraint, side_effect) |
|
return self.call(asm, args, name) |
|
|
|
def load_reg(self, reg_type, reg_name, name=''): |
|
""" |
|
Load a register value into an LLVM value. |
|
Example: v = load_reg(IntType(32), "eax") |
|
""" |
|
ftype = types.FunctionType(reg_type, []) |
|
return self.asm(ftype, "", "={%s}" % reg_name, [], False, name) |
|
|
|
def store_reg(self, value, reg_type, reg_name, name=''): |
|
""" |
|
Store an LLVM value inside a register |
|
Example: |
|
store_reg(Constant(IntType(32), 0xAAAAAAAA), IntType(32), "eax") |
|
""" |
|
ftype = types.FunctionType(types.VoidType(), [reg_type]) |
|
return self.asm(ftype, "", "{%s}" % reg_name, [value], True, name) |
|
|
|
def invoke(self, fn, args, normal_to, unwind_to, |
|
name='', cconv=None, fastmath=(), attrs=(), arg_attrs=None): |
|
inst = instructions.InvokeInstr(self.block, fn, args, normal_to, |
|
unwind_to, name=name, cconv=cconv, |
|
fastmath=fastmath, attrs=attrs, |
|
arg_attrs=arg_attrs) |
|
self._set_terminator(inst) |
|
return inst |
|
|
|
|
|
|
|
def gep(self, ptr, indices, inbounds=False, name=''): |
|
""" |
|
Compute effective address (getelementptr): |
|
name = getelementptr ptr, <indices...> |
|
""" |
|
instr = instructions.GEPInstr(self.block, ptr, indices, |
|
inbounds=inbounds, name=name) |
|
self._insert(instr) |
|
return instr |
|
|
|
|
|
|
|
def extract_element(self, vector, idx, name=''): |
|
""" |
|
Returns the value at position idx. |
|
""" |
|
instr = instructions.ExtractElement(self.block, vector, idx, name=name) |
|
self._insert(instr) |
|
return instr |
|
|
|
def insert_element(self, vector, value, idx, name=''): |
|
""" |
|
Returns vector with vector[idx] replaced by value. |
|
The result is undefined if the idx is larger or equal the vector length. |
|
""" |
|
instr = instructions.InsertElement(self.block, vector, value, idx, |
|
name=name) |
|
self._insert(instr) |
|
return instr |
|
|
|
def shuffle_vector(self, vector1, vector2, mask, name=''): |
|
""" |
|
Constructs a permutation of elements from *vector1* and *vector2*. |
|
Returns a new vector in the same length of *mask*. |
|
|
|
* *vector1* and *vector2* must have the same element type. |
|
* *mask* must be a constant vector of integer types. |
|
""" |
|
instr = instructions.ShuffleVector(self.block, vector1, vector2, mask, |
|
name=name) |
|
self._insert(instr) |
|
return instr |
|
|
|
|
|
|
|
def extract_value(self, agg, idx, name=''): |
|
""" |
|
Extract member number *idx* from aggregate. |
|
""" |
|
if not isinstance(idx, (tuple, list)): |
|
idx = [idx] |
|
instr = instructions.ExtractValue(self.block, agg, idx, name=name) |
|
self._insert(instr) |
|
return instr |
|
|
|
def insert_value(self, agg, value, idx, name=''): |
|
""" |
|
Insert *value* into member number *idx* from aggregate. |
|
""" |
|
if not isinstance(idx, (tuple, list)): |
|
idx = [idx] |
|
instr = instructions.InsertValue(self.block, agg, value, idx, name=name) |
|
self._insert(instr) |
|
return instr |
|
|
|
|
|
|
|
def phi(self, typ, name='', flags=()): |
|
inst = instructions.PhiInstr(self.block, typ, name=name, flags=flags) |
|
self._insert(inst) |
|
return inst |
|
|
|
|
|
|
|
def unreachable(self): |
|
inst = instructions.Unreachable(self.block) |
|
self._set_terminator(inst) |
|
return inst |
|
|
|
def atomic_rmw(self, op, ptr, val, ordering, name=''): |
|
inst = instructions.AtomicRMW( |
|
self.block, op, ptr, val, ordering, name=name) |
|
self._insert(inst) |
|
return inst |
|
|
|
def cmpxchg(self, ptr, cmp, val, ordering, failordering=None, name=''): |
|
""" |
|
Atomic compared-and-set: |
|
atomic { |
|
old = *ptr |
|
success = (old == cmp) |
|
if (success) |
|
*ptr = val |
|
} |
|
name = { old, success } |
|
|
|
If failordering is `None`, the value of `ordering` is used. |
|
""" |
|
failordering = ordering if failordering is None else failordering |
|
inst = instructions.CmpXchg(self.block, ptr, cmp, val, ordering, |
|
failordering, name=name) |
|
self._insert(inst) |
|
return inst |
|
|
|
def landingpad(self, typ, name='', cleanup=False): |
|
inst = instructions.LandingPadInstr(self.block, typ, name, cleanup) |
|
self._insert(inst) |
|
return inst |
|
|
|
def assume(self, cond): |
|
""" |
|
Optimizer hint: assume *cond* is always true. |
|
""" |
|
fn = self.module.declare_intrinsic("llvm.assume") |
|
return self.call(fn, [cond]) |
|
|
|
def fence(self, ordering, targetscope=None, name=''): |
|
""" |
|
Add a memory barrier, preventing certain reorderings of load and/or |
|
store accesses with |
|
respect to other processors and devices. |
|
""" |
|
inst = instructions.Fence(self.block, ordering, targetscope, name=name) |
|
self._insert(inst) |
|
return inst |
|
|
|
def comment(self, text): |
|
""" |
|
Puts a single-line comment into the generated IR. This will be ignored |
|
by LLVM, but can be useful for debugging the output of a compiler. Adds |
|
a comment to the source file. |
|
|
|
* *text* is a string that does not contain new line characters. |
|
""" |
|
inst = instructions.Comment(self.block, text) |
|
self._insert(inst) |
|
return inst |
|
|
|
@_uniop_intrinsic_int("llvm.bswap") |
|
def bswap(self, cond): |
|
""" |
|
Used to byte swap integer values with an even number of bytes (positive |
|
multiple of 16 bits) |
|
""" |
|
|
|
@_uniop_intrinsic_int("llvm.bitreverse") |
|
def bitreverse(self, cond): |
|
""" |
|
Reverse the bitpattern of an integer value; for example 0b10110110 |
|
becomes 0b01101101. |
|
""" |
|
|
|
@_uniop_intrinsic_int("llvm.ctpop") |
|
def ctpop(self, cond): |
|
""" |
|
Counts the number of bits set in a value. |
|
""" |
|
|
|
@_uniop_intrinsic_with_flag("llvm.ctlz") |
|
def ctlz(self, cond, flag): |
|
""" |
|
Counts leading zero bits in *value*. Boolean *flag* indicates whether |
|
the result is defined for ``0``. |
|
""" |
|
|
|
@_uniop_intrinsic_with_flag("llvm.cttz") |
|
def cttz(self, cond, flag): |
|
""" |
|
Counts trailing zero bits in *value*. Boolean *flag* indicates whether |
|
the result is defined for ``0``. |
|
""" |
|
|
|
@_triop_intrinsic("llvm.fma") |
|
def fma(self, a, b, c): |
|
""" |
|
Perform the fused multiply-add operation. |
|
""" |
|
|
|
def convert_from_fp16(self, a, to=None, name=''): |
|
""" |
|
Convert from an i16 to the given FP type |
|
""" |
|
if not to: |
|
raise TypeError("expected a float return type") |
|
if not isinstance(to, (types.FloatType, types.DoubleType)): |
|
raise TypeError("expected a float type, got %s" % to) |
|
if not (isinstance(a.type, types.IntType) and a.type.width == 16): |
|
raise TypeError("expected an i16 type, got %s" % a.type) |
|
|
|
opname = 'llvm.convert.from.fp16' |
|
fn = self.module.declare_intrinsic(opname, [to]) |
|
return self.call(fn, [a], name) |
|
|
|
@_uniop_intrinsic_float("llvm.convert.to.fp16") |
|
def convert_to_fp16(self, a): |
|
""" |
|
Convert the given FP number to an i16 |
|
""" |
|
|