Skip to content

Commit

Permalink
Add fastmath flag.
Browse files Browse the repository at this point in the history
* call instr now won’t print function type unless it is variable-arg.
* AttributeSet now iterate in sorted order.
  • Loading branch information
sklam committed Feb 3, 2017
1 parent fc21132 commit 3b10581
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 31 deletions.
6 changes: 4 additions & 2 deletions docs/source/ir/builder.rst
Expand Up @@ -442,11 +442,13 @@ Memory
Function call
'''''''''''''

.. method:: IRBuilder.call(fn, args, name='', cconv=None, tail=False)
.. method:: IRBuilder.call(fn, args, name='', cconv=None, tail=False, fastmath=())

Call function *fn* with arguments *args* (a sequence of values).
*cconc* is the optional calling convention. *tail*, if true, is
a hint for the optimizer to perform tail-call optimization.
a hint for the optimizer to perform tail-call optimization. *fastmath* is a
string or a sequence of strings of names for `fast-math flags
<http://llvm.org/docs/LangRef.html#fast-math-flags>`_.


Branches
Expand Down
4 changes: 2 additions & 2 deletions llvmlite/ir/builder.py
Expand Up @@ -719,13 +719,13 @@ def resume(self, landingpad):

# Call APIs

def call(self, fn, args, name='', cconv=None, tail=False):
def call(self, fn, args, name='', cconv=None, tail=False, fastmath=()):
"""
Call function *fn* with *args*:
name = fn(args...)
"""
inst = instructions.CallInstr(self.block, fn, args, name=name,
cconv=cconv, tail=tail)
cconv=cconv, tail=tail, fastmath=fastmath)
self._insert(inst)
return inst

Expand Down
41 changes: 28 additions & 13 deletions llvmlite/ir/instructions.py
Expand Up @@ -55,12 +55,18 @@ class CallInstrAttributes(AttributeSet):
_known = frozenset(['noreturn', 'nounwind', 'readonly', 'readnone'])


class FastMathFlags(AttributeSet):
_known = frozenset(['fast', 'nnan', 'ninf', 'nsz', 'arcp'])


class CallInstr(Instruction):
def __init__(self, parent, func, args, name='', cconv=None, tail=False):
def __init__(self, parent, func, args, name='', cconv=None, tail=False,
fastmath=()):
self.cconv = (func.calling_convention
if cconv is None and isinstance(func, Function)
else cconv)
self.tail = tail
self.fastmath = FastMathFlags(fastmath)
self.attributes = CallInstrAttributes()

