From 2ada54cf8d9c73af729824ab8bfbde36af78446d Mon Sep 17 00:00:00 2001 From: Sajjad Taheri Date: Tue, 22 Dec 2015 14:17:13 -0800 Subject: [PATCH] Bug 1160971 - Part 3: SIMD boolean vector support for JIT. r=bbouvier Based on a patch by Sajjad Taheri! --- js/src/builtin/SIMD.cpp | 1 + js/src/builtin/SIMD.h | 9 +- js/src/jit-test/tests/SIMD/bool32x4-const.js | 65 +++++++++ js/src/jit/BaselineBailouts.cpp | 1 + js/src/jit/BaselineIC.cpp | 36 +++-- js/src/jit/CodeGenerator.cpp | 5 + js/src/jit/EagerSimdUnbox.cpp | 1 + js/src/jit/InlinableNatives.h | 1 + js/src/jit/IonAnalysis.cpp | 1 + js/src/jit/IonBuilder.cpp | 2 +- js/src/jit/IonBuilder.h | 4 + js/src/jit/IonTypes.h | 27 +++- js/src/jit/LIR.h | 1 + js/src/jit/Lowering.cpp | 74 ++++++++-- js/src/jit/Lowering.h | 2 + js/src/jit/MCallOptimize.cpp | 126 ++++++++++++++++-- js/src/jit/MIR.cpp | 30 +++-- js/src/jit/MIR.h | 99 ++++++++++++-- js/src/jit/MOpcodes.h | 2 + js/src/jit/Recover.cpp | 8 +- js/src/jit/TypePolicy.cpp | 13 +- js/src/jit/shared/CodeGenerator-shared.cpp | 1 + js/src/jit/shared/LIR-shared.h | 49 ++++++- js/src/jit/shared/LOpcodes-shared.h | 3 + js/src/jit/shared/Lowering-shared-inl.h | 1 + js/src/jit/x64/Assembler-x64.cpp | 2 + js/src/jit/x64/CodeGenerator-x64.cpp | 2 + .../x86-shared/CodeGenerator-x86-shared.cpp | 50 ++++++- .../jit/x86-shared/CodeGenerator-x86-shared.h | 3 + js/src/jit/x86-shared/Lowering-x86-shared.cpp | 15 ++- js/src/jit/x86/Assembler-x86.cpp | 1 + js/src/jit/x86/CodeGenerator-x86.cpp | 2 + 32 files changed, 557 insertions(+), 80 deletions(-) create mode 100644 js/src/jit-test/tests/SIMD/bool32x4-const.js diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp index 596c1f64a981..912f47e6d04d 100644 --- a/js/src/builtin/SIMD.cpp +++ b/js/src/builtin/SIMD.cpp @@ -121,6 +121,7 @@ js::ToSimdConstant(JSContext* cx, HandleValue v, jit::SimdConstant* out) template bool js::ToSimdConstant(JSContext* cx, HandleValue v, jit::SimdConstant* out); template bool js::ToSimdConstant(JSContext* cx, HandleValue v, jit::SimdConstant* out); +template bool js::ToSimdConstant(JSContext* cx, HandleValue v, jit::SimdConstant* out); template static Elem diff --git a/js/src/builtin/SIMD.h b/js/src/builtin/SIMD.h index 78d1b1e5af51..b0efe001aa69 100644 --- a/js/src/builtin/SIMD.h +++ b/js/src/builtin/SIMD.h @@ -391,13 +391,6 @@ _(and) \ _(or) \ _(xor) -#define COMP_COMMONX4_TO_INT32X4_SIMD_OP(_) \ - _(lessThan) \ - _(lessThanOrEqual) \ - _(equal) \ - _(notEqual) \ - _(greaterThan) \ - _(greaterThanOrEqual) #define COMP_COMMONX4_TO_BOOL32X4_SIMD_OP(_) \ _(lessThan) \ _(lessThanOrEqual) \ @@ -428,7 +421,7 @@ _(check) #define FOREACH_COMMONX4_SIMD_OP(_) \ ION_COMMONX4_SIMD_OP(_) \ - COMP_COMMONX4_TO_INT32X4_SIMD_OP(_) + COMP_COMMONX4_TO_BOOL32X4_SIMD_OP(_) #define FORALL_SIMD_OP(_) \ FOREACH_INT32X4_SIMD_OP(_) \ FOREACH_FLOAT32X4_SIMD_OP(_) \ diff --git a/js/src/jit-test/tests/SIMD/bool32x4-const.js b/js/src/jit-test/tests/SIMD/bool32x4-const.js new file mode 100644 index 000000000000..54bada215b8a --- /dev/null +++ b/js/src/jit-test/tests/SIMD/bool32x4-const.js @@ -0,0 +1,65 @@ +load(libdir + "simd.js"); + +setJitCompilerOption("ion.warmup.trigger", 50); + +// Test constant folding into the Bool32x4 constructor. +// Verify that we get the truthiness right, c.f. the ECMA ToBoolean() function. +function f1() { + var B = SIMD.Bool32x4; + var S = SIMD.Bool32x4.splat; + return [ + B(false, false, false, true), + B(true), + B(undefined, null, "", "x"), + B({}, 0, 1, -0.0), + B(NaN, -NaN, Symbol(), objectEmulatingUndefined()), + + S(false), + S(true), + S(undefined), + S(null), + + S(""), + S("x"), + S(0), + S(1), + + S({}), + S(-0.0), + S(NaN), + S(Symbol()), + + S(objectEmulatingUndefined()) + ]; +} + +function f() { + for (var i = 0; i < 100; i++) { + var a = f1() + assertEqX4(a[0], [false, false, false, true]); + assertEqX4(a[1], [true, false, false, false]); + assertEqX4(a[2], [false, false, false, true]); + assertEqX4(a[3], [true, false, true, false]); + assertEqX4(a[4], [false, false, true, false]); + + // Splats. + assertEqX4(a[5], [false, false, false, false]); + assertEqX4(a[6], [true, true, true, true]); + assertEqX4(a[7], [false, false, false, false]); + assertEqX4(a[8], [false, false, false, false]); + + assertEqX4(a[9], [false, false, false, false]); + assertEqX4(a[10], [true, true, true, true]); + assertEqX4(a[11], [false, false, false, false]); + assertEqX4(a[12], [true, true, true, true]); + + assertEqX4(a[13], [true, true, true, true]); + assertEqX4(a[14], [false, false, false, false]); + assertEqX4(a[15], [false, false, false, false]); + assertEqX4(a[16], [true, true, true, true]); + + assertEqX4(a[17], [false, false, false, false]); + } +} + +f(); diff --git a/js/src/jit/BaselineBailouts.cpp b/js/src/jit/BaselineBailouts.cpp index 4c215de901f8..c26d34553058 100644 --- a/js/src/jit/BaselineBailouts.cpp +++ b/js/src/jit/BaselineBailouts.cpp @@ -1893,6 +1893,7 @@ jit::FinishBailoutToBaseline(BaselineBailoutInfo* bailoutInfo) case Bailout_NonObjectInput: case Bailout_NonStringInput: case Bailout_NonSymbolInput: + case Bailout_NonSimdBool32x4Input: case Bailout_NonSimdInt32x4Input: case Bailout_NonSimdFloat32x4Input: case Bailout_NonSharedTypedArrayInput: diff --git a/js/src/jit/BaselineIC.cpp b/js/src/jit/BaselineIC.cpp index 630639684e70..42ffce6c4c54 100644 --- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -5700,25 +5700,37 @@ GetTemplateObjectForNative(JSContext* cx, Native native, const CallArgs& args, if (JitSupportsSimd()) { #define ADD_INT32X4_SIMD_OP_NAME_(OP) || native == js::simd_int32x4_##OP +#define ADD_BOOL32X4_SIMD_OP_NAME_(OP) || native == js::simd_bool32x4_##OP #define ADD_FLOAT32X4_SIMD_OP_NAME_(OP) || native == js::simd_float32x4_##OP - if (false - ION_COMMONX4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_) - COMP_COMMONX4_TO_INT32X4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_) - COMP_COMMONX4_TO_INT32X4_SIMD_OP(ADD_FLOAT32X4_SIMD_OP_NAME_) - FOREACH_INT32X4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_)) - { + // Operations producing an int32x4. + if (false + ION_COMMONX4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_) + FOREACH_INT32X4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_)) + { Rooted descr(cx, cx->global()->getOrCreateSimdTypeDescr(cx)); res.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr)); return !!res; - } - if (false - FOREACH_FLOAT32X4_SIMD_OP(ADD_FLOAT32X4_SIMD_OP_NAME_) - ION_COMMONX4_SIMD_OP(ADD_FLOAT32X4_SIMD_OP_NAME_)) - { + } + // Operations producing a bool32x4. + if (false + BITWISE_COMMONX4_SIMD_OP(ADD_BOOL32X4_SIMD_OP_NAME_) + COMP_COMMONX4_TO_BOOL32X4_SIMD_OP(ADD_INT32X4_SIMD_OP_NAME_) + COMP_COMMONX4_TO_BOOL32X4_SIMD_OP(ADD_FLOAT32X4_SIMD_OP_NAME_)) + { + Rooted descr(cx, cx->global()->getOrCreateSimdTypeDescr(cx)); + res.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr)); + return !!res; + } + // Operations producing a float32x4. + if (false + FOREACH_FLOAT32X4_SIMD_OP(ADD_FLOAT32X4_SIMD_OP_NAME_) + ION_COMMONX4_SIMD_OP(ADD_FLOAT32X4_SIMD_OP_NAME_)) + { Rooted descr(cx, cx->global()->getOrCreateSimdTypeDescr(cx)); res.set(cx->compartment()->jitCompartment()->getSimdTemplateObjectFor(cx, descr)); return !!res; - } + } +#undef ADD_BOOL32X4_SIMD_OP_NAME_ #undef ADD_INT32X4_SIMD_OP_NAME_ #undef ADD_FLOAT32X4_SIMD_OP_NAME_ } diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 8e8e1bd9f960..e08280125860 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -4741,6 +4741,7 @@ CodeGenerator::visitSimdBox(LSimdBox* lir) Address objectData(object, InlineTypedObject::offsetOfDataStart()); switch (type) { + case MIRType_Bool32x4: case MIRType_Int32x4: masm.storeUnalignedInt32x4(in, objectData); break; @@ -4809,6 +4810,9 @@ CodeGenerator::visitSimdUnbox(LSimdUnbox* lir) // Convert the SIMD MIRType to a SimdTypeDescr::Type. js::SimdTypeDescr::Type type; switch (lir->mir()->type()) { + case MIRType_Bool32x4: + type = js::SimdTypeDescr::Bool32x4; + break; case MIRType_Int32x4: type = js::SimdTypeDescr::Int32x4; break; @@ -4830,6 +4834,7 @@ CodeGenerator::visitSimdUnbox(LSimdUnbox* lir) // Load the value from the data of the InlineTypedObject. Address objectData(object, InlineTypedObject::offsetOfDataStart()); switch (lir->mir()->type()) { + case MIRType_Bool32x4: case MIRType_Int32x4: masm.loadUnalignedInt32x4(objectData, simd); break; diff --git a/js/src/jit/EagerSimdUnbox.cpp b/js/src/jit/EagerSimdUnbox.cpp index 7a93d20dd661..61c161e699a6 100644 --- a/js/src/jit/EagerSimdUnbox.cpp +++ b/js/src/jit/EagerSimdUnbox.cpp @@ -20,6 +20,7 @@ MIRTypeToSimdTypeDescr(MIRType type) switch (type) { case MIRType_Float32x4: return SimdTypeDescr::Float32x4; case MIRType_Int32x4: return SimdTypeDescr::Int32x4; + case MIRType_Bool32x4: return SimdTypeDescr::Bool32x4; default: break; } MOZ_CRASH("unexpected MIRType"); diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h index 2015cc2f9060..19090dc4b6b4 100644 --- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -81,6 +81,7 @@ \ _(SimdInt32x4) \ _(SimdFloat32x4) \ + _(SimdBool32x4) \ \ _(TestBailout) \ _(TestAssertFloat32) \ diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index 013f07c74696..db53de8c0dd2 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -2471,6 +2471,7 @@ IsResumableMIRType(MIRType type) case MIRType_Value: case MIRType_Float32x4: case MIRType_Int32x4: + case MIRType_Bool32x4: return true; case MIRType_MagicHole: diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index 0df3751d1835..61fae85a45ed 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -11249,12 +11249,12 @@ IonBuilder::SimdTypeDescrToMIRType(SimdTypeDescr::Type type) switch (type) { case SimdTypeDescr::Int32x4: return MIRType_Int32x4; case SimdTypeDescr::Float32x4: return MIRType_Float32x4; + case SimdTypeDescr::Bool32x4: return MIRType_Bool32x4; case SimdTypeDescr::Int8x16: case SimdTypeDescr::Int16x8: case SimdTypeDescr::Float64x2: case SimdTypeDescr::Bool8x16: case SimdTypeDescr::Bool16x8: - case SimdTypeDescr::Bool32x4: case SimdTypeDescr::Bool64x2: return MIRType_Undefined; } MOZ_CRASH("unimplemented MIR type for a SimdTypeDescr::Type"); diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 2a50f2d619cf..bcecdd6b1df9 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -859,9 +859,11 @@ class IonBuilder unsigned numArgs, InlineTypedObject** templateObj); IonBuilder::InliningStatus boxSimd(CallInfo& callInfo, MInstruction* ins, InlineTypedObject* templateObj); + MDefinition* convertToBooleanSimdLane(MDefinition* scalar); InliningStatus inlineSimdInt32x4(CallInfo& callInfo, JSNative native); InliningStatus inlineSimdFloat32x4(CallInfo& callInfo, JSNative native); + InliningStatus inlineSimdBool32x4(CallInfo& callInfo, JSNative native); template InliningStatus inlineBinarySimd(CallInfo& callInfo, JSNative native, @@ -890,6 +892,8 @@ class IonBuilder InliningStatus inlineSimdStore(CallInfo& callInfo, JSNative native, SimdTypeDescr::Type type, unsigned numElems); + InliningStatus inlineSimdAnyAllTrue(CallInfo& callInfo, bool IsAllTrue, JSNative native); + // Utility intrinsics. InliningStatus inlineIsCallable(CallInfo& callInfo); InliningStatus inlineIsObject(CallInfo& callInfo); diff --git a/js/src/jit/IonTypes.h b/js/src/jit/IonTypes.h index 471cc4205e65..16d656b4c458 100644 --- a/js/src/jit/IonTypes.h +++ b/js/src/jit/IonTypes.h @@ -102,6 +102,7 @@ enum BailoutKind Bailout_NonSymbolInput, // SIMD Unbox expects a given type, bails out if it doesn't match. + Bailout_NonSimdBool32x4Input, Bailout_NonSimdInt32x4Input, Bailout_NonSimdFloat32x4Input, @@ -213,6 +214,8 @@ BailoutKindString(BailoutKind kind) return "Bailout_NonStringInput"; case Bailout_NonSymbolInput: return "Bailout_NonSymbolInput"; + case Bailout_NonSimdBool32x4Input: + return "Bailout_NonSimdBool32x4Input"; case Bailout_NonSimdInt32x4Input: return "Bailout_NonSimdInt32x4Input"; case Bailout_NonSimdFloat32x4Input: @@ -414,6 +417,7 @@ enum MIRType MIRType_Last = MIRType_ObjectGroup, MIRType_Float32x4 = MIRType_Float32 | (2 << VECTOR_SCALE_SHIFT), MIRType_Int32x4 = MIRType_Int32 | (2 << VECTOR_SCALE_SHIFT), + MIRType_Bool32x4 = MIRType_Boolean | (2 << VECTOR_SCALE_SHIFT), MIRType_Doublex2 = MIRType_Double | (1 << VECTOR_SCALE_SHIFT) }; @@ -571,7 +575,7 @@ IsNullOrUndefined(MIRType type) static inline bool IsSimdType(MIRType type) { - return type == MIRType_Int32x4 || type == MIRType_Float32x4; + return type == MIRType_Int32x4 || type == MIRType_Float32x4 || type == MIRType_Bool32x4; } static inline bool @@ -586,6 +590,12 @@ IsIntegerSimdType(MIRType type) return type == MIRType_Int32x4; } +static inline bool +IsBooleanSimdType(MIRType type) +{ + return type == MIRType_Bool32x4; +} + static inline bool IsMagicType(MIRType type) { @@ -655,7 +665,7 @@ ScalarTypeToLength(Scalar::Type type) } // Get the type of the individual lanes in a SIMD type. -// For example, Int32x4 -> Int32, FLoat32x4 -> Float32 etc. +// For example, Int32x4 -> Int32, Float32x4 -> Float32 etc. static inline MIRType SimdTypeToLaneType(MIRType type) { @@ -665,6 +675,19 @@ SimdTypeToLaneType(MIRType type) return MIRType((type >> ELEMENT_TYPE_SHIFT) & ELEMENT_TYPE_MASK); } +// Get the type expected when inserting a lane into a SIMD type. +// This is the argument type expected by the MSimdValue constructors as well as +// MSimdSplat and MSimdInsertElement. +static inline MIRType +SimdTypeToLaneArgumentType(MIRType type) +{ + MIRType laneType = SimdTypeToLaneType(type); + + // Boolean lanes should be pre-converted to an Int32 with the values 0 or -1. + // All other lane types are inserted directly. + return laneType == MIRType_Boolean ? MIRType_Int32 : laneType; +} + // Indicates a lane in a SIMD register: X for the first lane, Y for the second, // Z for the third (if any), W for the fourth (if any). enum SimdLane { diff --git a/js/src/jit/LIR.h b/js/src/jit/LIR.h index 102d7300aabd..517c389fae16 100644 --- a/js/src/jit/LIR.h +++ b/js/src/jit/LIR.h @@ -568,6 +568,7 @@ class LDefinition return LDefinition::SLOTS; case MIRType_Pointer: return LDefinition::GENERAL; + case MIRType_Bool32x4: case MIRType_Int32x4: return LDefinition::INT32X4; case MIRType_Float32x4: diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index f7a94d38b2fc..3a393c70c9f7 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4017,6 +4017,9 @@ LIRGenerator::visitSimdUnbox(MSimdUnbox* ins) BailoutKind kind; switch (ins->type()) { + case MIRType_Bool32x4: + kind = Bailout_NonSimdBool32x4Input; + break; case MIRType_Int32x4: kind = Bailout_NonSimdInt32x4Input; break; @@ -4037,12 +4040,17 @@ LIRGenerator::visitSimdConstant(MSimdConstant* ins) { MOZ_ASSERT(IsSimdType(ins->type())); - if (ins->type() == MIRType_Int32x4) + switch (ins->type()) { + case MIRType_Bool32x4: + case MIRType_Int32x4: define(new(alloc()) LInt32x4(), ins); - else if (ins->type() == MIRType_Float32x4) + break; + case MIRType_Float32x4: define(new(alloc()) LFloat32x4(), ins); - else + break; + default: MOZ_CRASH("Unknown SIMD kind when generating constant"); + } } void @@ -4083,15 +4091,25 @@ LIRGenerator::visitSimdExtractElement(MSimdExtractElement* ins) MOZ_ASSERT(IsSimdType(ins->input()->type())); MOZ_ASSERT(!IsSimdType(ins->type())); - if (ins->input()->type() == MIRType_Int32x4) { + switch (ins->input()->type()) { + case MIRType_Int32x4: { // Note: there could be int16x8 in the future, which doesn't use the // same instruction. We either need to pass the arity or create new LIns. LUse use = useRegisterAtStart(ins->input()); define(new(alloc()) LSimdExtractElementI(use), ins); - } else if (ins->input()->type() == MIRType_Float32x4) { + break; + } + case MIRType_Float32x4: { LUse use = useRegisterAtStart(ins->input()); define(new(alloc()) LSimdExtractElementF(use), ins); - } else { + break; + } + case MIRType_Bool32x4: { + LUse use = useRegisterAtStart(ins->input()); + define(new(alloc()) LSimdExtractElementB(use), ins); + break; + } + default: MOZ_CRASH("Unknown SIMD kind when extracting element"); } } @@ -4103,12 +4121,17 @@ LIRGenerator::visitSimdInsertElement(MSimdInsertElement* ins) LUse vec = useRegisterAtStart(ins->vector()); LUse val = useRegister(ins->value()); - if (ins->type() == MIRType_Int32x4) + switch (ins->type()) { + case MIRType_Int32x4: + case MIRType_Bool32x4: defineReuseInput(new(alloc()) LSimdInsertElementI(vec, val), ins, 0); - else if (ins->type() == MIRType_Float32x4) + break; + case MIRType_Float32x4: defineReuseInput(new(alloc()) LSimdInsertElementF(vec, val), ins, 0); - else + break; + default: MOZ_CRASH("Unknown SIMD kind when generating constant"); + } } void @@ -4130,6 +4153,26 @@ LIRGenerator::visitSimdSignMask(MSimdSignMask* ins) } } +void +LIRGenerator::visitSimdAllTrue(MSimdAllTrue* ins) +{ + MDefinition* input = ins->input(); + MOZ_ASSERT(IsBooleanSimdType(input->type())); + + LUse use = useRegisterAtStart(input); + define(new(alloc()) LSimdAllTrue(use), ins); +} + +void +LIRGenerator::visitSimdAnyTrue(MSimdAnyTrue* ins) +{ + MDefinition* input = ins->input(); + MOZ_ASSERT(IsBooleanSimdType(input->type())); + + LUse use = useRegisterAtStart(input); + define(new(alloc()) LSimdAnyTrue(use), ins); +} + void LIRGenerator::visitSimdSwizzle(MSimdSwizzle* ins) { @@ -4208,7 +4251,7 @@ LIRGenerator::visitSimdUnaryArith(MSimdUnaryArith* ins) // Cannot be at start, as the ouput is used as a temporary to store values. LUse in = use(ins->input()); - if (ins->type() == MIRType_Int32x4) { + if (ins->type() == MIRType_Int32x4 || ins->type() == MIRType_Bool32x4) { LSimdUnaryArithIx4* lir = new(alloc()) LSimdUnaryArithIx4(in); define(lir, ins); } else if (ins->type() == MIRType_Float32x4) { @@ -4224,7 +4267,7 @@ LIRGenerator::visitSimdBinaryComp(MSimdBinaryComp* ins) { MOZ_ASSERT(IsSimdType(ins->lhs()->type())); MOZ_ASSERT(IsSimdType(ins->rhs()->type())); - MOZ_ASSERT(ins->type() == MIRType_Int32x4); + MOZ_ASSERT(IsBooleanSimdType(ins->type())); if (ShouldReorderCommutative(ins->lhs(), ins->rhs(), ins)) ins->reverse(); @@ -4251,10 +4294,15 @@ LIRGenerator::visitSimdBinaryBitwise(MSimdBinaryBitwise* ins) MDefinition* rhs = ins->rhs(); ReorderCommutative(&lhs, &rhs, ins); - if (ins->type() == MIRType_Int32x4 || ins->type() == MIRType_Float32x4) { + switch (ins->type()) { + case MIRType_Bool32x4: + case MIRType_Int32x4: + case MIRType_Float32x4: { LSimdBinaryBitwiseX4* lir = new(alloc()) LSimdBinaryBitwiseX4; lowerForFPU(lir, ins, lhs, rhs); - } else { + break; + } + default: MOZ_CRASH("Unknown SIMD kind when doing bitwise operations"); } } diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index dff0e6420383..960227f302cb 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -296,6 +296,8 @@ class LIRGenerator : public LIRGeneratorSpecific void visitSimdConstant(MSimdConstant* ins); void visitSimdConvert(MSimdConvert* ins); void visitSimdReinterpretCast(MSimdReinterpretCast* ins); + void visitSimdAllTrue(MSimdAllTrue* ins); + void visitSimdAnyTrue(MSimdAnyTrue* ins); void visitPhi(MPhi* ins); void visitBeta(MBeta* ins); void visitObjectState(MObjectState* ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 7c26826195a4..d2634257eaab 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -206,6 +206,8 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return inlineSimdInt32x4(callInfo, target->native()); case InlinableNative::SimdFloat32x4: return inlineSimdFloat32x4(callInfo, target->native()); + case InlinableNative::SimdBool32x4: + return inlineSimdBool32x4(callInfo, target->native()); // Testing functions. case InlinableNative::TestBailout: @@ -3048,6 +3050,38 @@ IonBuilder::inlineConstructTypedObject(CallInfo& callInfo, TypeDescr* descr) return InliningStatus_Inlined; } +IonBuilder::InliningStatus +IonBuilder::inlineSimdBool32x4(CallInfo& callInfo, JSNative native) +{ +#define INLINE_SIMD_BITWISE_(OP) \ + if (native == js::simd_bool32x4_##OP) \ + return inlineBinarySimd(callInfo, native, MSimdBinaryBitwise::OP##_, \ + SimdTypeDescr::Bool32x4); + + BITWISE_COMMONX4_SIMD_OP(INLINE_SIMD_BITWISE_) +#undef INLINE_SIMD_BITWISE_ + + if (native == js::simd_bool32x4_extractLane) + return inlineSimdExtractLane(callInfo, native, SimdTypeDescr::Bool32x4); + if (native == js::simd_bool32x4_replaceLane) + return inlineSimdReplaceLane(callInfo, native, SimdTypeDescr::Bool32x4); + + if (native == js::simd_bool32x4_not) + return inlineUnarySimd(callInfo, native, MSimdUnaryArith::not_, SimdTypeDescr::Bool32x4); + + if (native == js::simd_bool32x4_splat) + return inlineSimdSplat(callInfo, native, SimdTypeDescr::Bool32x4); + if (native == js::simd_bool32x4_check) + return inlineSimdCheck(callInfo, native, SimdTypeDescr::Bool32x4); + + if (native == js::simd_bool32x4_allTrue) + return inlineSimdAnyAllTrue(callInfo, true, native); + if (native == js::simd_bool32x4_anyTrue) + return inlineSimdAnyAllTrue(callInfo, false, native); + + return InliningStatus_NotInlined; +} + IonBuilder::InliningStatus IonBuilder::inlineSimdInt32x4(CallInfo& callInfo, JSNative native) { @@ -3078,7 +3112,7 @@ IonBuilder::inlineSimdInt32x4(CallInfo& callInfo, JSNative native) if (native == js::simd_int32x4_##OP) \ return inlineCompSimd(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Int32x4); - COMP_COMMONX4_TO_INT32X4_SIMD_OP(INLINE_SIMD_COMPARISON_) + COMP_COMMONX4_TO_BOOL32X4_SIMD_OP(INLINE_SIMD_COMPARISON_) #undef INLINE_SIMD_COMPARISON_ if (native == js::simd_int32x4_extractLane) @@ -3160,7 +3194,7 @@ IonBuilder::inlineSimdFloat32x4(CallInfo& callInfo, JSNative native) if (native == js::simd_float32x4_##OP) \ return inlineCompSimd(callInfo, native, MSimdBinaryComp::OP, SimdTypeDescr::Float32x4); - COMP_COMMONX4_TO_INT32X4_SIMD_OP(INLINE_SIMD_COMPARISON_) + COMP_COMMONX4_TO_BOOL32X4_SIMD_OP(INLINE_SIMD_COMPARISON_) #undef INLINE_SIMD_COMPARISON_ if (native == js::simd_float32x4_extractLane) @@ -3221,6 +3255,37 @@ IonBuilder::inlineSimdFloat32x4(CallInfo& callInfo, JSNative native) return InliningStatus_NotInlined; } +// The representation of boolean SIMD vectors is the same as the corresponding +// integer SIMD vectors with -1 lanes meaning true and 0 lanes meaning false. +// +// Functions that set the value of a boolean vector lane work by applying +// ToBoolean on the input argument, so they accept any argument type, just like +// the MNot and MTest instructions. +// +// Convert any scalar value into an appropriate SIMD lane value: An Int32 value +// that is either 0 for false or -1 for true. +MDefinition* +IonBuilder::convertToBooleanSimdLane(MDefinition* scalar) +{ + MSub* result; + + if (scalar->type() == MIRType_Boolean) { + // The input scalar is already a boolean with the int32 values 0 / 1. + // Compute result = 0 - scalar. + result = MSub::New(alloc(), constant(Int32Value(0)), scalar); + } else { + // For any other type, let MNot handle the conversion to boolean. + // Compute result = !scalar - 1. + MNot* inv = MNot::New(alloc(), scalar); + current->add(inv); + result = MSub::New(alloc(), inv, constant(Int32Value(1))); + } + + result->setInt32Specialization(); + current->add(result); + return result; +} + IonBuilder::InliningStatus IonBuilder::inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* descr) { @@ -3246,10 +3311,12 @@ IonBuilder::inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* descr) // When there are missing arguments, provide a default value // containing the coercion of 'undefined' to the right type. MConstant* defVal = nullptr; + MIRType laneType = SimdTypeToLaneType(simdType); if (callInfo.argc() < SimdTypeToLength(simdType)) { - MIRType laneType = SimdTypeToLaneType(simdType); if (laneType == MIRType_Int32) { defVal = constant(Int32Value(0)); + } else if (laneType == MIRType_Boolean) { + defVal = constant(BooleanValue(false)); } else { MOZ_ASSERT(IsFloatingPointType(laneType)); defVal = constant(DoubleNaNValue()); @@ -3257,10 +3324,18 @@ IonBuilder::inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* descr) } } + MDefinition* lane[4]; + for (unsigned i = 0; i < 4; i++) + lane[i] = callInfo.getArgWithDefault(i, defVal); + + // Convert boolean lanes into Int32 0 / -1. + if (laneType == MIRType_Boolean) { + for (unsigned i = 0; i < 4; i++) + lane[i] = convertToBooleanSimdLane(lane[i]); + } + MSimdValueX4* values = - MSimdValueX4::New(alloc(), simdType, - callInfo.getArgWithDefault(0, defVal), callInfo.getArgWithDefault(1, defVal), - callInfo.getArgWithDefault(2, defVal), callInfo.getArgWithDefault(3, defVal)); + MSimdValueX4::New(alloc(), simdType, lane[0], lane[1], lane[2], lane[3]); current->add(values); MSimdBox* obj = MSimdBox::New(alloc(), constraints(), values, inlineTypedObject, @@ -3326,7 +3401,7 @@ IonBuilder::inlineCompSimd(CallInfo& callInfo, JSNative native, MSimdBinaryComp: SimdTypeDescr::Type compType) { InlineTypedObject* templateObj = nullptr; - if (!checkInlineSimd(callInfo, native, SimdTypeDescr::Int32x4, 2, &templateObj)) + if (!checkInlineSimd(callInfo, native, SimdTypeDescr::Bool32x4, 2, &templateObj)) return InliningStatus_NotInlined; // If the type of any of the arguments is neither a SIMD type, an Object @@ -3362,7 +3437,13 @@ IonBuilder::inlineSimdSplat(CallInfo& callInfo, JSNative native, SimdTypeDescr:: // See comment in inlineBinarySimd MIRType mirType = SimdTypeDescrToMIRType(type); - MSimdSplatX4* ins = MSimdSplatX4::New(alloc(), callInfo.getArg(0), mirType); + MDefinition* arg = callInfo.getArg(0); + + // Convert to 0 / -1 before splatting a boolean lane. + if (SimdTypeToLaneType(mirType) == MIRType_Boolean) + arg = convertToBooleanSimdLane(arg); + + MSimdSplatX4* ins = MSimdSplatX4::New(alloc(), arg, mirType); return boxSimd(callInfo, ins, templateObj); } @@ -3408,8 +3489,14 @@ IonBuilder::inlineSimdReplaceLane(CallInfo& callInfo, JSNative native, SimdTypeD // See comment in inlineBinarySimd MIRType mirType = SimdTypeDescrToMIRType(type); - MSimdInsertElement* ins = MSimdInsertElement::New(alloc(), callInfo.getArg(0), - callInfo.getArg(2), mirType, SimdLane(lane)); + + // Convert to 0 / -1 before inserting a boolean lane. + MDefinition* value = callInfo.getArg(2); + if (SimdTypeToLaneType(mirType) == MIRType_Boolean) + value = convertToBooleanSimdLane(value); + + MSimdInsertElement* ins = + MSimdInsertElement::New(alloc(), callInfo.getArg(0), value, mirType, SimdLane(lane)); return boxSimd(callInfo, ins, templateObj); } @@ -3486,6 +3573,25 @@ IonBuilder::inlineSimdShuffle(CallInfo& callInfo, JSNative native, SimdTypeDescr return boxSimd(callInfo, ins, templateObj); } +IonBuilder::InliningStatus +IonBuilder::inlineSimdAnyAllTrue(CallInfo& callInfo, bool IsAllTrue, JSNative native) +{ + InlineTypedObject* templateObj = nullptr; + if (!checkInlineSimd(callInfo, native, SimdTypeDescr::Bool32x4, 1, &templateObj)) + return InliningStatus_NotInlined; + + MUnaryInstruction* ins; + if (IsAllTrue) + ins = MSimdAllTrue::New(alloc(), callInfo.getArg(0), MIRType_Bool32x4); + else + ins = MSimdAnyTrue::New(alloc(), callInfo.getArg(0), MIRType_Bool32x4); + + current->add(ins); + current->push(ins); + callInfo.setImplicitlyUsedUnchecked(); + return InliningStatus_Inlined; +} + // Get the typed array element type corresponding to the lanes in a SIMD vector type. // This only applies to SIMD types that can be loaded and stored to a typed array. static Scalar::Type diff --git a/js/src/jit/MIR.cpp b/js/src/jit/MIR.cpp index 509079f55afe..0918a842f19d 100644 --- a/js/src/jit/MIR.cpp +++ b/js/src/jit/MIR.cpp @@ -906,7 +906,7 @@ MConstant::canProduceFloat32() const MDefinition* MSimdValueX4::foldsTo(TempAllocator& alloc) { - DebugOnly laneType = SimdTypeToLaneType(type()); + DebugOnly laneType = SimdTypeToLaneArgumentType(type()); bool allConstants = true; bool allSame = true; @@ -925,6 +925,13 @@ MSimdValueX4::foldsTo(TempAllocator& alloc) if (allConstants) { SimdConstant cst; switch (type()) { + case MIRType_Bool32x4: { + int32_t a[4]; + for (size_t i = 0; i < 4; ++i) + a[i] = getOperand(i)->constantToBoolean() ? -1 : 0; + cst = SimdConstant::CreateX4(a); + break; + } case MIRType_Int32x4: { int32_t a[4]; for (size_t i = 0; i < 4; ++i) @@ -952,7 +959,7 @@ MSimdValueX4::foldsTo(TempAllocator& alloc) MDefinition* MSimdSplatX4::foldsTo(TempAllocator& alloc) { - DebugOnly laneType = SimdTypeToLaneType(type()); + DebugOnly laneType = SimdTypeToLaneArgumentType(type()); MDefinition* op = getOperand(0); if (!op->isConstantValue()) return this; @@ -960,20 +967,19 @@ MSimdSplatX4::foldsTo(TempAllocator& alloc) SimdConstant cst; switch (type()) { + case MIRType_Bool32x4: { + int32_t v = op->constantToBoolean() ? -1 : 0; + cst = SimdConstant::SplatX4(v); + break; + } case MIRType_Int32x4: { - int32_t a[4]; - int32_t v = getOperand(0)->constantValue().toInt32(); - for (size_t i = 0; i < 4; ++i) - a[i] = v; - cst = SimdConstant::CreateX4(a); + int32_t v = op->constantValue().toInt32(); + cst = SimdConstant::SplatX4(v); break; } case MIRType_Float32x4: { - float a[4]; - float v = getOperand(0)->constantValue().toNumber(); - for (size_t i = 0; i < 4; ++i) - a[i] = v; - cst = SimdConstant::CreateX4(a); + float v = op->constantValue().toNumber(); + cst = SimdConstant::SplatX4(v); break; } default: MOZ_CRASH("unexpected type in MSimdSplatX4::foldsTo"); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 96f7e06c1533..3f0da427e86f 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -1401,7 +1401,7 @@ class MSimdValueX4 static MSimdValueX4* NewAsmJS(TempAllocator& alloc, MIRType type, MDefinition* x, MDefinition* y, MDefinition* z, MDefinition* w) { - mozilla::DebugOnly laneType = SimdTypeToLaneType(type); + mozilla::DebugOnly laneType = SimdTypeToLaneArgumentType(type); MOZ_ASSERT(laneType == x->type()); MOZ_ASSERT(laneType == y->type()); MOZ_ASSERT(laneType == z->type()); @@ -1445,7 +1445,7 @@ class MSimdSplatX4 static MSimdSplatX4* NewAsmJS(TempAllocator& alloc, MDefinition* v, MIRType type) { - MOZ_ASSERT(SimdTypeToLaneType(type) == v->type()); + MOZ_ASSERT(SimdTypeToLaneArgumentType(type) == v->type()); return new(alloc) MSimdSplatX4(type, v); } @@ -1493,6 +1493,9 @@ class MSimdConstant bool congruentTo(const MDefinition* ins) const override { if (!ins->isSimdConstant()) return false; + // Bool32x4 and Int32x4 share the same underlying SimdConstant representation. + if (type() != ins->type()) + return false; return value() == ins->toSimdConstant()->value(); } @@ -1602,7 +1605,10 @@ class MSimdExtractElement MOZ_ASSERT(IsSimdType(vecType)); MOZ_ASSERT(uint32_t(lane) < SimdTypeToLength(vecType)); MOZ_ASSERT(!IsSimdType(laneType)); - MOZ_ASSERT(SimdTypeToLaneType(vecType) == laneType); + // The resulting type should match the lane type. + // Allow extracting boolean lanes directly into an Int32 (for asm.js). + MOZ_ASSERT(SimdTypeToLaneType(vecType) == laneType || + (IsBooleanSimdType(vecType) && laneType == MIRType_Int32)); setMovable(); specialization_ = vecType; @@ -1665,7 +1671,7 @@ class MSimdInsertElement MIRType type, SimdLane lane) { MOZ_ASSERT(vec->type() == type); - MOZ_ASSERT(SimdTypeToLaneType(type) == val->type()); + MOZ_ASSERT(SimdTypeToLaneArgumentType(type) == val->type()); return new(alloc) MSimdInsertElement(vec, val, type, lane); } @@ -1752,6 +1758,83 @@ class MSimdSignMask ALLOW_CLONE(MSimdSignMask) }; +// Returns true if all lanes are true. +class MSimdAllTrue + : public MUnaryInstruction, + public SimdPolicy<0>::Data +{ + protected: + explicit MSimdAllTrue(MDefinition* obj, MIRType simdType, MIRType result) + : MUnaryInstruction(obj) + { + MOZ_ASSERT(result == MIRType_Boolean || result == MIRType_Int32); + setResultType(result); + specialization_ = simdType; + setMovable(); + } + + public: + INSTRUCTION_HEADER(SimdAllTrue) + + static MSimdAllTrue* NewAsmJS(TempAllocator& alloc, MDefinition* obj) + { + MOZ_ASSERT(IsSimdType(obj->type())); + return new(alloc) MSimdAllTrue(obj, obj->type(), MIRType_Int32); + } + + static MSimdAllTrue* New(TempAllocator& alloc, MDefinition* obj, MIRType type) + { + return new(alloc) MSimdAllTrue(obj, type, MIRType_Boolean); + } + + AliasSet getAliasSet() const override { + return AliasSet::None(); + } + bool congruentTo(const MDefinition* ins) const override { + return congruentIfOperandsEqual(ins); + } + ALLOW_CLONE(MSimdAllTrue) +}; + +// Returns true if any lane is true. +class MSimdAnyTrue + : public MUnaryInstruction, + public SimdPolicy<0>::Data +{ + protected: + explicit MSimdAnyTrue(MDefinition* obj, MIRType simdType, MIRType result) + : MUnaryInstruction(obj) + { + MOZ_ASSERT(result == MIRType_Boolean || result == MIRType_Int32); + setResultType(result); + specialization_ = simdType; + setMovable(); + } + + public: + INSTRUCTION_HEADER(SimdAnyTrue) + + static MSimdAnyTrue* NewAsmJS(TempAllocator& alloc, MDefinition* obj) + { + MOZ_ASSERT(IsSimdType(obj->type())); + return new(alloc) MSimdAnyTrue(obj, obj->type(), MIRType_Int32); + } + + static MSimdAnyTrue* New(TempAllocator& alloc, MDefinition* obj, MIRType type) + { + return new(alloc) MSimdAnyTrue(obj, type, MIRType_Boolean); + } + + AliasSet getAliasSet() const override { + return AliasSet::None(); + } + bool congruentTo(const MDefinition* ins) const override { + return congruentIfOperandsEqual(ins); + } + + ALLOW_CLONE(MSimdAnyTrue) +}; + // Base for the MSimdSwizzle and MSimdShuffle classes. class MSimdShuffleBase { @@ -2049,14 +2132,14 @@ class MSimdBinaryComp public: enum Operation { #define NAME_(x) x, - COMP_COMMONX4_TO_INT32X4_SIMD_OP(NAME_) + COMP_COMMONX4_TO_BOOL32X4_SIMD_OP(NAME_) #undef NAME_ }; static const char* OperationName(Operation op) { switch (op) { #define NAME_(x) case x: return #x; - COMP_COMMONX4_TO_INT32X4_SIMD_OP(NAME_) + COMP_COMMONX4_TO_BOOL32X4_SIMD_OP(NAME_) #undef NAME_ } MOZ_CRASH("unexpected operation"); @@ -2068,7 +2151,7 @@ class MSimdBinaryComp MSimdBinaryComp(MDefinition* left, MDefinition* right, Operation op, MIRType opType) : MBinaryInstruction(left, right), operation_(op) { - setResultType(MIRType_Int32x4); + setResultType(MIRType_Bool32x4); specialization_ = opType; setMovable(); if (op == equal || op == notEqual) @@ -2344,7 +2427,7 @@ class MSimdSelect static MSimdSelect* NewAsmJS(TempAllocator& alloc, MDefinition* mask, MDefinition* lhs, MDefinition* rhs, MIRType t, bool isElementWise) { - MOZ_ASSERT(mask->type() == MIRType_Int32x4); + MOZ_ASSERT(mask->type() == (isElementWise ? MIRType_Bool32x4 : t)); MOZ_ASSERT(lhs->type() == rhs->type()); MOZ_ASSERT(lhs->type() == t); return new(alloc) MSimdSelect(mask, lhs, rhs, t, isElementWise); diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index d1fcddbb5cf1..256b98e17d3a 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -31,6 +31,8 @@ namespace jit { _(SimdBinaryBitwise) \ _(SimdShift) \ _(SimdSelect) \ + _(SimdAllTrue) \ + _(SimdAnyTrue) \ _(CloneLiteral) \ _(Parameter) \ _(Callee) \ diff --git a/js/src/jit/Recover.cpp b/js/src/jit/Recover.cpp index bf7c3019a59c..6382af5ebb33 100644 --- a/js/src/jit/Recover.cpp +++ b/js/src/jit/Recover.cpp @@ -1339,6 +1339,11 @@ RSimdBox::recover(JSContext* cx, SnapshotIterator& iter) const MOZ_ASSERT(iter.allocationReadable(a)); const FloatRegisters::RegisterContent* raw = iter.floatAllocationPointer(a); switch (SimdTypeDescr::Type(type_)) { + case SimdTypeDescr::Bool32x4: + MOZ_ASSERT_IF(a.mode() == RValueAllocation::ANY_FLOAT_REG, + a.fpuReg().isSimd128()); + resultObject = js::CreateSimd(cx, (const Bool32x4::Elem*) raw); + break; case SimdTypeDescr::Int32x4: MOZ_ASSERT_IF(a.mode() == RValueAllocation::ANY_FLOAT_REG, a.fpuReg().isSimd128()); @@ -1364,9 +1369,6 @@ RSimdBox::recover(JSContext* cx, SnapshotIterator& iter) const case SimdTypeDescr::Bool16x8: MOZ_CRASH("NYI, RSimdBox of Bool16x8"); break; - case SimdTypeDescr::Bool32x4: - MOZ_CRASH("NYI, RSimdBox of Bool32x4"); - break; case SimdTypeDescr::Bool64x2: MOZ_CRASH("NYI, RSimdBox of Bool64x2"); break; diff --git a/js/src/jit/TypePolicy.cpp b/js/src/jit/TypePolicy.cpp index d79ac2f15763..00d9ff7ec206 100644 --- a/js/src/jit/TypePolicy.cpp +++ b/js/src/jit/TypePolicy.cpp @@ -570,6 +570,15 @@ SimdScalarPolicy::staticAdjustInputs(TempAllocator& alloc, MInstruction* ins MIRType laneType = SimdTypeToLaneType(ins->type()); MDefinition* in = ins->getOperand(Op); + + // A vector with boolean lanes requires Int32 inputs that have already been + // converted to 0/-1. + // We can't insert a MIRType_Boolean lane directly - it requires conversion. + if (laneType == MIRType_Boolean) { + MOZ_ASSERT(in->type() == MIRType_Int32, "Boolean SIMD vector requires Int32 lanes."); + return true; + } + if (in->type() == laneType) return true; @@ -857,8 +866,8 @@ SimdSelectPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) { MIRType specialization = ins->typePolicySpecialization(); - // First input is the mask, which has to be an int32x4 (for now). - if (!MaybeSimdUnbox(alloc, ins, MIRType_Int32x4, 0)) + // First input is the mask, which has to be a bool32x4. + if (!MaybeSimdUnbox(alloc, ins, MIRType_Bool32x4, 0)) return false; // Next inputs are the two vectors of a particular type. diff --git a/js/src/jit/shared/CodeGenerator-shared.cpp b/js/src/jit/shared/CodeGenerator-shared.cpp index 7ec90544736c..95ec41c5ca5c 100644 --- a/js/src/jit/shared/CodeGenerator-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-shared.cpp @@ -458,6 +458,7 @@ CodeGeneratorShared::encodeAllocation(LSnapshot* snapshot, MDefinition* mir, break; } case MIRType_Float32: + case MIRType_Bool32x4: case MIRType_Int32x4: case MIRType_Float32x4: { diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 77564f5abcc6..3358ed9e8551 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -250,6 +250,16 @@ class LSimdExtractElementBase : public LInstructionHelper<1, 1, 0> } }; +// Extracts an element from a given SIMD bool32x4 lane. +class LSimdExtractElementB : public LSimdExtractElementBase +{ + public: + LIR_HEADER(SimdExtractElementB); + explicit LSimdExtractElementB(const LAllocation& base) + : LSimdExtractElementBase(base) + {} +}; + // Extracts an element from a given SIMD int32x4 lane. class LSimdExtractElementI : public LSimdExtractElementBase { @@ -259,6 +269,7 @@ class LSimdExtractElementI : public LSimdExtractElementBase : LSimdExtractElementBase(base) {} }; + // Extracts an element from a given SIMD float32x4 lane. class LSimdExtractElementF : public LSimdExtractElementBase { @@ -293,7 +304,8 @@ class LSimdInsertElementBase : public LInstructionHelper<1, 2, 0> } }; -// Replace an element from a given SIMD int32x4 lane with a given value. +// Replace an element from a given SIMD integer or boolean lane with a given value. +// The value inserted into a boolean lane should be 0 or -1. class LSimdInsertElementI : public LSimdInsertElementBase { public: @@ -606,6 +618,37 @@ class LSimdSelect : public LInstructionHelper<1, 3, 1> } }; +class LSimdAnyTrue : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(SimdAnyTrue) + explicit LSimdAnyTrue(const LAllocation& input) { + setOperand(0, input); + } + const LAllocation* vector() { + return getOperand(0); + } + MSimdAnyTrue* mir() const { + return mir_->toSimdAnyTrue(); + } +}; + +class LSimdAllTrue : public LInstructionHelper<1, 1, 0> +{ + public: + LIR_HEADER(SimdAllTrue) + explicit LSimdAllTrue(const LAllocation& input) { + setOperand(0, input); + } + const LAllocation* vector() { + return getOperand(0); + } + MSimdAllTrue* mir() const { + return mir_->toSimdAllTrue(); + } +}; + + // Constant 32-bit integer. class LInteger : public LInstructionHelper<1, 0, 0> { @@ -693,7 +736,7 @@ class LFloat32 : public LInstructionHelper<1, 0, 0> } }; -// Constant SIMD int32x4 +// Constant SIMD int32x4. Also used for bool32x4. class LInt32x4 : public LInstructionHelper<1, 0, 0> { public: @@ -703,7 +746,7 @@ class LInt32x4 : public LInstructionHelper<1, 0, 0> const SimdConstant& getValue() const { return mir_->toSimdConstant()->value(); } }; -// Constant SIMD float32x4 +// Constant SIMD float32x4. class LFloat32x4 : public LInstructionHelper<1, 0, 0> { public: diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index 48e91e579e6a..7d24603a8e46 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -22,8 +22,11 @@ _(SimdSplatX4) \ _(Int32x4) \ _(Float32x4) \ + _(SimdAllTrue) \ + _(SimdAnyTrue) \ _(SimdReinterpretCast) \ _(SimdExtractElementI) \ + _(SimdExtractElementB) \ _(SimdExtractElementF) \ _(SimdInsertElementI) \ _(SimdInsertElementF) \ diff --git a/js/src/jit/shared/Lowering-shared-inl.h b/js/src/jit/shared/Lowering-shared-inl.h index c1fe853a915e..526f543203ad 100644 --- a/js/src/jit/shared/Lowering-shared-inl.h +++ b/js/src/jit/shared/Lowering-shared-inl.h @@ -159,6 +159,7 @@ LIRGeneratorShared::defineReturn(LInstruction* lir, MDefinition* mir) case MIRType_Double: lir->setDef(0, LDefinition(vreg, LDefinition::DOUBLE, LFloatReg(ReturnDoubleReg))); break; + case MIRType_Bool32x4: case MIRType_Int32x4: lir->setDef(0, LDefinition(vreg, LDefinition::INT32X4, LFloatReg(ReturnSimd128Reg))); break; diff --git a/js/src/jit/x64/Assembler-x64.cpp b/js/src/jit/x64/Assembler-x64.cpp index c5791b4b67aa..be3a0cd53848 100644 --- a/js/src/jit/x64/Assembler-x64.cpp +++ b/js/src/jit/x64/Assembler-x64.cpp @@ -54,6 +54,7 @@ ABIArgGenerator::next(MIRType type) case MIRType_Double: current_ = ABIArg(FloatArgRegs[regIndex_++]); break; + case MIRType_Bool32x4: case MIRType_Int32x4: case MIRType_Float32x4: // On Win64, >64 bit args need to be passed by reference, but asm.js @@ -88,6 +89,7 @@ ABIArgGenerator::next(MIRType type) else current_ = ABIArg(FloatArgRegs[floatRegIndex_++]); break; + case MIRType_Bool32x4: case MIRType_Int32x4: case MIRType_Float32x4: if (floatRegIndex_ == NumFloatArgRegs) { diff --git a/js/src/jit/x64/CodeGenerator-x64.cpp b/js/src/jit/x64/CodeGenerator-x64.cpp index 05f60a4c599b..de46357a4d4d 100644 --- a/js/src/jit/x64/CodeGenerator-x64.cpp +++ b/js/src/jit/x64/CodeGenerator-x64.cpp @@ -751,6 +751,7 @@ CodeGeneratorX64::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins) // Aligned access: code is aligned on PageSize + there is padding // before the global data section. case MIRType_Int32x4: + case MIRType_Bool32x4: label = masm.loadRipRelativeInt32x4(ToFloatRegister(ins->output())); break; case MIRType_Float32x4: @@ -785,6 +786,7 @@ CodeGeneratorX64::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins) // Aligned access: code is aligned on PageSize + there is padding // before the global data section. case MIRType_Int32x4: + case MIRType_Bool32x4: label = masm.storeRipRelativeInt32x4(ToFloatRegister(ins->value())); break; case MIRType_Float32x4: diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp index 2835a47893be..cebf7dd95304 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp @@ -287,6 +287,7 @@ CodeGeneratorX86Shared::visitAsmJSPassStackArg(LAsmJSPassStackArg* ins) // StackPointer is SIMD-aligned and ABIArgGenerator guarantees // stack offsets are SIMD-aligned. case MIRType_Int32x4: + case MIRType_Bool32x4: masm.storeAlignedInt32x4(ToFloatRegister(ins->arg()), dst); return; case MIRType_Float32x4: @@ -2308,7 +2309,7 @@ CodeGeneratorX86Shared::visitOutOfLineSimdFloatToIntCheck(OutOfLineSimdFloatToIn void CodeGeneratorX86Shared::visitSimdValueInt32x4(LSimdValueInt32x4* ins) { - MOZ_ASSERT(ins->mir()->type() == MIRType_Int32x4); + MOZ_ASSERT(ins->mir()->type() == MIRType_Int32x4 || ins->mir()->type() == MIRType_Bool32x4); FloatRegister output = ToFloatRegister(ins->output()); if (AssemblerX86Shared::HasSSE41()) { @@ -2359,7 +2360,8 @@ CodeGeneratorX86Shared::visitSimdSplatX4(LSimdSplatX4* ins) JS_STATIC_ASSERT(sizeof(float) == sizeof(int32_t)); switch (mir->type()) { - case MIRType_Int32x4: { + case MIRType_Int32x4: + case MIRType_Bool32x4: { Register r = ToRegister(ins->getOperand(0)); masm.vmovd(r, output); masm.vpshufd(0, output, output); @@ -2397,6 +2399,28 @@ CodeGeneratorX86Shared::visitSimdReinterpretCast(LSimdReinterpretCast* ins) } } +void +CodeGeneratorX86Shared::visitSimdExtractElementB(LSimdExtractElementB* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + Register output = ToRegister(ins->output()); + + SimdLane lane = ins->lane(); + if (lane == LaneX) { + // The value we want to extract is in the low double-word + masm.moveLowInt32(input, output); + } else if (AssemblerX86Shared::HasSSE41()) { + masm.vpextrd(lane, input, output); + } else { + uint32_t mask = MacroAssembler::ComputeShuffleMask(lane); + masm.shuffleInt32(mask, input, ScratchSimd128Reg); + masm.moveLowInt32(ScratchSimd128Reg, output); + } + + // We need to generate a 0/1 value. We have 0/-1. + masm.and32(Imm32(1), output); +} + void CodeGeneratorX86Shared::visitSimdExtractElementI(LSimdExtractElementI* ins) { @@ -2508,6 +2532,28 @@ CodeGeneratorX86Shared::visitSimdSignMaskX4(LSimdSignMaskX4* ins) masm.vmovmskps(input, output); } +void +CodeGeneratorX86Shared::visitSimdAllTrue(LSimdAllTrue* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + Register output = ToRegister(ins->output()); + + masm.vmovmskps(input, output); + masm.cmp32(output, Imm32(0xf)); + masm.emitSet(Assembler::Zero, output); +} + +void +CodeGeneratorX86Shared::visitSimdAnyTrue(LSimdAnyTrue* ins) +{ + FloatRegister input = ToFloatRegister(ins->input()); + Register output = ToRegister(ins->output()); + + masm.vmovmskps(input, output); + masm.cmp32(output, Imm32(0x0)); + masm.emitSet(Assembler::NonZero, output); +} + template void CodeGeneratorX86Shared::visitSimdGeneralShuffle(LSimdGeneralShuffleBase* ins, Reg tempRegister) { diff --git a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h index 1672916e7b14..e43020b3b7ee 100644 --- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h +++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h @@ -262,6 +262,7 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared void visitInt32x4ToFloat32x4(LInt32x4ToFloat32x4* ins); void visitFloat32x4ToInt32x4(LFloat32x4ToInt32x4* ins); void visitSimdReinterpretCast(LSimdReinterpretCast* lir); + void visitSimdExtractElementB(LSimdExtractElementB* lir); void visitSimdExtractElementI(LSimdExtractElementI* lir); void visitSimdExtractElementF(LSimdExtractElementF* lir); void visitSimdInsertElementI(LSimdInsertElementI* lir); @@ -279,6 +280,8 @@ class CodeGeneratorX86Shared : public CodeGeneratorShared void visitSimdBinaryBitwiseX4(LSimdBinaryBitwiseX4* lir); void visitSimdShift(LSimdShift* lir); void visitSimdSelect(LSimdSelect* ins); + void visitSimdAllTrue(LSimdAllTrue* ins); + void visitSimdAnyTrue(LSimdAnyTrue* ins); template void visitSimdGeneralShuffle(LSimdGeneralShuffleBase* lir, Reg temp); void visitSimdGeneralShuffleI(LSimdGeneralShuffleI* lir); diff --git a/js/src/jit/x86-shared/Lowering-x86-shared.cpp b/js/src/jit/x86-shared/Lowering-x86-shared.cpp index 2eccb198439b..3864cdabb070 100644 --- a/js/src/jit/x86-shared/Lowering-x86-shared.cpp +++ b/js/src/jit/x86-shared/Lowering-x86-shared.cpp @@ -639,6 +639,7 @@ LIRGeneratorX86Shared::visitSimdSplatX4(MSimdSplatX4* ins) switch (ins->type()) { case MIRType_Int32x4: + case MIRType_Bool32x4: define(lir, ins); break; case MIRType_Float32x4: @@ -656,7 +657,8 @@ LIRGeneratorX86Shared::visitSimdSplatX4(MSimdSplatX4* ins) void LIRGeneratorX86Shared::visitSimdValueX4(MSimdValueX4* ins) { - if (ins->type() == MIRType_Float32x4) { + switch (ins->type()) { + case MIRType_Float32x4: { // Ideally, x would be used at start and reused for the output, however // register allocation currently doesn't permit us to tie together two // virtual registers with different types. @@ -666,14 +668,19 @@ LIRGeneratorX86Shared::visitSimdValueX4(MSimdValueX4* ins) LAllocation w = useRegister(ins->getOperand(3)); LDefinition t = temp(LDefinition::FLOAT32X4); define(new (alloc()) LSimdValueFloat32x4(x, y, z, w, t), ins); - } else { - MOZ_ASSERT(ins->type() == MIRType_Int32x4); - + break; + } + case MIRType_Bool32x4: + case MIRType_Int32x4: { // No defineReuseInput => useAtStart for everyone. LAllocation x = useRegisterAtStart(ins->getOperand(0)); LAllocation y = useRegisterAtStart(ins->getOperand(1)); LAllocation z = useRegisterAtStart(ins->getOperand(2)); LAllocation w = useRegisterAtStart(ins->getOperand(3)); define(new(alloc()) LSimdValueInt32x4(x, y, z, w), ins); + break; + } + default: + MOZ_CRASH("Unknown SIMD kind"); } } diff --git a/js/src/jit/x86/Assembler-x86.cpp b/js/src/jit/x86/Assembler-x86.cpp index 971bb6b8e2da..6c5a32436b6f 100644 --- a/js/src/jit/x86/Assembler-x86.cpp +++ b/js/src/jit/x86/Assembler-x86.cpp @@ -32,6 +32,7 @@ ABIArgGenerator::next(MIRType type) break; case MIRType_Int32x4: case MIRType_Float32x4: + case MIRType_Bool32x4: // SIMD values aren't passed in or out of C++, so we can make up // whatever internal ABI we like. visitAsmJSPassArg assumes // SimdMemoryAlignment. diff --git a/js/src/jit/x86/CodeGenerator-x86.cpp b/js/src/jit/x86/CodeGenerator-x86.cpp index 41d36d30985a..b0439469c95d 100644 --- a/js/src/jit/x86/CodeGenerator-x86.cpp +++ b/js/src/jit/x86/CodeGenerator-x86.cpp @@ -792,6 +792,7 @@ CodeGeneratorX86::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar* ins) // Aligned access: code is aligned on PageSize + there is padding // before the global data section. case MIRType_Int32x4: + case MIRType_Bool32x4: label = masm.vmovdqaWithPatch(PatchedAbsoluteAddress(), ToFloatRegister(ins->output())); break; case MIRType_Float32x4: @@ -825,6 +826,7 @@ CodeGeneratorX86::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar* ins) // Aligned access: code is aligned on PageSize + there is padding // before the global data section. case MIRType_Int32x4: + case MIRType_Bool32x4: label = masm.vmovdqaWithPatch(ToFloatRegister(ins->value()), PatchedAbsoluteAddress()); break; case MIRType_Float32x4: