strange code behavior when non-prototyped user function are called

I noticed a strange behavior when a non-prototyped user-defined function is called.

E.g. for the following test.c code segment:

void foo(void);
void bar(void);

void foo(void){
bar();
}

void bar(void){
printf(“inside bar()”);
}

LLVM-GCC 2.7 -O0 will generate the following code for foo(), which is all fine.

define void @foo() nounwind {
entry:
call void @bar() nounwind
br label %return

return: ; preds = %entry
ret void
}

However, if I comment out the first 2 lines (the prototype declarations), llvm-gcc will generate the following code for foo() instead:
define void @foo() nounwind {
entry:
%0 = call i32 (…)* bitcast (void ()* @bar to i32 (…)*)() nounwind ; [#uses=0]
br label %return

return: ; preds = %entry
ret void
}

Notice there is now a bitcast operator embedded inside the call instruction.

The problem, however, is that the 2nd flavor of LLVM IR breaks my code, which tries to detect all Call Instructions inside all Functions:

if (BitCastInst *BCI = dyn_cast(II)){
errs() <<“find BitCastInst\n”;

}

if (CallInst *CI = dyn_cast(II)){
errs() <<“find CallInst\n”;

}

As a result, neither of the above two cases can trigger.

I am asking how I can detect all such function calls as CallInsts.

Thank you very much

Chuck

Hi Chuck,

However, if I comment out the first 2 lines (the prototype declarations),
llvm-gcc will generate the following code for foo() instead:
define void @foo() nounwind {
entry:
%0 = call i32 (...)* bitcast (void ()* @bar to i32 (...)*)() nounwind ; <i32>
[#uses=0]

in C a prototype like this
   void bar()
or no prototype at all means that bar takes a variable number of arguments
(corresponds to ... in the LLVM type); if there is no prototype then bar
is also considered to return i32.

So in foo, the call to bar is technically a call to a function of type i32
(...). However later the body of bar is output, at which point the type of
@bar is corrected to void (void). This results in a bitcast in the call.
The instcombine pass should clean this up.

Ciao,

Duncan.

Duncan,

Thank you for the reply.

I fully agree with your explanation on how LLVM-GCC deals with user-defined function calls when their respective prototypes are not specified.

I guess it is an internal hack on LLVM-IR when such cases happen, and -instcombine will naturally clean them up.

I think a better option is just use -O2 (or -std-compile-opts), which includes -instcombine as part of its defaults.

As how -instcombine does its work, I will have to read its source code. It is currently a task on my todo list for the weekend. :slight_smile:

Thank you

Chuck

I guess it is an internal hack on LLVM-IR when such cases happen,

It is not an internal hack. This is what C standard says.