Bug 1069956: SIMD: Add support for int32x4.fromFloat32x4 and float32x4.fromInt32x4 in Odin; r=luke
authorBenjamin Bouvier <benj@benj.me>
Thu, 25 Sep 2014 13:18:08 +0200
changeset 222695 fd0ef779e8a17325af669c6bfb86029c8c57e470
parent 222694 1926709eaf906f6667979a2830d748807e466878
child 222696 c965698b314e116da8d54f5b5e7665ee75e29477
push id7107
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 17:43:31 +0000
treeherdermozilla-aurora@b4b34e0acc75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1069956
milestone35.0a1
Bug 1069956: SIMD: Add support for int32x4.fromFloat32x4 and float32x4.fromInt32x4 in Odin; r=luke
js/src/asmjs/AsmJSLink.cpp
js/src/asmjs/AsmJSModule.h
js/src/asmjs/AsmJSValidate.cpp
js/src/jit-test/tests/asm.js/testSIMD.js
--- a/js/src/asmjs/AsmJSLink.cpp
+++ b/js/src/asmjs/AsmJSLink.cpp
@@ -359,23 +359,25 @@ ValidateSimdOperation(JSContext *cx, Asm
           case AsmJSSimdOperation_or: native = simd_int32x4_or; break;
           case AsmJSSimdOperation_xor: native = simd_int32x4_xor; break;
           case AsmJSSimdOperation_select: native = simd_int32x4_select; break;
           case AsmJSSimdOperation_splat: native = simd_int32x4_splat; break;
           case AsmJSSimdOperation_withX: native = simd_int32x4_withX; break;
           case AsmJSSimdOperation_withY: native = simd_int32x4_withY; break;
           case AsmJSSimdOperation_withZ: native = simd_int32x4_withZ; break;
           case AsmJSSimdOperation_withW: native = simd_int32x4_withW; break;
+          case AsmJSSimdOperation_fromFloat32x4: native = simd_int32x4_fromFloat32x4; break;
           case AsmJSSimdOperation_lessThanOrEqual:
           case AsmJSSimdOperation_greaterThanOrEqual:
           case AsmJSSimdOperation_notEqual:
           case AsmJSSimdOperation_mul:
           case AsmJSSimdOperation_div:
           case AsmJSSimdOperation_max:
           case AsmJSSimdOperation_min:
+          case AsmJSSimdOperation_fromInt32x4:
             MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
                                                     "place");
         }
         break;
       case AsmJSSimdType_float32x4:
         switch (global.simdOperation()) {
           case AsmJSSimdOperation_add: native = simd_float32x4_add; break;
           case AsmJSSimdOperation_sub: native = simd_float32x4_sub; break;
@@ -393,16 +395,20 @@ ValidateSimdOperation(JSContext *cx, Asm
           case AsmJSSimdOperation_or: native = simd_float32x4_or; break;
           case AsmJSSimdOperation_xor: native = simd_float32x4_xor; break;
           case AsmJSSimdOperation_select: native = simd_float32x4_select; break;
           case AsmJSSimdOperation_splat: native = simd_float32x4_splat; break;
           case AsmJSSimdOperation_withX: native = simd_float32x4_withX; break;
           case AsmJSSimdOperation_withY: native = simd_float32x4_withY; break;
           case AsmJSSimdOperation_withZ: native = simd_float32x4_withZ; break;
           case AsmJSSimdOperation_withW: native = simd_float32x4_withW; break;
+          case AsmJSSimdOperation_fromInt32x4: native = simd_float32x4_fromInt32x4; break;
+          case AsmJSSimdOperation_fromFloat32x4:
+             MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("shouldn't have been validated in the first "
+                                                     "place");
         }
         break;
     }
     if (!native || !IsNativeFunction(v, native))
         return LinkFail(cx, "bad SIMD.type.* operation");
     return true;
 }
 
--- a/js/src/asmjs/AsmJSModule.h
+++ b/js/src/asmjs/AsmJSModule.h
@@ -91,17 +91,19 @@ enum AsmJSSimdOperation
     AsmJSSimdOperation_and,
     AsmJSSimdOperation_or,
     AsmJSSimdOperation_xor,
     AsmJSSimdOperation_select,
     AsmJSSimdOperation_splat,
     AsmJSSimdOperation_withX,
     AsmJSSimdOperation_withY,
     AsmJSSimdOperation_withZ,
