Casting from float to unsigned char - incorrect output?

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

Casting from float to unsigned char - incorrect output?

ryan baird
I am compiling the following code for the MIPS architecture:

unsigned char trunc(float f) {
        return (unsigned char) f;
}

and it produces the following assembly (directives removed for convenience:
trunc:
    trunc.w.s    $f0, $f12
    mfc1    $2, $f0
    jr    $ra
    nop

However, this does not seem to produce the correct output for negative numbers. When I run the following code, I get -1 instead of 255 (which is produced by compiling natively with gcc).
int trunc(float c);
int main() {
  printf("%d\n", trunc(-1.0));
}

I am running the mips code on a PISA simulator (SimpleScalar's  Simple-Sim 3.0) instead of a MIPS IV simulator, so there is a little bit of translation occurring before I can simulate it; here is the revised code:
trunc:
    cvt.w.s    $f0, $f12
    mfc1    $2, $f0
    jr    $ra
    nop
the cvt.w.s function in PISA MIPS is rounding towards zero, so it should meet the specification for trunc.w.s in MIPS IV.

Here are the commands I used to compile test.c (with the trunc function in it):
clang -emit-llvm -mfloat-abi=hard -ccc-host-triple mipsel-unknown-linux -ccc-clang-archs mipsel -O3 -S -o test_unopt.ll test.c
opt -std-compile-opts test_unopt.ll -o test.ll
llc -march=mipsel -mcpu=mips32 -float-abi=hard -relocation-model=static test.ll -o test.s

Here is the llvm intermediate representation:
; ModuleID = 'test_unopt.ll'
target datalayout = "e-p:32:32:32-i1:8:8-i8:8:32-i16:16:32-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-n32"
target triple = "mipsel-unknown-linux"

define zeroext i8 @trunc(float %f) nounwind readnone {
entry:
  %conv = fptoui float %f to i8
  ret i8 %conv
}

This is the assembly produced by another compiler; it is more complex, but it produces the expected output, matching with GCC: (directives removed for convenience)
trunc:
        mov.s   $f4,$f12
        la      $2,L6
        l.s     $f6,($2)
        c.lt.s  $f12,$f6
        bc1t    .L3
        sub.s   $f0,$f12,$f6
        cvt.w.s $f0,$f0
        mfc1    $2,$f0
        lui     $3,32768
        addu    $3,$2,$3
        j       .L4
.L3:
         cvt.w.s $f4,$f4
         mfc1    $3,$f4
.L4:
        sll     $2,$3,24
        srl     $2,$2,24
        sll     $2,$2,24
        srl     $2,$2,24 
        j       $31
.data
.align 4
L6:
.word 1325400064

Am I correct in my analysis that LLVM's assembly output is wrong?  Is there a way for me to get the correct output?


_______________________________________________
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: Casting from float to unsigned char - incorrect output?

Eli Friedman-2
On Mon, Aug 6, 2012 at 12:43 PM, ryan baird <[hidden email]> wrote:

> I am compiling the following code for the MIPS architecture:
>
> unsigned char trunc(float f) {
>         return (unsigned char) f;
> }
>
> and it produces the following assembly (directives removed for convenience:
> trunc:
>     trunc.w.s    $f0, $f12
>     mfc1    $2, $f0
>     jr    $ra
>     nop
>
> However, this does not seem to produce the correct output for negative
> numbers. When I run the following code, I get -1 instead of 255 (which is
> produced by compiling natively with gcc).
> int trunc(float c);
> int main() {
>   printf("%d\n", trunc(-1.0));
> }

That code has undefined behavior; see 6.3.1.4p1 in C99

-Eli
_______________________________________________
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: Casting from float to unsigned char - incorrect output?

Jim Grosbach
In reply to this post by ryan baird

On Aug 6, 2012, at 12:43 PM, ryan baird <[hidden email]> wrote:

> I am compiling the following code for the MIPS architecture:
>
> unsigned char trunc(float f) {
>         return (unsigned char) f;
> }
>
> and it produces the following assembly (directives removed for convenience:
> trunc:
>     trunc.w.s    $f0, $f12
>     mfc1    $2, $f0
>     jr    $ra
>     nop
>
> However, this does not seem to produce the correct output for negative numbers. When I run the following code, I get -1 instead of 255 (which is produced by compiling natively with gcc).
> int trunc(float c);
> int main() {
>   printf("%d\n", trunc(-1.0));
> }

The prototype for trunc() here and it's definition above don't match. Dunno if that's related to the behavior you're seeing, but it's definitely a bug in the source code.

>
> I am running the mips code on a PISA simulator (SimpleScalar's  Simple-Sim 3.0) instead of a MIPS IV simulator, so there is a little bit of translation occurring before I can simulate it; here is the revised code:
> trunc:
>     cvt.w.s    $f0, $f12
>     mfc1    $2, $f0
>     jr    $ra
>     nop
> the cvt.w.s function in PISA MIPS is rounding towards zero, so it should meet the specification for trunc.w.s in MIPS IV.
>
> Here are the commands I used to compile test.c (with the trunc function in it):
> clang -emit-llvm -mfloat-abi=hard -ccc-host-triple mipsel-unknown-linux -ccc-clang-archs mipsel -O3 -S -o test_unopt.ll test.c
> opt -std-compile-opts test_unopt.ll -o test.ll
> llc -march=mipsel -mcpu=mips32 -float-abi=hard -relocation-model=static test.ll -o test.s
>
> Here is the llvm intermediate representation:
> ; ModuleID = 'test_unopt.ll'
> target datalayout = "e-p:32:32:32-i1:8:8-i8:8:32-i16:16:32-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-n32"
> target triple = "mipsel-unknown-linux"
>
> define zeroext i8 @trunc(float %f) nounwind readnone {
> entry:
>   %conv = fptoui float %f to i8
>   ret i8 %conv
> }
>
> This is the assembly produced by another compiler; it is more complex, but it produces the expected output, matching with GCC: (directives removed for convenience)
> trunc:
>         mov.s   $f4,$f12
>         la      $2,L6
>         l.s     $f6,($2)
>         c.lt.s  $f12,$f6
>         bc1t    .L3
>         sub.s   $f0,$f12,$f6
>         cvt.w.s $f0,$f0
>         mfc1    $2,$f0
>         lui     $3,32768
>         addu    $3,$2,$3
>         j       .L4
> .L3:
>          cvt.w.s $f4,$f4
>          mfc1    $3,$f4
> .L4:
>         sll     $2,$3,24
>         srl     $2,$2,24
>         sll     $2,$2,24
>         srl     $2,$2,24  
>         j       $31
> .data
> .align 4
> L6:
> .word 1325400064
>
> Am I correct in my analysis that LLVM's assembly output is wrong?  Is there a way for me to get the correct output?
>
> _______________________________________________
> 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: Casting from float to unsigned char - incorrect output?

ryan baird
In reply to this post by Eli Friedman-2
I didn't realize the code was undefined, I'll let my professor know; but for code comparison purposes we're still seeking advice on producing the output that matches the other compilers (even if it involves doing a translation on the .ll file or additional translation to the produced assembly).  We can't fairly compare the code if it doesn't do the same thing.  This bug came up in susan from the mibench test suite at the end of setup_brightness_lut where it casts float temp to an unsigned char.  I don't think we can include the results in our report if we change the tests.

Even though the behavior is undefined, it doesn't make sense to me that a function that returns an unsigned char would ever return a 32 bit -1, because that value does not fit the functions return type.


On Mon, Aug 6, 2012 at 2:03 PM, Eli Friedman <[hidden email]> wrote:
On Mon, Aug 6, 2012 at 12:43 PM, ryan baird <[hidden email]> wrote:
> I am compiling the following code for the MIPS architecture:
>
> unsigned char trunc(float f) {
>         return (unsigned char) f;
> }
>
> and it produces the following assembly (directives removed for convenience:
> trunc:
>     trunc.w.s    $f0, $f12
>     mfc1    $2, $f0
>     jr    $ra
>     nop
>
> However, this does not seem to produce the correct output for negative
> numbers. When I run the following code, I get -1 instead of 255 (which is
> produced by compiling natively with gcc).
> int trunc(float c);
> int main() {
>   printf("%d\n", trunc(-1.0));
> }

That code has undefined behavior; see 6.3.1.4p1 in C99

-Eli



_______________________________________________
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: Casting from float to unsigned char - incorrect output?

Richard Smith-33
This should work if your float is between INT_MIN and INT_MAX:

unsigned char trunc(float f) {

  return (unsigned char)(int)f;
}

int main() {
  printf("%d\n", trunc(-1.0));
}

For this, Clang produces:

trunc:
trunc.w.s $f0, $f12
mfc1 $2, $f0
andi $2, $2, 255
jr $ra
nop

On Mon, Aug 6, 2012 at 1:28 PM, ryan baird <[hidden email]> wrote:
I didn't realize the code was undefined, I'll let my professor know; but for code comparison purposes we're still seeking advice on producing the output that matches the other compilers (even if it involves doing a translation on the .ll file or additional translation to the produced assembly).  We can't fairly compare the code if it doesn't do the same thing.  This bug came up in susan from the mibench test suite at the end of setup_brightness_lut where it casts float temp to an unsigned char.  I don't think we can include the results in our report if we change the tests.

Even though the behavior is undefined, it doesn't make sense to me that a function that returns an unsigned char would ever return a 32 bit -1, because that value does not fit the functions return type.


On Mon, Aug 6, 2012 at 2:03 PM, Eli Friedman <[hidden email]> wrote:
On Mon, Aug 6, 2012 at 12:43 PM, ryan baird <[hidden email]> wrote:
> I am compiling the following code for the MIPS architecture:
>
> unsigned char trunc(float f) {
>         return (unsigned char) f;
> }
>
> and it produces the following assembly (directives removed for convenience:
> trunc:
>     trunc.w.s    $f0, $f12
>     mfc1    $2, $f0
>     jr    $ra
>     nop
>
> However, this does not seem to produce the correct output for negative
> numbers. When I run the following code, I get -1 instead of 255 (which is
> produced by compiling natively with gcc).
> int trunc(float c);
> int main() {
>   printf("%d\n", trunc(-1.0));
> }

That code has undefined behavior; see 6.3.1.4p1 in C99

-Eli



_______________________________________________
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