Different signatures or smart casting?

Hello!
I linked two bitcode files like described below using LTO and find
they are linked fine instead of making an error.

Ksenia Dolgorukova wrote:

Hello!
I linked two bitcode files like described below using LTO and find
they are linked fine instead of making an error.
----------
1.bc
----------
...
define i32 @main(i32 %argc, i8** %argv) #0 {
entry:
...
%call = call i32 @_Z1aj(i64 2)
...
}
declare i32 @_Z1aj(i64) #1
...

----------
2.bc
----------
...
define i32 @_Z1aj(i32 %b) #0 {
entry:
...
}
...

----------

In the first file function "_Z1aj" is declared as having i64 argument,
whereas in the second file the function "_Z1aj" is described as having
i32 argument!

Are function signatures different?

They're both 'a(unsigned int)', but perhaps you compiled one of them targeting a 32-bit platform and the other targeting a 64-bit platform? What triple is at the top of each .bc file?

They both was compiled with clang for 64-bit target. These example
bitcode files was modified by hand to check if this case will work.
Actually, this feature was revealed when I compiled 'coremark'
benchmark with LTO for x68-64 target. I think, different signatures
was caused by preprocessor directives.
But I wander how can it work.

Traditional *nix linkers and object file formats do not carry any type information for functions, only the address of the entry point.

It works because when arguments are passed in registers (as they are for x86_64), everything is passed as the register size (here 64 bit), no matter what size you declared it.

It can also work on machines that pass arguments on the stack, because often there is an alignment requirement on the stack pointer. However, that is more likely to work between i8/i16/i32 than between those and i64. (it depends on the machine)

Hello Ksenia,

Function names in the examples below are mangled names. _Z1aj means
function with name 'a' which accepts one argument of 'unsigned int'
type. The following declaration is correct from LLVM bitcode point of
view but the function name does not match type of the function's
argument.

declare i32 @_Z1aj(i64)

If you compile the following code by g++ you get _Z1ay name because
'unsigned long long' corresponds to 'y' in the mangled name.

int a(unsigned long long);

The linker does not know anything about real type of functions and
uses only mangled names for lookup. That is why the linker
successfully link this code.

In the real case 'void a(unsigned)' converts to the _Z1aj, while 'void
a(unsigned long long)' gets _Z1ay name and linker shows an error.

That is interesting why the code runs without any error. I think that
is because you run it on 64-bit systems and arguments passed by
registers. Probably it fails if you compile it and run on 32-bit
systems.