-    AsmJSSimdOperation_withW
+    AsmJSSimdOperation_withW,
+    AsmJSSimdOperation_fromInt32x4,
+    AsmJSSimdOperation_fromFloat32x4
 };
 
 // These labels describe positions in the prologue/epilogue of functions while
 // compiling an AsmJSModule.
 struct AsmJSFunctionLabels
 {
     AsmJSFunctionLabels(jit::Label &entry, jit::Label &overflowExit)
       : entry(entry), overflowExit(overflowExit) {}
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -1422,17 +1422,19 @@ class MOZ_STACK_CLASS ModuleCompiler
             !addStandardLibrarySimdOpName("xor", AsmJSSimdOperation_xor) ||
             !addStandardLibrarySimdOpName("select", AsmJSSimdOperation_select) ||
             !addStandardLibrarySimdOpName("splat", AsmJSSimdOperation_splat) ||
             !addStandardLibrarySimdOpName("max", AsmJSSimdOperation_max) ||
             !addStandardLibrarySimdOpName("min", AsmJSSimdOperation_min) ||
             !addStandardLibrarySimdOpName("withX", AsmJSSimdOperation_withX) ||
             !addStandardLibrarySimdOpName("withY", AsmJSSimdOperation_withY) ||
             !addStandardLibrarySimdOpName("withZ", AsmJSSimdOperation_withZ) ||
-            !addStandardLibrarySimdOpName("withW", AsmJSSimdOperation_withW))
+            !addStandardLibrarySimdOpName("withW", AsmJSSimdOperation_withW) ||
+            !addStandardLibrarySimdOpName("fromFloat32x4", AsmJSSimdOperation_fromFloat32x4) ||
+            !addStandardLibrarySimdOpName("fromInt32x4", AsmJSSimdOperation_fromInt32x4))
         {
             return false;
         }
 
         uint32_t srcStart = parser_.pc->maybeFunction->pn_body->pn_pos.begin;
         uint32_t srcBodyStart = tokenStream().currentToken().pos.end;
 
         // "use strict" should be added to the source if we are in an implicit
@@ -2504,16 +2506,27 @@ class FunctionCompiler
         MOZ_ASSERT(mask->type() == MIRType_Int32x4);
         MOZ_ASSERT(IsSimdType(lhs->type()) && rhs->type() == lhs->type());
         MOZ_ASSERT(lhs->type() == type);
         MSimdTernaryBitwise *ins = MSimdTernaryBitwise::NewAsmJS(alloc(), mask, lhs, rhs, op, type);
         curBlock_->add(ins);
         return ins;
     }
 
+    MDefinition *convertSimd(MDefinition *vec, MIRType from, MIRType to)
+    {
+        if (inDeadCode())
+            return nullptr;
+
+        MOZ_ASSERT(IsSimdType(from) && IsSimdType(to) && from != to);
+        MSimdConvert *ins = MSimdConvert::NewAsmJS(alloc(), vec, from, to);
+        curBlock_->add(ins);
+        return ins;
+    }
+
     MDefinition *splatSimd(MDefinition *v, MIRType type)
     {
         if (inDeadCode())
             return nullptr;
 
         JS_ASSERT(IsSimdType(type));
         MSimdSplatX4 *ins = MSimdSplatX4::New(alloc(), type, v);
         curBlock_->add(ins);
@@ -3583,23 +3596,26 @@ IsSimdValidOperationType(AsmJSSimdType t
       case AsmJSSimdOperation_xor:
       case AsmJSSimdOperation_select:
       case AsmJSSimdOperation_splat:
       case AsmJSSimdOperation_withX:
       case AsmJSSimdOperation_withY:
       case AsmJSSimdOperation_withZ:
       case AsmJSSimdOperation_withW:
         return true;
+      case AsmJSSimdOperation_fromFloat32x4:
+        return type == AsmJSSimdType_int32x4;
       case AsmJSSimdOperation_mul:
       case AsmJSSimdOperation_div:
       case AsmJSSimdOperation_max:
       case AsmJSSimdOperation_min:
       case AsmJSSimdOperation_lessThanOrEqual:
       case AsmJSSimdOperation_notEqual:
       case AsmJSSimdOperation_greaterThanOrEqual:
+      case AsmJSSimdOperation_fromInt32x4:
         return type == AsmJSSimdType_float32x4;
     }
     return false;
 }
 
 static bool
 CheckGlobalMathImport(ModuleCompiler &m, ParseNode *initNode, PropertyName *varName,
                       PropertyName *field)