# Fix and validate arguments
Expand Down Expand Up @@ -105,24 +111,33 @@ def called_function(self):
def _descr(self, buf, add_metadata):
args = ', '.join(['{0} {1}'.format(a.type, a.get_reference())
for a in self.args])
fnty = self.callee.type
if isinstance(fnty, types.PointerType):
fnty = fnty.pointee
callee_ref = "{0} {1}".format(fnty, self.callee.get_reference())

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)
buf.append("{0}{1} {2}({3}){4}{5}\n".format(
'tail ' if self.tail else '',
self.opname,
callee_ref,
args,
''.join([" " + attr for attr in self.attributes]),
self._stringify_metadata(leading_comma=True) if add_metadata else "",
))
buf.append("{tail}{op}{fastmath} {callee}({args}){attr}{meta}\n".format(
tail='tail ' if self.tail else '',
op=self.opname,
callee=callee_ref,
fastmath=''.join([" " + attr for attr in self.fastmath]),
args=args,
attr=''.join([" " + attr for attr in self.attributes]),
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):
assert isinstance(normal_to, Block)
Expand Down
21 changes: 19 additions & 2 deletions llvmlite/ir/values.py
Expand Up @@ -505,12 +505,29 @@ def descr(self, buf):


class AttributeSet(set):
"""A set of string attribute.
Only accept items listed in *_known*.
Properties:
* Iterate in sorted order
"""
_known = ()

def __init__(self, args=()):
if isinstance(args, str):
args = [args]
for name in args:
self.add(name)

def add(self, name):
assert name in self._known
if name not in self._known:
raise ValueError('unknown attr {!r} for {}'.format(name, self))
return super(AttributeSet, self).add(name)

def __iter__(self):
# In sorted order
return iter(sorted(super(AttributeSet, self).__iter__()))


class FunctionAttributes(AttributeSet):
_known = frozenset([
Expand Down Expand Up @@ -546,7 +563,7 @@ def personality(self, val):
self._personality = val

def __repr__(self):
attrs = sorted(self)
attrs = list(self)
if self.alignstack:
attrs.append('alignstack({0:d})'.format(self.alignstack))
if self.personality:
Expand Down
28 changes: 16 additions & 12 deletions llvmlite/tests/test_ir.py
Expand Up @@ -597,12 +597,12 @@ def test_binops_with_overflow(self):
builder.usub_with_overflow(a, b, 'h')
self.check_block(block, """\
my_block:
%"c" = call {i32, i1} (i32, i32) @"llvm.sadd.with.overflow.i32"(i32 %".1", i32 %".2")
%"d" = call {i32, i1} (i32, i32) @"llvm.smul.with.overflow.i32"(i32 %".1", i32 %".2")
%"e" = call {i32, i1} (i32, i32) @"llvm.ssub.with.overflow.i32"(i32 %".1", i32 %".2")
%"f" = call {i32, i1} (i32, i32) @"llvm.uadd.with.overflow.i32"(i32 %".1", i32 %".2")
%"g" = call {i32, i1} (i32, i32) @"llvm.umul.with.overflow.i32"(i32 %".1", i32 %".2")
%"h" = call {i32, i1} (i32, i32) @"llvm.usub.with.overflow.i32"(i32 %".1", i32 %".2")
%"c" = call {i32, i1} @"llvm.sadd.with.overflow.i32"(i32 %".1", i32 %".2")
%"d" = call {i32, i1} @"llvm.smul.with.overflow.i32"(i32 %".1", i32 %".2")
%"e" = call {i32, i1} @"llvm.ssub.with.overflow.i32"(i32 %".1", i32 %".2")
%"f" = call {i32, i1} @"llvm.uadd.with.overflow.i32"(i32 %".1", i32 %".2")
%"g" = call {i32, i1} @"llvm.umul.with.overflow.i32"(i32 %".1", i32 %".2")
%"h" = call {i32, i1} @"llvm.usub.with.overflow.i32"(i32 %".1", i32 %".2")
""")

def test_unary_ops(self):
Expand Down Expand Up @@ -1017,12 +1017,16 @@ def test_call(self):
builder.call(f, (a, b), 'res_f_fast', cconv='fastcc')
res_f_readonly = builder.call(f, (a, b), 'res_f_readonly')
res_f_readonly.attributes.add('readonly')
builder.call(f, (a, b), 'res_fast', fastmath='fast')
builder.call(f, (a, b), 'res_nnan_ninf', fastmath=('nnan', 'ninf'))
self.check_block(block, """\
my_block:
%"res_f" = call float (i32, i32) @"f"(i32 %".1", i32 %".2")
%"res_f" = call float @"f"(i32 %".1", i32 %".2")
%"res_g" = call double (i32, ...) @"g"(i32 %".2", i32 %".1")
%"res_f_fast" = call fastcc float (i32, i32) @"f"(i32 %".1", i32 %".2")
%"res_f_readonly" = call float (i32, i32) @"f"(i32 %".1", i32 %".2") readonly
%"res_f_fast" = call fastcc float @"f"(i32 %".1", i32 %".2")
%"res_f_readonly" = call float @"f"(i32 %".1", i32 %".2") readonly
%"res_fast" = call fast float @"f"(i32 %".1", i32 %".2")
%"res_nnan_ninf" = call ninf nnan float @"f"(i32 %".1", i32 %".2")
""")

def test_call_metadata(self):
Expand All @@ -1039,7 +1043,7 @@ def test_call_metadata(self):
self.check_block(block, """\
my_block:
%"a" = alloca i32
call void (metadata, metadata, metadata) @"llvm.dbg.declare"(metadata i32* %"a", metadata !0, metadata !0)
call void @"llvm.dbg.declare"(metadata i32* %"a", metadata !0, metadata !0)
""")

def test_invoke(self):
Expand All @@ -1053,7 +1057,7 @@ def test_invoke(self):
builder.invoke(f, (a, b), bb_normal, bb_unwind, 'res_f')
self.check_block(block, """\
my_block:
%"res_f" = invoke float (i32, i32) @"f"(i32 %".1", i32 %".2")
%"res_f" = invoke float @"f"(i32 %".1", i32 %".2")
to label %"normal" unwind label %"unwind"
""")

Expand Down Expand Up @@ -1084,7 +1088,7 @@ def test_assume(self):
self.check_block(block, """\
my_block:
%"c" = icmp sgt i32 %".1", %".2"
call void (i1) @"llvm.assume"(i1 %"c")
call void @"llvm.assume"(i1 %"c")
""")


Expand Down

0 comments on commit 3b10581

Please sign in to comment.