Should LLVM::CallOp be able to carry argument attributes for indirect calls?

Hi,

I am working on dealing with the equivalent of this C program in Fortran.

typedef _Complex __float128 (*CplxFunc)(_Complex __float128 z);
void test(CplxFunc p, _Complex __float128 z) {
  p(z);
}

On x86-64, the expected LLVM IR for the p(z) call is:

call void %6(ptr sret({ fp128, fp128 }) align 16 %4, ptr noundef byval({ fp128, fp128 }) align 16 %5)

But in MLIR LLVM dialect, I did not see a way to stick the attributes to the parameters of an indirect call (only CallableOpInterface seems to have parameter attributes).

Would it make sense to optionally add such attributes to the LLVM::CallOp operation?

1 Like

On the general basis, if something is supported by LLVM IR, it should also be supported by the LLVM dialect. So yes, we should add support for argument attributes on indirect calls.

As far as I understand, parameter/argument attributes can appear on CallOp, InvokeOp, as well as on intrinsics (which in LLVM IR are also calls). It looks like call parameter attributes can also appear on direct calls (https://github.com/llvm/llvm-project/blob/088d272e83259a5d8e577a3d2e62012c42a9f9db/llvm/lib/IR/Instructions.cpp#L401). I didn’t find a good explanation in the language ref though.

It looks like call parameter attributes can also appear on direct calls (https://github.com/llvm/llvm-project/blob/088d272e83259a5d8e577a3d2e62012c42a9f9db/llvm/lib/IR/Instructions.cpp#L401). I didn’t find a good explanation in the language ref though.

Thanks for the reply. Interestingly, clang is setting the call instruction argument attributes even for direct call (this can be observed with clang -emit-llvm for the program below). But removing the attributes on the CallOp seems to have no effect on the resulting assembly for direct calls as long as they are on the function declaration (flang does not generate those call attributes for direct calls and this has not been an issue so far).

_Complex __float128 func(_Complex __float128 z);
void test(_Complex __float128 z) {
  func(z);
}

However, the attributes on an llvm call instruction will be applied for direct calls even if they are not on the function declaration. For instance the same assembly is not generated for the two direct calls to the same symbol in the IR below.

define dso_local void @test(ptr noundef byval({ fp128, fp128 }) align 16 %0) {
  %2 = alloca { fp128, fp128 }, align 16
  call void @func(ptr sret({ fp128, fp128 }) align 16 %2, ptr noundef byval({ fp128, fp128 }) align 16 %0)
  ret void
}

define dso_local void @test2(ptr noundef byval({ fp128, fp128 }) align 16 %0) {
  %2 = alloca { fp128, fp128 }, align 16
  call void @func(ptr %2, ptr %0)
  ret void
}

declare void @func(ptr, ptr )

So MLIR LLVM call instructions likely needs to be able to carry argument attributes for both direct and indirect calls to be like LLVM IR (although I also cannot say if it is an intended feature).

So MLIR LLVM call instructions likely needs to be able to carry argument attributes for both direct and indirect calls to be like LLVM IR (although I also cannot say if it is an intended feature).

Yes both direct and indirect calls can have parameter attributes in LLVM and LLVM dialect should probably be able to replicate this. Argument / parameter attributes are quite diverse. While it makes sense to annotate alignment on the call, something like byval makes more sense on the function (for direct calls…). Maybe clang choses to annotate both to be on the safe side…

It is valid to have attributes on both calls and functions. Furthermore, these arguments can be different, which can be useful for localized analyses and optimizations.

As an extreme example that I don’t think LLVM does (because it is not supposed to know about the C standard library):

decl @memove(ptr %0, ptr %1)

def @foo {
  ...
  llvm.call @memmove(ptr noalias %0, ptr noalias %1)
}

this specific memmove can be replaced by a memcpy because we know that pointers don’t alias, but it doesn’t indicate anything for other call sites or the function itself. Attributes are often used to store analysis results in the IR, and there are other opportunities for local optimization or even function specialization thanks to that.