@@ -4959,16 +4975,33 @@ CheckSimdOperationCall(FunctionCompiler 
         return CheckSimdWith(f, call, retType, SimdLane::LaneX, def, type);
       case AsmJSSimdOperation_withY:
         return CheckSimdWith(f, call, retType, SimdLane::LaneY, def, type);
       case AsmJSSimdOperation_withZ:
         return CheckSimdWith(f, call, retType, SimdLane::LaneZ, def, type);
       case AsmJSSimdOperation_withW:
         return CheckSimdWith(f, call, retType, SimdLane::LaneW, def, type);
 
+      case AsmJSSimdOperation_fromInt32x4: {
+        DefinitionVector defs;
+        if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(Type::Int32x4), &defs))
+            return false;
+        *def = f.convertSimd(defs[0], MIRType_Int32x4, retType.toMIRType());
+        *type = retType;
+        return true;
+      }
+      case AsmJSSimdOperation_fromFloat32x4: {
+        DefinitionVector defs;
+        if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(Type::Float32x4), &defs))
+            return false;
+        *def = f.convertSimd(defs[0], MIRType_Float32x4, retType.toMIRType());
+        *type = retType;
+        return true;
+      }
+
       case AsmJSSimdOperation_splat: {
         DefinitionVector defs;
         Type formalType = retType.simdToCoercedScalarType();
         if (!CheckSimdCallArgs(f, call, 1, CheckArgIsSubtypeOf(formalType), &defs))
             return false;
         *def = f.splatSimd(defs[0], retType.toMIRType());
         *type = retType;
         return true;
--- a/js/src/jit-test/tests/asm.js/testSIMD.js
+++ b/js/src/jit-test/tests/asm.js/testSIMD.js
@@ -559,16 +559,38 @@ CheckF4Comp(NEF32, 'var y=f4(1,0,3,4);  
 CheckF4Comp(GTF32, 'var y=f4(1,2,3,4);  var z=f4(-1,1,0,2); var x=i4(0,0,0,0); x=gt(y,z)', [T, T, T, T]);
 CheckF4Comp(GTF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4);  var x=i4(0,0,0,0); x=gt(y,z)', [F, F, F, F]);
 CheckF4Comp(GTF32, 'var y=f4(1,0,3,4);  var z=f4(1,1,7,0);  var x=i4(0,0,0,0); x=gt(y,z)', [F, F, F, T]);
 
 CheckF4Comp(GEF32, 'var y=f4(1,2,3,4);  var z=f4(-1,1,0,2); var x=i4(0,0,0,0); x=ge(y,z)', [T, T, T, T]);
 CheckF4Comp(GEF32, 'var y=f4(-1,1,0,2); var z=f4(1,2,3,4);  var x=i4(0,0,0,0); x=ge(y,z)', [F, F, F, F]);
 CheckF4Comp(GEF32, 'var y=f4(1,0,3,4);  var z=f4(1,1,7,0);  var x=i4(0,0,0,0); x=ge(y,z)', [T, F, F, T]);
 
+// Conversions operators
+const CVTIF = 'var cvt=f4.fromInt32x4;';
+const CVTFI = 'var cvt=i4.fromFloat32x4;';
+
+assertAsmTypeFail('glob', USE_ASM + I32 + "var cvt=i4.fromInt32x4; return {}");
+assertAsmTypeFail('glob', USE_ASM + F32 + "var cvt=f4.fromFloat32x4; return {}");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CVTIF + "function f() {var x=i4(1,2,3,4); x=cvt(x);} return f");
+assertAsmTypeFail('glob', USE_ASM + I32 + F32 + CVTIF + "function f() {var x=f4(1,2,3,4); x=cvt(x);} return f");
+
+var f = asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CVTIF + 'function f(x){x=i4(x); var y=f4(0,0,0,0); y=cvt(x); return f4(y);} return f'), this);
+assertEqX4(f(SIMD.int32x4(1,2,3,4)), [1, 2, 3, 4]);
+assertEqX4(f(SIMD.int32x4(0,INT32_MIN,INT32_MAX,-1)), [0, Math.fround(INT32_MIN), Math.fround(INT32_MAX), -1]);
+
+// TODO amend tests once int32x4.fromFloat32x4 is fully specified, when float
+// values can't be converted into an int32 without overflowing.  In these
+// tests, we assume x86/x64, so a conversion which failed will return the
+// undefined int32 value. See also bug 1068028.
+const UNDEFINED_INT32 = 0x80000000 | 0;
+var f = asmLink(asmCompile('glob', USE_ASM + I32 + F32 + CVTFI + 'function f(x){x=f4(x); var y=i4(0,0,0,0); y=cvt(x); return i4(y);} return f'), this);
+assertEqX4(f(SIMD.float32x4(1,2,3,4)), [1, 2, 3, 4]);
+assertEqX4(f(SIMD.float32x4(NaN,Infinity,-Infinity,-0)), [UNDEFINED_INT32, UNDEFINED_INT32, UNDEFINED_INT32, 0]);
+
 // Bitwise ops
 const ANDI32 = 'var andd=i4.and;';
 const ORI32 = 'var orr=i4.or;';
 const XORI32 = 'var xorr=i4.xor;';
 
 CheckI4(ANDI32, 'var x=i4(42,1337,-1,13); var y=i4(2, 4, 7, 15); x=andd(x,y)', [42 & 2, 1337 & 4, -1 & 7, 13 & 15]);
 CheckI4(ORI32, ' var x=i4(42,1337,-1,13); var y=i4(2, 4, 7, 15); x=orr(x,y)',  [42 | 2, 1337 | 4, -1 | 7, 13 | 15]);
 CheckI4(XORI32, 'var x=i4(42,1337,-1,13); var y=i4(2, 4, 7, 15); x=xorr(x,y)', [42 ^ 2, 1337 ^ 4, -1 ^ 7, 13 ^ 15]);