diff --git a/llvmlite/ir/builder.py b/llvmlite/ir/builder.py index f18a8d8bd..b4958770e 100644 --- a/llvmlite/ir/builder.py +++ b/llvmlite/ir/builder.py @@ -872,14 +872,14 @@ def resume(self, landingpad): # Call APIs def call(self, fn, args, name='', cconv=None, tail=False, fastmath=(), - attrs=()): + 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) + attrs=attrs, arg_attrs=arg_attrs) self._insert(inst) return inst @@ -908,9 +908,11 @@ def store_reg(self, value, reg_type, reg_name, name=''): return self.asm(ftype, "", "{%s}" % reg_name, [value], True, name) def invoke(self, fn, args, normal_to, unwind_to, - name='', cconv=None, tail=False): + name='', cconv=None, fastmath=(), attrs=(), arg_attrs=None): inst = instructions.InvokeInstr(self.block, fn, args, normal_to, - unwind_to, name=name, cconv=cconv) + unwind_to, name=name, cconv=cconv, + fastmath=fastmath, attrs=attrs, + arg_attrs=arg_attrs) self._set_terminator(inst) return inst diff --git a/llvmlite/ir/instructions.py b/llvmlite/ir/instructions.py index 7e82ee032..f337c1586 100644 --- a/llvmlite/ir/instructions.py +++ b/llvmlite/ir/instructions.py @@ -5,7 +5,7 @@ from llvmlite.ir import types from llvmlite.ir.values import (Block, Function, Value, NamedValue, Constant, MetaDataArgument, MetaDataString, AttributeSet, - Undefined) + Undefined, ArgumentAttributes) from llvmlite.ir._utils import _HasMetadata @@ -63,13 +63,20 @@ class FastMathFlags(AttributeSet): class CallInstr(Instruction): def __init__(self, parent, func, args, name='', cconv=None, tail=False, - fastmath=(), attrs=()): + fastmath=(), attrs=(), arg_attrs=None): 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(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) @@ -111,8 +118,13 @@ def called_function(self): return self.callee def _descr(self, buf, add_metadata): - args = ', '.join(['{0} {1}'.format(a.type, a.get_reference()) - for a in self.args]) + def descr_arg(i, a): + if i in self.arg_attributes: + attrs = ' '.join(self.arg_attributes[i]._to_list()) + ' ' + 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 @@ -142,10 +154,12 @@ def descr(self, buf): class InvokeInstr(CallInstr): def __init__(self, parent, func, args, normal_to, unwind_to, name='', - cconv=None): + 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) + 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 diff --git a/llvmlite/tests/test_ir.py b/llvmlite/tests/test_ir.py index e97e528ac..ab5864719 100644 --- a/llvmlite/tests/test_ir.py +++ b/llvmlite/tests/test_ir.py @@ -1181,6 +1181,39 @@ def test_call_metadata(self): call void @"llvm.dbg.declare"(metadata i32* %"a", metadata !0, metadata !0) """) # noqa E501 + def test_call_attributes(self): + block = self.block(name='my_block') + builder = ir.IRBuilder(block) + fun_ty = ir.FunctionType( + ir.VoidType(), (int32.as_pointer(), int32, int32.as_pointer())) + fun = ir.Function(builder.function.module, fun_ty, 'fun') + fun.args[0].add_attribute('sret') + retval = builder.alloca(int32, name='retval') + other = builder.alloca(int32, name='other') + builder.call( + fun, + (retval, ir.Constant(int32, 42), other), + arg_attrs={ + 0: ('sret', 'noalias'), + 2: 'noalias' + } + ) + self.check_block(block, """\ + my_block: + %"retval" = alloca i32 + %"other" = alloca i32 + call void @"fun"(i32* noalias sret %"retval", i32 42, i32* noalias %"other") + """) # noqa E501 + + def test_invalid_call_attributes(self): + block = self.block() + builder = ir.IRBuilder(block) + fun_ty = ir.FunctionType(ir.VoidType(), ()) + fun = ir.Function(builder.function.module, fun_ty, 'fun') + with self.assertRaises(ValueError): + # The function has no arguments, so this should fail. + builder.call(fun, (), arg_attrs={0: 'sret'}) + def test_invoke(self): block = self.block(name='my_block') builder = ir.IRBuilder(block) @@ -1196,6 +1229,39 @@ def test_invoke(self): to label %"normal" unwind label %"unwind" """) + def test_invoke_attributes(self): + block = self.block(name='my_block') + builder = ir.IRBuilder(block) + fun_ty = ir.FunctionType( + ir.VoidType(), (int32.as_pointer(), int32, int32.as_pointer())) + fun = ir.Function(builder.function.module, fun_ty, 'fun') + fun.calling_convention = "fastcc" + fun.args[0].add_attribute('sret') + retval = builder.alloca(int32, name='retval') + other = builder.alloca(int32, name='other') + bb_normal = builder.function.append_basic_block(name='normal') + bb_unwind = builder.function.append_basic_block(name='unwind') + builder.invoke( + fun, + (retval, ir.Constant(int32, 42), other), + bb_normal, + bb_unwind, + cconv='fastcc', + fastmath='fast', + attrs='noinline', + arg_attrs={ + 0: ('sret', 'noalias'), + 2: 'noalias' + } + ) + self.check_block(block, """\ + my_block: + %"retval" = alloca i32 + %"other" = alloca i32 + invoke fast fastcc void @"fun"(i32* noalias sret %"retval", i32 42, i32* noalias %"other") noinline + to label %"normal" unwind label %"unwind" + """) # noqa E501 + def test_landingpad(self): block = self.block(name='my_block') builder = ir.IRBuilder(block)