Incompatible types at call site

Hi,

For a call like this,

%tmp6 = call i32 (…)* bitcast (i32 (i8*, i8, i8**)* @ssplit to i32 (…))(i8 %tmp599, i32 46, i8** %domainv3) nounwind ;

does the 2nd argument get zero extended or sign extended?

Thanks,
Arushi

Hi Arushi,

For a call like this,

%tmp6 = call i32 (...)* bitcast (i32 (i8*, i8, i8**)* @ssplit to i32 (...)*)(i8*
%tmp599, i32 46, i8** %domainv3) nounwind ; <i32>

does the 2nd argument get zero extended or sign extended?

neither since it does not have the zext or sext attribute.

Ciao, Duncan.

Since you're passing an i32 to a function expecting an i8 I'd actually
expect it to get truncated.

However, other outcomes are possible. For example, on 16-bit
machines[1], this might actually cause the parameters to fail to line
up, so while the second parameter would likely be either 0 or 46
there's no telling what the *third* parameter would wind up being.
(I'm not sure if LLVM actually supports any such machines though)

[1]: In this case meaning machines where a C 'int' is 16 bits, since
this depends on the C calling convention for the machine in question.

%tmp63 = call %struct.TypHeader* (…)* bitcast (%struct.TypHeader* (%struct.TypHeader*, i64, i64)* @Cyclotomic to %struct.TypHeader* (…))(%struct.TypHeader %tmp62, i64 %tmp24, i32 1) nounwind, !dbg !907 ; <%struct.TypHeader*> [#uses=1]

the 3rd parameter is now used in an srem statement. How do we know what value is used? Does this use decide whether the value is sign extended or zero extended?

Arushi

Hi Arushi,

  %tmp63 = call %struct.TypHeader* (...)* bitcast (%struct.TypHeader*
(%struct.TypHeader*, i64, i64)* @Cyclotomic to %struct.TypHeader*
(...)*)(%struct.TypHeader* %tmp62, i64 %tmp24, i32 1) nounwind, !dbg !907 ;
<%struct.TypHeader*> [#uses=1]

the 3rd parameter is now used in an srem statement. How do we know what value is
used? Does this use decide whether the value is sign extended or zero extended?

inside the called function the 3rd value will contain rubbish. That's because
the function takes an i64 parameter but via the bitcast you pretend it takes an
i32 parameter, which is wrong. This is not the correct way to pass an i32 to a
function that takes an i64. Where did you get this IR from?

Ciao, Duncan.

PS: It may not contain rubbish in practice, in particular probably the lower 32
bits will have the "right" value, but nothing guarantees that.

Hi Arushi,

%tmp63 = call %struct.TypHeader* (…)* bitcast (%struct.TypHeader*
(%struct.TypHeader*, i64, i64)* @Cyclotomic to %struct.TypHeader*
(…))(%struct.TypHeader %tmp62, i64 %tmp24, i32 1) nounwind, !dbg !907 ;
<%struct.TypHeader*> [#uses=1]

the 3rd parameter is now used in an srem statement. How do we know what value is
used? Does this use decide whether the value is sign extended or zero extended?

inside the called function the 3rd value will contain rubbish. That’s because
the function takes an i64 parameter but via the bitcast you pretend it takes an
i32 parameter, which is wrong. This is not the correct way to pass an i32 to a
function that takes an i64. Where did you get this IR from?

This is unoptimized IR, generated for a SPEC example.

The optimized IR, converts this call to

%tmp63 = call%struct.TypHeader*
(%struct.TypHeader*, i64, i64)* @Cyclotomic (%struct.TypHeader* %tmp62, i64 %tmp24, i64 1) nounwind
<%struct.TypHeader*> [#uses=1]

I was just wondering what the logic was to infer when such conversions could be made.

Unoptimized IR

%tmp63 = call %struct.TypHeader* (…)* bitcast (%struct.TypHeader*
(%struct.TypHeader*, i64, i64)* @Cyclotomic to %struct.TypHeader*
(…))(%struct.TypHeader %tmp62, i64 %tmp24, i32 1) nounwind, !dbg !907 ;
<%struct.TypHeader*> [#uses=1]

Optimized IR

%tmp63 = call%struct.TypHeader*
(%struct.TypHeader*, i64, i64)* @Cyclotomic (%struct.TypHeader* %tmp62, i64 %tmp24, i64 1) nounwind
<%struct.TypHeader*> [#uses=1]

The way InstCombine manages this, is (from InstCombineCalls.cpp:1039)

if ((*AI)->getType() == ParamTy) {
Args.push_back(*AI);
} else {
Instruction::CastOps opcode = CastInst::getCastOpcode(*AI,
false, ParamTy, false);
Args.push_back(Builder->CreateCast(opcode, *AI, ParamTy, “tmp”));
}

When it asks for the castOpcode, it assumes both types are unsigned(indicated by the false arguments).

Is it correct to assume this?

Thanks,
Arushi

Hi Arushi,

When it asks for the castOpcode, it assumes both types are unsigned(indicated by
the false arguments).

Is it correct to assume this?

yes, because the behaviour of the original code was undefined.

Ciao, Duncan.

PS: This is the sort of thing that happens when a function is declared with a
prototype such as
   void *Cyclotomic(void *, long, int)
but the function actually has a different prototype, for example
   void *Cyclotomic(void *, long, long)
and the function is called from a place that has only seen the wrong prototype.
In short, this is usually a bug in the original code. Confusion between int and
long is common, since on 32 bit platforms they are the same but differ on 64 bit
platforms. Of course it could also be a compiler bug. You got this from
compiling Fortran with dragonegg, right? What was the original code?

I got this from C code compiled by llvm-gcc.

There is a consistent prototype for the function

TypHandle Cyclotomic ( hdRes, n, m )
TypHandle hdRes;
long n, m;

the call looks as follows,
hdI = ProdCyc( hdI, Cyclotomic( HdResult, n, 1 ) );

which is basically

Cyclotomic( HdResult, n, 1 );

The problem is the fact that it interprets 1 as an int32 instead of an int64. And later converts it by assuming an unsigned to unsigned cast.

Arushi

Hi Arushi,

I got this from C code compiled by llvm-gcc.

There is a consistent prototype for the function

TypHandle Cyclotomic ( hdRes, n, m )
     TypHandle hdRes;
     long n, m;

the call looks as follows,
  hdI = ProdCyc( hdI, Cyclotomic( HdResult, n, 1 ) );

I bet the call occurs before the function is defined. In this case C treats
the callee as being a varargs function (...) of the kind you see in your IR.

Ciao, Duncan.

Hi Arushi,

I got this from C code compiled by llvm-gcc.

There is a consistent prototype for the function

TypHandle Cyclotomic ( hdRes, n, m )
TypHandle hdRes;
long n, m;

the call looks as follows,
hdI = ProdCyc( hdI, Cyclotomic( HdResult, n, 1 ) );

I bet the call occurs before the function is defined. In this case C treats
the callee as being a varargs function (…) of the kind you see in your IR.

Unfortunately that is’t true either :frowning:

Also, the calls which do not pass a constant int, do not get the cast on the function.

Hi Arushi,

    Hi Arushi,

        I got this from C code compiled by llvm-gcc.

        There is a consistent prototype for the function

        TypHandle Cyclotomic ( hdRes, n, m )
             TypHandle hdRes;
             long n, m;

        the call looks as follows,
          hdI = ProdCyc( hdI, Cyclotomic( HdResult, n, 1 ) );

    I bet the call occurs before the function is defined. In this case C treats
    the callee as being a varargs function (...) of the kind you see in your IR.

Unfortunately that is't true either :frowning:

something along these lines is clearly occurring. I suggest you open a bug
report with preprocessed source.

Also, the calls which do not pass a constant int, do not get the cast on the
function.

Probably because they have a type, which the compiler can use, while a constant
like "1" gets a default type like i32.

Ciao, Duncan.

Hi Arushi,

I got this from C code compiled by llvm-gcc.

There is a consistent prototype for the function

TypHandle Cyclotomic ( hdRes, n, m )
     TypHandle hdRes;
     long n, m;

I just remembered that Kernighan and Ritchie style declarations like this don't
have the semantics you might expect. In particular this is not equivalent to
saying
   TypHandle Cyclotomic ( TypHandle hdRes, long n, long m )
If you change the declaration to the previous line, does the nasty bitcasting
go away?

Ciao, Duncan.

That does solve the problem of the generated code.

Thanks a lot.

Arushi