llvm-gcc miscompilation or it's the gcc's rule?

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

llvm-gcc miscompilation or it's the gcc's rule?

Zhou Sheng
Hi,

Here is C function:

uint64_t mul(uint32_t x, uint32_t y) {
  return x * y;
}

current llvm-gcc-4.0 with -O3 will compile it to:

define i64 @mul(i32 %x, i32 %y) nounwind  {
entry:
    %tmp3 = mul i32 %y, %x      ; <i32> [#uses=1]
    %tmp34 = zext i32 %tmp3 to i64      ; <i64> [#uses=1]
    ret i64 %tmp34
}

This seems incorrect. I think it should extend %x, %y to i64 first and then do the multiplication stuff.
Otherwise, the result may lose some bits if %x, %y are very big.

gcc seems have the same issue. Is this a bug or just gcc's rule?


Thanks,
Sheng.

_______________________________________________
LLVM Developers mailing list
[hidden email]         http://llvm.cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
Reply | Threaded
Open this post in threaded view
|

Re: llvm-gcc miscompilation or it's the gcc's rule?

Rob Barris-2

I don't think C has a way to express 32b x 32b -> 64b multiply, even  
though there is (on x86 anyway) a hardware instruction that does it.

The type of your expression (x * y) is still uint32_t.  The implicit  
type coercion up to uint64_t as part of the return statement doesn't  
change this.

On Jan 13, 2008, at 10:29 PM, Zhou Sheng wrote:

> Hi,
>
> Here is C function:
>
> uint64_t mul(uint32_t x, uint32_t y) {
>   return x * y;
> }
>
> current llvm-gcc-4.0 with -O3 will compile it to:
>
> define i64 @mul(i32 %x, i32 %y) nounwind  {
> entry:
>     %tmp3 = mul i32 %y, %x      ; <i32> [#uses=1]
>     %tmp34 = zext i32 %tmp3 to i64      ; <i64> [#uses=1]
>     ret i64 %tmp34
> }
>
> This seems incorrect. I think it should extend %x, %y to i64 first  
> and then do the multiplication stuff.
> Otherwise, the result may lose some bits if %x, %y are very big.
>
> gcc seems have the same issue. Is this a bug or just gcc's rule?
>
>
> Thanks,
> Sheng.
> _______________________________________________
> LLVM Developers mailing list
> [hidden email]         http://llvm.cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev

_______________________________________________
LLVM Developers mailing list
[hidden email]         http://llvm.cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
Reply | Threaded
Open this post in threaded view
|

Re: llvm-gcc miscompilation or it's the gcc's rule?

Chris Lattner
On Jan 13, 2008, at 10:35 PM, Rob Barris wrote:
> I don't think C has a way to express 32b x 32b -> 64b multiply, even
> though there is (on x86 anyway) a hardware instruction that does it.
>
> The type of your expression (x * y) is still uint32_t.  The implicit
> type coercion up to uint64_t as part of the return statement doesn't
> change this.

Right. llvm-gcc and GCC are correct.  If you want this, use:

uint64_t mul(uint32_t x, uint32_t y) {
  return (uint64_t )x * y;
}

Your code generator should handle optimizing this to the simple  
instruction if your target has an instruction to do this.  For  
example, the X86 backend produces:

_mul:
        movl 8(%esp), %eax
        mull 4(%esp)
        ret

-Chris
_______________________________________________
LLVM Developers mailing list
[hidden email]         http://llvm.cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev
Reply | Threaded
Open this post in threaded view
|

Re: llvm-gcc miscompilation or it's the gcc's rule?

Morten Ofstad
In reply to this post by Zhou Sheng
Zhou Sheng wrote:

> Hi,
>
> Here is C function:
>
> uint64_t mul(uint32_t x, uint32_t y) {
>   return x * y;
> }
>
> current llvm-gcc-4.0 with -O3 will compile it to:
>
> define i64 @mul(i32 %x, i32 %y) nounwind  {
> entry:
>     %tmp3 = mul i32 %y, %x      ; <i32> [#uses=1]
>     %tmp34 = zext i32 %tmp3 to i64      ; <i64> [#uses=1]
>     ret i64 %tmp34
> }
>
> This seems incorrect. I think it should extend %x, %y to i64 first and
> then do the multiplication stuff.
> Otherwise, the result may lose some bits if %x, %y are very big.
>
> gcc seems have the same issue. Is this a bug or just gcc's rule?

This is not a bug, it's following the C standard. The multiplication of two uint32 is performed, computing an uint32
result and this result is then returned from the function which causes it to be promoted to uint64. Your code is
equivalent to this:

uint64_t mul(uint32_t x, uint32_t y) {
   uint32_t result = x * y;
   return (uint64_t) result;
}

while what you wanted was this:

uint64_t mul(uint32_t x, uint32_t y) {
   return (uint64_t)x * (uint64_t)y;
}

m.
_______________________________________________
LLVM Developers mailing list
[hidden email]         http://llvm.cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/llvmdev