ecmascript_simd
ecmascript_simd copied to clipboard
Bitwise conversions doesn't preserve the bits in the polyfill for Firefox
When doing bitwise conversion of int32x4->float32x4->int32x4 the bits aren't preserved for Firefox, when NaN patterns are present. This code:
var i4 = SIMD.int32x4(0x7fc00000,0x7fe00000,0x7ff00000,0x7ff80000);
var i4c = SIMD.int32x4.fromFloat32x4Bits(SIMD.float32x4.fromInt32x4Bits(i4));
results in i4c != i4. All lanes will be converted to the 'common' NaN representation (0x7fc00000). The reason is that the individual lanes on a SIMD.float32x4 are canonicalized when extracted into the .x_, .y_, .z_, and .w_ properties respectively.
I experimented a bit with an alternative representation, where the lane values are held in a typed array instead of individual Number objects. The following full example shows the alternative respresentation.
I think it's important to keep the polyfill as close to the VM implementation as possible, even if it results in a performance drop.
<!doctype html>
<html>
<body>
<script src="ecmascript_simd.js"></script>
<script>
var SIMD_NEW = {};
SIMD_NEW.int32x4 = function (x, y, z, w) {
if (!(this instanceof SIMD_NEW.int32x4)) {
return new SIMD_NEW.int32x4(x, y, z, w);
}
if (x instanceof ArrayBuffer) {
this.storage_ = new Int32Array(x.slice(0));
}
else {
this.storage_ = new Int32Array(4);
this.storage_[0] = x;
this.storage_[1] = y;
this.storage_[2] = z;
this.storage_[3] = w;
}
}
SIMD_NEW.float32x4 = function (x, y, z, w) {
if (!(this instanceof SIMD_NEW.float32x4)) {
return new SIMD_NEW.float32x4(x, y, z, w);
}
if (x instanceof ArrayBuffer) {
this.storage_ = new Float32Array(x.slice(0));
}
else {
this.storage_ = new Float32Array(4);
this.storage_[0] = x;
this.storage_[1] = y;
this.storage_[2] = z;
this.storage_[3] = w;
}
}
SIMD_NEW.float32x4.fromInt32x4Bits = function (i4) {
return SIMD_NEW.float32x4(i4.storage_.buffer);
}
SIMD_NEW.int32x4.fromFloat32x4Bits = function (f4) {
return SIMD_NEW.int32x4(f4.storage_.buffer);
}
Object.defineProperty(SIMD_NEW.int32x4.prototype, 'x', {
get: function() { return this.storage_[0]; }
});
Object.defineProperty(SIMD_NEW.int32x4.prototype, 'y', {
get: function() { return this.storage_[1]; }
});
Object.defineProperty(SIMD_NEW.int32x4.prototype, 'z', {
get: function() { return this.storage_[2]; }
});
Object.defineProperty(SIMD_NEW.int32x4.prototype, 'w', {
get: function() { return this.storage_[3]; }
});
function toString(i4) {
return "int32x4(" + i4.x.toString(16) + ", " + i4.y.toString(16) + ", " + i4.z.toString(16) + ", " + i4.w.toString(16) + ")";
}
var i4 = SIMD.int32x4(0x7fc00000,0x7fe00000,0x7ff00000,0x7ff80000);
var i4c = SIMD.int32x4.fromFloat32x4Bits(SIMD.float32x4.fromInt32x4Bits(i4));
var i4_new = SIMD_NEW.int32x4(0x7fc00000,0x7fe00000,0x7ff00000,0x7ff80000);
var i4c_new = SIMD_NEW.int32x4.fromFloat32x4Bits(SIMD_NEW.float32x4.fromInt32x4Bits(i4_new));
alert("ic4 = " + toString(i4c) + "\n" +
"ic4_new = " + toString(i4c_new));
</script>
</body>
</html>
This turns out to be a problem for emulating intrinsics like _mm_cmpeq_ps in <xmmintrin.h> too. Since it's defined to return __m128, which is float32x4, we bitcast the int32x4 result to float32x4, and NaNs get canonicalized, and what was once 0xffffffff is now some other NaN. This breaks users depending on it being all-ones, and it breaks users depending just on having the sign-bit set.