[wasm32 target] Float cast after !! can be wrong when compiled with -x c++

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

[wasm32 target] Float cast after !! can be wrong when compiled with -x c++

Kristof Beyls via cfe-dev
Hi all

With all llvm/clang versions that support the wasm32 target (8, 9, current build of 10) the following function returns the correct 0.0f when compiled with '-x c' but it wrongfully returns 1.0f when compiled with '-x c++'.

    #ifdef __cplusplus
    extern "C"
    #endif
    float test()
    {
        static unsigned int mask = 2;
        mask |= 4;
        return (float)(!!(mask & 1));
    }

Here is how I built it to a wasm module:
    clang -x c -nostdinc -O3 -target wasm32 -c -o test.c.o test.c
    wasm-ld -no-entry -export=test -o test.c.wasm test.c.o

    clang -x c++ -nostdinc -O3 -target wasm32 -c -o test.cpp.o test.c
    wasm-ld -no-entry -export=test -o test.cpp.wasm test.cpp.o

And here is how I run the test in node.js v12
    node -e "WebAssembly.instantiate(new Uint8Array(require('fs').readFileSync('test.c.wasm'))).then(m => { console.log('c: ' + m.instance.exports.test()); })"
    node -e "WebAssembly.instantiate(new Uint8Array(require('fs').readFileSync('test.cpp.wasm'))).then(m => { console.log('cpp: ' + m.instance.exports.test()); })"

The output I get is
    c: 0
    cpp: 1

And this is the disassembly when running it through the wasm2wat tool from wabt:
    wasm2wat -f test.c.wasm
      (func $test (type 0) (result f32)
        (local i32)
        (i32.store offset=1024
          (i32.const 0)
          (i32.and
            (local.tee 0
              (i32.load offset=1024
                (i32.const 0)))
            (i32.const 5)))
        (f32.convert_i32_s
          (i32.and
            (local.get 0)
            (i32.const 1))))

    wasm2wat -f test.cpp.wasm
      (func $test (type 0) (result f32)
        (local i32)
        (i32.store offset=1024
          (i32.const 0)
          (i32.and
            (local.tee 0
              (i32.load offset=1024
                (i32.const 0)))
            (i32.const 5)))
        (select
          (f32.const 0x1p+0 (;=1;))
          (f32.const 0x0p+0 (;=0;))
          (local.get 0)))

The disassembly of the '-x c' module reads basically what the original C code was.
But it looks like the '-x c++' module returns something like (value_of_mask_at_function_entry ? 1 : 0) which makes no sense to me.
If the code line `mask |= 4` is changed into `mask &= 1`, the function returns 1 the first time it is called, then 0 afterwards which matches that assumption (the '-x c' variant stays correct).

Playing with compilation flag variations like -O0 did not affect the result.

Here's the versions I tested with (all on Win64)
  LLD 10.0.0 / clang version 10.0.0 (trunk) Target: x86_64-pc-windows-msvc
  LLD 8.0.0 / clang version 8.0.0 (tags/RELEASE_800/final) Target: x86_64-pc-windows-msvc
  LLD 9.0.0 / clang version 9.0.0 (tags/RELEASE_900/final) Target: x86_64-pc-windows-msvc

They all produced identical assembly as far as I can tell.

Thanks for checking this out, hopefully this is the right place to report this and greetings from Tokyo,
- Bernhard
_______________________________________________
cfe-dev mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
Reply | Threaded
Open this post in threaded view
|

Re: [wasm32 target] Float cast after !! can be wrong when compiled with -x c++

Kristof Beyls via cfe-dev
Hi Bernhard,

Interesting example. `select` instruction returns the operand
corresponding to "1" if the input is not zero, and since the value is 6
that is what getting picked. To avoid that the compiler would need to
still emit "and" instruction in the C++ mode. I think this is a bug, can
you please file it at bugs.llvm.org - assign to me or Thomas Lively.

Note C++ has (unsigned) bool type and in C !! is done on int. IR for C
and C++ variants, respectively (this does not explain the output though):

; Function Attrs: nofree norecurse nounwind
define hidden float @test() local_unnamed_addr #0 {
entry:
   %0 = load i32, i32* @test.mask, align 4, !tbaa !2
   %or = or i32 %0, 4
   store i32 %or, i32* @test.mask, align 4, !tbaa !2
   %and = and i32 %0, 1
   %conv = sitofp i32 %and to float
   ret float %conv
}

