diff --git a/js/src/asmjs/AsmJSLink.cpp b/js/src/asmjs/AsmJSLink.cpp index 27333431e40..528f69af44b 100644 --- a/js/src/asmjs/AsmJSLink.cpp +++ b/js/src/asmjs/AsmJSLink.cpp @@ -352,6 +352,7 @@ ValidateSimdOperation(JSContext *cx, AsmJSModule::Global &global, HandleValue gl 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_lessThanOrEqual: case AsmJSSimdOperation_greaterThanOrEqual: case AsmJSSimdOperation_notEqual: @@ -377,6 +378,7 @@ ValidateSimdOperation(JSContext *cx, AsmJSModule::Global &global, HandleValue gl 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; } break; } diff --git a/js/src/asmjs/AsmJSModule.h b/js/src/asmjs/AsmJSModule.h index 536595ef2d3..60fd219975d 100644 --- a/js/src/asmjs/AsmJSModule.h +++ b/js/src/asmjs/AsmJSModule.h @@ -90,6 +90,7 @@ enum AsmJSSimdOperation AsmJSSimdOperation_or, AsmJSSimdOperation_xor, AsmJSSimdOperation_select, + AsmJSSimdOperation_splat }; // These labels describe positions in the prologue/epilogue of functions while diff --git a/js/src/asmjs/AsmJSValidate.cpp b/js/src/asmjs/AsmJSValidate.cpp index 604efea8d17..c9ce8f82d46 100644 --- a/js/src/asmjs/AsmJSValidate.cpp +++ b/js/src/asmjs/AsmJSValidate.cpp @@ -1378,7 +1378,8 @@ class MOZ_STACK_CLASS ModuleCompiler !addStandardLibrarySimdOpName("and", AsmJSSimdOperation_and) || !addStandardLibrarySimdOpName("or", AsmJSSimdOperation_or) || !addStandardLibrarySimdOpName("xor", AsmJSSimdOperation_xor) || - !addStandardLibrarySimdOpName("select", AsmJSSimdOperation_select)) + !addStandardLibrarySimdOpName("select", AsmJSSimdOperation_select) || + !addStandardLibrarySimdOpName("splat", AsmJSSimdOperation_splat)) { return false; } @@ -3524,6 +3525,7 @@ IsSimdValidOperationType(AsmJSSimdType type, AsmJSSimdOperation op) case AsmJSSimdOperation_or: case AsmJSSimdOperation_xor: case AsmJSSimdOperation_select: + case AsmJSSimdOperation_splat: return true; case AsmJSSimdOperation_mul: case AsmJSSimdOperation_div: @@ -4692,6 +4694,38 @@ CheckMathBuiltinCall(FunctionCompiler &f, ParseNode *callNode, AsmJSMathBuiltinF return true; } +static bool +CheckUnarySimd(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global, + MDefinition **def, Type *type) +{ + unsigned numArgs = CallArgListLength(call); + if (numArgs != 1) + return f.failf(call, "expected 1 argument to unary arithmetic SIMD operation, got %u", numArgs); + + ParseNode *arg = CallArgList(call); + MDefinition *argDef; + Type argType; + if (!CheckExpr(f, arg, &argDef, &argType)) + return false; + + // For now, the only unary SIMD operation is splat(scalar). + MOZ_ASSERT(global->simdOperation() == AsmJSSimdOperation_splat); + switch (global->simdOperationType()) { + case AsmJSSimdType_int32x4: + if (!argType.isIntish()) + return f.failf(arg, "%s is not a subtype of intish", argType.toChars()); + break; + case AsmJSSimdType_float32x4: + if (!CheckFloatCoercionArg(f, arg, argType, argDef, &argDef)) + return false; + break; + } + + *type = global->simdOperationType(); + *def = f.simdSplat(argDef, type->toMIRType()); + return true; +} + static bool CheckBinarySimd(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Global *global, MDefinition **def, Type *type) @@ -4714,7 +4748,6 @@ CheckBinarySimd(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Glob if (lhsType != retType || rhsType != retType) return f.failf(lhs, "arguments to SIMD binary op should both be %s", retType.toChars()); - MIRType opType = retType.toMIRType(); switch (global->simdOperation()) { case AsmJSSimdOperation_add: @@ -4774,6 +4807,7 @@ CheckBinarySimd(FunctionCompiler &f, ParseNode *call, const ModuleCompiler::Glob *def = f.binarySimd(lhsDef, rhsDef, MSimdBinaryBitwise::xor_, opType); *type = retType; break; + case AsmJSSimdOperation_splat: case AsmJSSimdOperation_select: MOZ_CRASH("unexpected SIMD binary operation"); } @@ -4824,6 +4858,8 @@ CheckSimdOperationCall(FunctionCompiler &f, ParseNode *call, const ModuleCompile { JS_ASSERT(global->isSimdOperation()); switch (global->simdOperation()) { + case AsmJSSimdOperation_splat: + return CheckUnarySimd(f, call, global, def, type); case AsmJSSimdOperation_add: case AsmJSSimdOperation_sub: case AsmJSSimdOperation_mul: diff --git a/js/src/jit-test/tests/asm.js/testSIMD.js b/js/src/jit-test/tests/asm.js/testSIMD.js index fc4bd56b66e..5a9cfe5c6e1 100644 --- a/js/src/jit-test/tests/asm.js/testSIMD.js +++ b/js/src/jit-test/tests/asm.js/testSIMD.js @@ -1,5 +1,5 @@ load(libdir + "asm.js"); -var heap = new ArrayBuffer(4096); +var heap = new ArrayBuffer(0x10000); // Set to true to see more JS debugging spew const DEBUG = false; @@ -553,6 +553,34 @@ assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32SEL + "function f assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32SEL + "function f() {var m=i4(0,0xffffffff,0,0xffffffff); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return f4(f4sel(m,x,y)); } return f"), this)(), [5, 2, 7, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + F32 + F32SEL + "function f() {var m=i4(0,0,0xffffffff,0xffffffff); var x=f4(1,2,3,4); var y=f4(5,6,7,8); return f4(f4sel(m,x,y)); } return f"), this)(), [5, 6, 3, 4]); +// Splat +const I32SPLAT = 'var splat=i4.splat;' +const F32SPLAT = 'var splat=f4.splat;' + +assertAsmTypeFail('glob', USE_ASM + I32 + F32 + I32SPLAT + "function f() {var m=i4(1,2,3,4); var p=f4(1.,2.,3.,4.); p=splat(1);} return f"); +assertAsmTypeFail('glob', USE_ASM + I32 + I32SPLAT + "function f() {var m=i4(1,2,3,4); m=splat(1, 2)} return f"); +assertAsmTypeFail('glob', USE_ASM + I32 + I32SPLAT + "function f() {var m=i4(1,2,3,4); m=splat()} return f"); + +assertAsmTypeFail('glob', USE_ASM + I32 + I32SPLAT + "function f() {var m=i4(1,2,3,4); m=splat(m);} return f"); +assertAsmTypeFail('glob', USE_ASM + I32 + I32SPLAT + "function f() {var m=i4(1,2,3,4); m=splat(1.0);} return f"); +assertAsmTypeFail('glob', USE_ASM + I32 + I32SPLAT + FROUND + "function f() {var m=i4(1,2,3,4); m=splat(f32(1.0));} return f"); + +assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + I32SPLAT + 'function f(){return i4(splat(42));} return f'), this)(), [42, 42, 42, 42]); + +const l33t = Math.fround(13.37); +assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32SPLAT + FROUND + 'function f(){return f4(splat(1));} return f'), this)(), [1, 1, 1, 1]); +assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32SPLAT + FROUND + 'function f(){return f4(splat(1 >>> 0));} return f'), this)(), [1, 1, 1, 1]); +assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32SPLAT + FROUND + 'function f(){return f4(splat(13.37));} return f'), this)(), [l33t, l33t, l33t, l33t]); +assertEqX4(asmLink(asmCompile('glob', USE_ASM + F32 + F32SPLAT + FROUND + 'function f(){return f4(splat(f32(13.37)));} return f'), this)(), [l33t, l33t, l33t, l33t]); + +var i32view = new Int32Array(heap); +var f32view = new Float32Array(heap); +i32view[0] = 42; +assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + I32 + I32SPLAT + 'var i32=new glob.Int32Array(heap); function f(){return i4(splat(i32[0]));} return f'), this, {}, heap)(), [42, 42, 42, 42]); +f32view[0] = 42; +assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + F32 + F32SPLAT + 'var f32=new glob.Float32Array(heap); function f(){return f4(splat(f32[0]));} return f'), this, {}, heap)(), [42, 42, 42, 42]); +assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', USE_ASM + F32 + F32SPLAT + FROUND + 'function f(){return f4(splat(f32(1) + f32(2)));} return f'), this, {}, heap)(), [3, 3, 3, 3]); + // Dead code assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + 'function f(){var x=i4(1,2,3,4); return i4(x); x=i4(5,6,7,8); return i4(x);} return f'), this)(), [1, 2, 3, 4]); assertEqX4(asmLink(asmCompile('glob', USE_ASM + I32 + 'function f(){var x=i4(1,2,3,4); var c=0; return i4(x); c=x.x|0; return i4(x);} return f'), this)(), [1, 2, 3, 4]);