Newbie question: LLVM IR, printf, and varargs

Hi,

I am trying to print two strings using printf. I have tried various things, but keep getting this error:

llc: printf.ll:4:11: error: ‘@printf’ defined with type ‘i32 (i8*, …)*’

%1 = call i32 @printf(i8* null, i8*, i8* null)

The code is:

   declare i32 @printf(i8* nocapture readonly, ...) nounwind

   define i32 @main() nounwind {
      %1 = call i32 @printf(i8* null, i8* null)
      ret i32 0
  }

I am aware that the call will core dump, but I am initially only trying to figure out why LLC won’t accept the call itself. I started out trying with real values and then reduced it to the above to see if I could make it work. Comparing with the output of Clang didn’t help; it does the same - passes in two i8* pointers and declares @printf in the same way (and LLC accepts the Clang output as valid input). The Clang code goes as follows (edited snippet):

@.str = private unnamed_addr constant [11 x i8] c"Error: %s\0A\00", align 1
@.str1 = private unnamed_addr constant [5 x i8] c"Test\00", align 1

define i32 @main() nounwind {

%1 = tail call i32 (i8*, …)* @printf(i8* getelementptr inbounds ([11 x i8]* @.str, i32 0, i32 0), i8* getelementptr inbounds ([5 x i8]* @.str1, i32 0, i32 0)) nounwind
ret i32 0
}

declare i32 @printf(i8* nocapture readonly, …) nounwind

A good thing about this question is that the answer will find its way into the Mapping Highlevel doc, which is why I am asking it in the first place.

This is on Windows using a 32-bit version of LLVM llc v3.4 (built about a week ago).

I searched the LR, the FAQ, and Google but found nothing of relevance.

– Mikael

You're missing the cast, IMHO. The cast appeases the LLVM type checker
w.r.t. mismatching function type

Eli

I just tried adding the cast but it didn’t help. I have the feeling that I am overlooking something very obvious, but I can’t seem to figure out what it is. Thanks for your suggestion, though.

– Mikael

This code:

declare i32 @printf(i8* nocapture readonly, …) nounwind

define i32 @bar(i8* %c, i32 %i) #0 {
entry:
%call = tail call i32 (i8*, …)* @printf(i8* %c, i8* %c)
ret i32 %call
}

Is accepted without complaints by close-to-trunk llc on my Ubuntu machine.

Eli

Whoops… Seems I forgot the asterisk (*) after the cast. Or something. Because I did insert the cast and it didn’t work. But NOW it works. Thank you for spending some time on this - and also for presenting the solution.

– Mikael

Whoops... Seems I forgot the asterisk (*) after the cast. Or something.
Because I did insert the cast and it didn't work. But NOW it works.
Thank you for spending some time on this - and also for presenting the
solution.

It's not a "cast" for any meaning of cast in the language. It's just that
there's a hack, which is that if the type in front of the call is a
function pointer type, then that is used *as the type of the thing being
called*, while if it's not, then it is used *as the return type*, and the
type of the call is inferred from the arguments present on the instruction.
See this chunk of code in lib/AsmParser/LLParser.cpp:

  // If RetType is a non-function pointer type, then this is the short
syntax
  // for the call, which means that RetType is just the return type. Infer
the
  // rest of the function argument types from the arguments that are
present.
  PointerType *PFTy = 0;
  FunctionType *Ty = 0;
  if (!(PFTy = dyn_cast<PointerType>(RetType)) ||
      !(Ty = dyn_cast<FunctionType>(PFTy->getElementType()))) {
    // Pull out the types of all of the arguments...
    std::vector<Type*> ParamTypes;
    for (unsigned i = 0, e = ArgList.size(); i != e; ++i)
      ParamTypes.push_back(ArgList[i].V->getType());

    if (!FunctionType::isValidReturnType(RetType))
      return Error(RetTypeLoc, "Invalid result type for LLVM function");

    Ty = FunctionType::get(RetType, ParamTypes, false);
    PFTy = PointerType::getUnqual(Ty);
  }

-- Sean Silva

That explains why I didn’t need two times “i32”: call i32 i32(i8*, …)* @printf (I actually tried that at one point). But I sort of sensed that I was heading for deep water when I decided to use printf; in all other samples I’ve stuck with puts because of the expected difficulties with varargs functions. Anyways, it is now documented. I do need to rephrase the documentation after this clarification, though.

– Mikael