; Function Attrs: nofree norecurse nounwind
define hidden float @test() local_unnamed_addr #0 {
entry:
   %0 = load i32, i32* @_ZZ4testE4mask, align 4, !tbaa !2
   %or = or i32 %0, 4
   store i32 %or, i32* @_ZZ4testE4mask, align 4, !tbaa !2
   %and = and i32 %0, 1
   %tobool = icmp ne i32 %and, 0
   %conv = uitofp i1 %tobool to float
   ret float %conv
}

-Petr

On 11/6/19 9:33 AM, Bernhard - via cfe-dev wrote:

> Hi all
>
> With all llvm/clang versions that support the wasm32 target (8, 9, current build of 10) the following function returns the correct 0.0f when compiled with '-x c' but it wrongfully returns 1.0f when compiled with '-x c++'.
>
>      #ifdef __cplusplus
>      extern "C"
>      #endif
>      float test()
>      {
>          static unsigned int mask = 2;
>          mask |= 4;the two possible fp values, but the entries are mixed up
>          return (float)(!!(mask & 1));
>      }
>
> Here is how I built it to a wasm module:
>      clang -x c -nostdinc -O3 -target wasm32 -c -o test.c.o test.c
>      wasm-ld -no-entry -export=test -o test.c.wasm test.c.o
>
>      clang -x c++ -nostdinc -O3 -target wasm32 -c -o test.cpp.o test.c
>      wasm-ld -no-entry -export=test -o test.cpp.wasm test.cpp.o
>
> And here is how I run the test in node.js v12
>      node -e "WebAssembly.instantiate(new Uint8Array(require('fs').readFileSync('test.c.wasm'))).then(m => { console.log('c: ' + m.instance.exports.test()); })"
>      node -e "WebAssembly.instantiate(new Uint8Array(require('fs').readFileSync('test.cpp.wasm'))).then(m => { console.log('cpp: ' + m.instance.exports.test()); })"
>
> The output I get is
>      c: 0
>      cpp: 1
>
> And this is the disassembly when running it through the wasm2wat tool from wabt:
>      wasm2wat -f test.c.wasm
>        (func $test (type 0) (result f32)
>          (local i32)
>          (i32.store offset=1024
>            (i32.const 0)
>            (i32.and
>              (local.tee 0
>                (i32.load offset=1024
>                  (i32.const 0)))
>              (i32.const 5)))
>          (f32.convert_i32_s
>            (i32.and
>              (local.get 0)
>              (i32.const 1))))
>
>      wasm2wat -f test.cpp.wasm
>        (func $test (type 0) (result f32)
>          (local i32)
>          (i32.store offset=1024
>            (i32.const 0)
>            (i32.and
>              (local.tee 0
>                (i32.load offset=1024
>                  (i32.const 0)))
>              (i32.const 5)))
>          (select
>            (f32.const 0x1p+0 (;=1;))
>            (f32.const 0x0p+0 (;=0;))
>            (local.get 0)))
>
> The disassembly of the '-x c' module reads basically what the original C code was.
> But it looks like the '-x c++' module returns something like (value_of_mask_at_function_entry ? 1 : 0) which makes no sense to me.
> If the code line `mask |= 4` is changed into `mask &= 1`, the function returns 1 the first time it is called, then 0 afterwards which matches that assumption (the '-x c' variant stays correct).
>
> Playing with compilation flag variations like -O0 did not affect the result.
>
> Here's the versions I tested with (all on Win64)
>    LLD 10.0.0 / clang version 10.0.0 (trunk) Target: x86_64-pc-windows-msvc
>    LLD 8.0.0 / clang version 8.0.0 (tags/RELEASE_800/final) Target: x86_64-pc-windows-msvc
>    LLD 9.0.0 / clang version 9.0.0 (tags/RELEASE_900/final) Target: x86_64-pc-windows-msvc
>
> They all produced identical assembly as far as I can tell.
>
> Thanks for checking this out, hopefully this is the right place to report this and greetings from Tokyo,
> - Bernhard
> _______________________________________________
> cfe-dev mailing list
> [hidden email]
> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev
_______________________________________________
cfe-dev mailing list
[hidden email]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-dev