Open ceautery opened 8 years ago
The problem is that wasm %
can trap (on corner cases) while asm.js %
cannot. But if you build with asm2wasm --imprecise
it will ignore the corner cases, and do what you want here.
I am surprised about that browser error you saw, though. Do you have a testcase + full steps to reproduce?
@kripken - I'm running 64 bit Chrome Canary 54.0.2827.0 with chrome://flags/#enable-webassembly enabled, and a local web server. To reproduce what I'm seeing...
Create test.asm.js with the following content:
function TestModule() {
"use asm";
function test(n) {
n = n | 0;
return n % 2
}
return { test: test }
}
Convert to S-expr syntax, and assemble with:
binaryen/bin/asm2wasm test.asm.js -o test.wast
binaryen/bin/wasm-as test.wast -o test.wasm
Place test.wasm in web server's root directory. Create test.html with this content:
<!doctype html>
<html>
<head>
<script>
var module;
fetch("test.wasm")
.then(resp => resp.arrayBuffer())
.then(buffer => module = Wasm.instantiateModule(new Uint8Array(buffer)));
</script>
</head>
<body>
</body>
</html>
Navigate to localhost(:port)/test.html, then open the JavaScript console. The complete error I'm seeing is:
Uncaught (in promise) WasmModule::Instantiate(): Import #0 module="asm2wasm" function="i32s-rem" error: FFI is not an object
Running asm2wasm with -i, as you suggested, corrected the import error. Thanks for the reply and the suggestion!
That looks like invalid asm.js. You must coerce on a %
because otherwise it can't tell if it's a signed or unsigned operation.
I didn't realize you were using handwritten asm.js here. asm2wasm is only tested on emscripten output, so it's possible you'll find bugs, although in this case, I'm not sure yet.
Ah. I'll conjecture emscripten would have also created the $i32s-rem import... somewhere, so this likely isn't a bug, as you said.
asm2wasm handles handwritten asm.js code liberally. I've had success using much fewer type coercions than the spec would suggest you need. In general, once a variable's type has been referenced in a function, you seem pretty free to operate on it willy-nilly with variables of the same type. Here's an example, from my (amateur - I've been playing with asm.js for just a few days) attempts at writing various prime number calculators, where I only needed to include the -i flag on asm2wasm, and wasm-as didn't complain about at all, which ran fine in a browser:
function PrimeModule() {
"use asm";
var HEAP8 = new global.Int8Array(buffer);
var HEAP32 = new global.Int32Array(buffer);
var sqrt = global.Math.sqrt;
function sieve(max) {
max = max | 0;
var sieveOffset = 0, upperLimit = 0, i = 0, outputOffset = 0;
upperLimit = ~~sqrt(+max);
for (sieveOffset = 2; sieveOffset <= upperLimit; sieveOffset = sieveOffset + 1) {
if (HEAP8[sieveOffset >> 0] == 0) {
for (i = sieveOffset * sieveOffset; i <= max; i = i + sieveOffset) {
HEAP8[i >> 0] = 1;
}
}
}
outputOffset = max + ((4 - max % 4) % 4);
for (i = 2; i <= max; i = i + 1) {
if (HEAP8[i >> 0] == 0) {
HEAP32[outputOffset >> 2] = i;
outputOffset = outputOffset + 4;
}
}
return (outputOffset - max - ((4 - max % 4) % 4)) >> 2;
}
return { getPrimes: sieve };
}
...and...
function PrimeModule() {
"use asm";
var HEAP32 = new global.Int32Array(buffer);
function getPrimes(max) {
max = max | 0;
var byteOffset = 0, candidate = 0, limit = 0, square = 0;
candidate = 3;
limit = 2;
square = 4;
HEAP32[byteOffset >> 2] = 2; // First prime
while (candidate < max) {
if ((isPrime(candidate, byteOffset, limit) | 0) == 1) {
byteOffset = byteOffset + 4;
HEAP32[byteOffset >> 2] = candidate;
}
candidate = candidate + 2;
if (candidate >= square) {
square = square + (limit * 2) + 1;
limit = limit + 1;
}
}
return (byteOffset >> 2) + 1;
}
function isPrime(candidate, stopByteOffset, limit) {
candidate = candidate | 0;
stopByteOffset = stopByteOffset | 0;
limit = limit | 0;
var primeDivisor = 0, byteOffset = 0;
byteOffset = 4;
while (byteOffset <= stopByteOffset) {
primeDivisor = HEAP32[byteOffset >> 2];
if (primeDivisor > limit) break;
if (candidate % primeDivisor == 0) return 0;
byteOffset = byteOffset + 4;
}
return 1;
}
return { getPrimes: getPrimes };
}
Note the general lack of any coercions after variables are declared.
Anyway, thanks for your time. I didn't realize on the outset that these tools were specifically meant to be used in conjunction with emscripten... and they've turned out to be quite usable directly.
It would be great if it did work on all asm.js, just I've only had time for emscripten output ;) Fixes for non-emscripten output would be welcome of course.
The asm.js line:
...where "val" and "i" are both ints, gets translated as an external import in the header and a call to that import:
and then
This compiles fine with wasm-as, but fails in the browser with an instantiation error of
If I edit the asm2wasm output to remove the imports and swap the call_import line with:
...wasm-as compiles it fine, and the module works as expected in the browser. Can asm2wasm use the built-in S expression i32.rem_s for integer modulus operations, or is there an overriding reason to stick with the external call? If so, how do I reference the import from JavaScript?
Thanks!