Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

255u8.leading_zeros() is wrong on emscripten #39119

Closed
alexcrichton opened this issue Jan 17, 2017 · 6 comments
Closed

255u8.leading_zeros() is wrong on emscripten #39119

alexcrichton opened this issue Jan 17, 2017 · 6 comments
Labels
C-bug Category: This is a bug. O-asmjs Target: asm.js - http://asmjs.org/ O-emscripten Target: 50% off wasm32-unknown-musl. the savings come out of stdio.h, but hey, you get SDL!

Comments

@alexcrichton
Copy link
Member

Running some tests this program is incorrect when run through emscripten currently:

fn main() {                                               
    println!("{}", 255u8.leading_zeros());                
}

This program prints 232 when I'd expect 0. This may be due to the implementation in emscripten-core/emscripten#4544 which looks like:

function _llvm_ctlz_i8(x, isZeroUndef) { 
    x = x | 0;                           
    isZeroUndef = isZeroUndef | 0;       
    return (Math_clz32(x) | 0) - 24 | 0; 
}                                        

(in the compiled JS at least)

x coming in is -1, so Math_clz32(x) returns 0, and then chopping of 24 returns -24. When interpreted as a u8 that's 232 (what we see).

@kripken I'm not familiar with asmjs internals/representations, but is this a bug in the intrinsic? Or perhaps in our own representation of integers along the line? Or did I get unlucky with an LLVM mismatch or something?

@alexcrichton alexcrichton added the O-asmjs Target: asm.js - http://asmjs.org/ label Jan 17, 2017
@kripken
Copy link

kripken commented Jan 17, 2017

It does seem like the asm.js implementation of that intrinsic should be modified to just look at the lower 8 bits, instead of assuming the top 24 are zero. I'll do that. However, I worry there is something more generally wrong here.

I would expect 255u8 to be an llvm i8, which is then sent into the LLVM intrinsic which receives an i8. After compilation to JS, any math operation on it should preserve that only the lower 8 bits are used (since we &255 after e.g. addition etc. - that's why we assume we can look at only the lower 8 bits; but, in certain ffi conditions, that might be false, which is why I want to fix it in emscripten as I said).

But that seems to not be what's happening here. It's apparently sent as 0xffffffff, which means it was sign-extended, despite being unsigned, and despite the intrinsic expecting an i8 so no extension should have been done.. Did that happen on the rust side, llvm side, or emscripten side? Bitcode could help.

@alexcrichton
Copy link
Member Author

@kripken sure yeah, for this rust program:

pub fn main() -> u32 {
    255u8.leading_zeros()
}

I get this IR (unoptimized):

; ModuleID = 'foo.cgu-0.rs'
source_filename = "foo.cgu-0.rs"
target datalayout = "e-p:32:32-i64:64-v128:32:128-n32-S128"
target triple = "asmjs-unknown-emscripten"

; Function Attrs: inlinehint uwtable
define internal i32 @"_ZN4core3num20_$LT$impl$u20$u8$GT$13leading_zeros17h0d6a1b849828bff0E"(i8) unnamed_addr #0 {
entry-block:
  %tmp_ret = alloca i8
  br label %start

start:                                            ; preds = %entry-block
  %1 = call i8 @llvm.ctlz.i8(i8 %0, i1 false)
  store i8 %1, i8* %tmp_ret
  %2 = load i8, i8* %tmp_ret
  br label %bb1

bb1:                                              ; preds = %start
  %3 = zext i8 %2 to i32
  ret i32 %3
}

; Function Attrs: uwtable
define i32 @_ZN3foo4main17ha0ecef40c3578f1eE() unnamed_addr #1 {
entry-block:
  br label %start

start:                                            ; preds = %entry-block
  %0 = call i32 @"_ZN4core3num20_$LT$impl$u20$u8$GT$13leading_zeros17h0d6a1b849828bff0E"(i8 -1)
  br label %bb1

bb1:                                              ; preds = %start
  ret i32 %0
}

; Function Attrs: nounwind readnone
declare i8 @llvm.ctlz.i8(i8, i1) #2

attributes #0 = { inlinehint uwtable }
attributes #1 = { uwtable }
attributes #2 = { nounwind readnone }

I agree that something's getting sign extended, I'm just not sure where :(

kripken added a commit to emscripten-core/emscripten that referenced this issue Jan 17, 2017
…g i8/i16 values - while normally we can assume that the top bits are clear due to how LLVM creates the call, if the call is an ffi or constructed in a non-clang way (see rust-lang/rust#39119) then we must be careful
kripken added a commit to emscripten-core/emscripten that referenced this issue Jan 17, 2017
…g i8/i16 values - while normally we can assume that the top bits are clear due to how LLVM creates the call, if the call is an ffi or constructed in a non-clang way (see rust-lang/rust#39119 - rust emits a negative constant i8 -1 instead of clang's i8 255) then we must be careful
@kripken
Copy link

kripken commented Jan 17, 2017

Great, perfect. So the issue is clang emits i8 255 for such constants, but rust i8 -1, and we just never handled the latter case, and indeed the intrinsics need fixing, but there is no deeper issue hidden here. Fix in emscripten-core/emscripten#4865

@alexcrichton
Copy link
Member Author

Awesome, thanks for the quick fix @kripken!

@brson
Copy link
Contributor

brson commented Jan 18, 2017

Thanks @kripken !

kripken added a commit to emscripten-core/emscripten that referenced this issue Jan 19, 2017
…g i8/i16 values - while normally we can assume that the top bits are clear due to how LLVM creates the call, if the call is an ffi or constructed in a non-clang way (see rust-lang/rust#39119 - rust emits a negative constant i8 -1 instead of clang's i8 255) then we must be careful (#4865)
alexcrichton added a commit to alexcrichton/rust that referenced this issue Jan 19, 2017
This commit adds a new entry to the Travis matrix which will execute emscripten
test suites. Along the way it updates a few bits of the test suite to continue
passing on emscripten, such as:

* Ignoring i128/u128 tests as they're presumably just not working (didn't
  investigate as to why)
* Disabling a few process tests (not working on emscripten)
* Ignore some num tests in libstd (rust-lang#39119)
* Fix some warnings when compiling
alexcrichton added a commit to alexcrichton/rust that referenced this issue Jan 20, 2017
travis: Get an emscripten builder online

This commit adds a new entry to the Travis matrix which will execute emscripten
test suites. Along the way it updates a few bits of the test suite to continue
passing on emscripten, such as:

* Ignoring i128/u128 tests as they're presumably just not working (didn't
  investigate as to why)
* Disabling a few process tests (not working on emscripten)
* Ignore some num tests in libstd (rust-lang#39119)
* Fix some warnings when compiling
@Mark-Simulacrum Mark-Simulacrum added the C-bug Category: This is a bug. label Jul 26, 2017
@alexcrichton
Copy link
Member Author

I believe this has since been fixed

@workingjubilee workingjubilee added the O-emscripten Target: 50% off wasm32-unknown-musl. the savings come out of stdio.h, but hey, you get SDL! label Oct 11, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug. O-asmjs Target: asm.js - http://asmjs.org/ O-emscripten Target: 50% off wasm32-unknown-musl. the savings come out of stdio.h, but hey, you get SDL!
Projects
None yet
Development

No branches or pull requests

5 participants