LLVMSharp icon indicating copy to clipboard operation
LLVMSharp copied to clipboard

BuildCall2 fails with assertion error when passing variadic arguments to printf

Open larkliy opened this issue 9 months ago • 3 comments

Description

When trying to use BuildCall2 to call the printf function, I'm encountering an assertion error related to type mismatch in variadic arguments. The error occurs specifically when passing an integer value as a variadic argument to printf.

Error Message

Assertion failed: (i >= FTy->getNumParams() || FTy->getParamType(i) == Args[i]->getType()) && "Calling a function with a bad signature!", file C:\src\llvm_package_6923b0a7\llvm-project\llvm\lib\IR\Instructions.cpp, line 455

Code Example

Here's a simplified version of my code that reproduces the issue:

public LLVMValueRef VisitPrintStatement(CodeParser.PrintStatementContext context)
{
    string variableName = context.ID().GetText();
    if (!_variables.TryGetValue(variableName, out var variable))
    {
        throw new Exception($"Variable {variableName} is not defined.");
    }

    // Load integer value from allocated memory
    var varType = variable.TypeOf.ElementType;
    var value = Builder.BuildLoad2(varType, variable, $"{variableName}_value");
    
    // Create format string for printf
    string formatStr = "%d\n";
    var formatStrPtr = Builder.BuildGlobalStringPtr(formatStr, "formatStr");
    
    // Define printf function
    var printfType = LLVMTypeRef.CreateFunction(LLVMTypeRef.Int32, [LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)], true);
    var func = GetOrInsertFunction("printf", printfType);
    
    // Call printf - this line causes the assertion error
    var call = Builder.BuildCall2(func.TypeOf, func, [formatStrPtr, value]);
    
    return call;
}

Debugging Information

I've examined the types right before the assertion fails:

  • printfType = {i32 (i8*, ...)}
  • func = {declare i32 @printf(i8*, ...)}
  • formatStrPtr = {i8* getelementptr inbounds ([4 x i8], [4 x i8]* @formatStr, i32 0, i32 0)}
  • args[0] = {i8* getelementptr inbounds ([4 x i8], [4 x i8]* @formatStr, i32 0, i32 0)}
  • args[1] = { %value1_value = load i32, i32* %value1, align 4}

Expected Behavior

I expect BuildCall2 to properly handle variadic functions and their arguments, especially for common use cases like printf with integer arguments.

Environment

  • LLVMSharp version: Latest in nuget.
  • .NET version: .NET 9
  • OS: Windows

Any guidance would be greatly appreciated!

larkliy avatar Mar 22 '25 11:03 larkliy

Still happen on the latest release? https://github.com/dotnet/LLVMSharp/releases/tag/v20.1.2

ds5678 avatar May 29 '25 19:05 ds5678

Still happen on the latest release? https://github.com/dotnet/LLVMSharp/releases/tag/v20.1.2

I haven't used this library since its error:0

larkliy avatar May 30 '25 13:05 larkliy

After experiencing the same issue, found the root cause: LLVMModuleRef.AddFunction(...) returns a pointer to the function. So calling LLVMValueRef.TypeOf, predictably, returned the {ptr} type instead of the function type. Looking at the C API (https://llvm.org/doxygen/IR_2Core_8cpp_source.html), there's no way to get the function pointer from the LLVMValueRef, so I stored it in a separate Dictionary.

In your example, passing printfType instead of func.TypeOf should do the trick.

jorgestg avatar Jun 27 '25 07:06 jorgestg