From 0cb9c4ed18ad2da28eb7a2a2cbefe4e8db565e7b Mon Sep 17 00:00:00 2001 From: "Nicolas B. Pierron" Date: Mon, 26 Jan 2015 12:22:55 +0100 Subject: [PATCH] Bug 1112156 - Add MSimdUnbox to extract SIMD values from the TypedObjects. r=bbouvier --- js/src/jit-test/tests/ion/simd-unbox.js | 108 ++++++++++++++++++++++++ js/src/jit/CodeGenerator.cpp | 68 +++++++++++++++ js/src/jit/CodeGenerator.h | 1 + js/src/jit/LIR-Common.h | 20 +++++ js/src/jit/LOpcodes.h | 1 + js/src/jit/Lowering.cpp | 24 ++++++ js/src/jit/Lowering.h | 1 + js/src/jit/MIR.h | 27 ++++++ js/src/jit/MOpcodes.h | 1 + js/src/jit/MacroAssembler.cpp | 14 +++ js/src/jit/MacroAssembler.h | 4 + js/src/jit/arm/MacroAssembler-arm.h | 3 + js/src/jit/mips/MacroAssembler-mips.h | 3 +- js/src/jit/x64/MacroAssembler-x64.h | 4 + 14 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 js/src/jit-test/tests/ion/simd-unbox.js diff --git a/js/src/jit-test/tests/ion/simd-unbox.js b/js/src/jit-test/tests/ion/simd-unbox.js new file mode 100644 index 000000000000..0e62b0465148 --- /dev/null +++ b/js/src/jit-test/tests/ion/simd-unbox.js @@ -0,0 +1,108 @@ +if (typeof SIMD === "undefined") + quit(); + +setJitCompilerOption("baseline.warmup.trigger", 10); +setJitCompilerOption("ion.warmup.trigger", 30); +var max = 40, pivot = 35; + +var i32x4 = SIMD.int32x4; +var f32x4 = SIMD.float32x4; +var i32x4Add = SIMD.int32x4.add; + +var FakeSIMDType = function (o) { this.x = o.x; this.y = o.y; this.z = o.z; this.w = o.w; }; +if (this.hasOwnProperty("TypedObject")) { + var TO = TypedObject; + FakeSIMDType = new TO.StructType({ x: TO.int32, y: TO.int32, z: TO.int32, w: TO.int32 }); +} + + +function simdunbox_bail_undef(i, lhs, rhs) { + return i32x4Add(lhs, rhs); +} + +function simdunbox_bail_object(i, lhs, rhs) { + return i32x4Add(lhs, rhs); +} + +function simdunbox_bail_typeobj(i, lhs, rhs) { + return i32x4Add(lhs, rhs); +} + +function simdunbox_bail_badsimd(i, lhs, rhs) { + return i32x4Add(lhs, rhs); +} + +var arr_undef = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ]; +var fail_undef = 0; +var arr_object = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ]; +var fail_object = 0; +var arr_typeobj = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ]; +var fail_typeobj = 0; +var arr_badsimd = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ]; +var fail_badsimd = 0; +for (var i = 0; i < max; i++) { + try { + arr_undef[i + 2] = simdunbox_bail_undef(i, arr_undef[i], arr_undef[i + 1]); + } catch (x) { + arr_undef[i + 2] = arr_undef[i - 1]; + fail_undef++; + } + + try { + arr_object[i + 2] = simdunbox_bail_object(i, arr_object[i], arr_object[i + 1]); + } catch (x) { + arr_object[i + 2] = arr_object[i - 1]; + fail_object++; + } + + try { + arr_typeobj[i + 2] = simdunbox_bail_typeobj(i, arr_typeobj[i], arr_typeobj[i + 1]); + } catch (x) { + arr_typeobj[i + 2] = arr_typeobj[i - 1]; + fail_typeobj++; + } + + try { + arr_badsimd[i + 2] = simdunbox_bail_badsimd(i, arr_badsimd[i], arr_badsimd[i + 1]); + } catch (x) { + arr_badsimd[i + 2] = arr_badsimd[i - 1]; + fail_badsimd++; + } + + if (i + 2 == pivot) { + arr_undef[pivot] = undefined; + arr_object[pivot] = { x: 0, y: 1, z: 2, w: 3 }; + arr_typeobj[pivot] = new FakeSIMDType({ x: 0, y: 1, z: 2, w: 3 }); + arr_badsimd[pivot] = f32x4(0, 1, 2, 3); + } +} + +assertEq(fail_undef, 2); +assertEq(fail_object, 2); +assertEq(fail_typeobj, 2); +assertEq(fail_badsimd, 2); + +// Assert that all SIMD values are correct. +function assertEqX4(real, expected, assertFunc) { + if (typeof assertFunc === 'undefined') + assertFunc = assertEq; + + assertFunc(real.x, expected[0]); + assertFunc(real.y, expected[1]); + assertFunc(real.z, expected[2]); + assertFunc(real.w, expected[3]); +} + +var fib = [0, 1]; +for (i = 0; i < max + 5; i++) + fib[i+2] = (fib[i] + fib[i+1]) | 0; + +for (i = 0; i < max; i++) { + if (i == pivot) + continue; + var ref = fib.slice(i < pivot ? i : i - 3); + assertEqX4(arr_undef[i], ref); + assertEqX4(arr_object[i], ref); + assertEqX4(arr_typeobj[i], ref); + assertEqX4(arr_badsimd[i], ref); +} diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index f3d685bf6170..66f3e56b375f 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -4233,6 +4233,74 @@ CodeGenerator::visitSimdBox(LSimdBox *lir) } } +void +CodeGenerator::visitSimdUnbox(LSimdUnbox *lir) +{ + Register object = ToRegister(lir->input()); + FloatRegister simd = ToFloatRegister(lir->output()); + Register temp = ToRegister(lir->temp()); + Label bail; + + // obj->type() + masm.loadPtr(Address(object, JSObject::offsetOfType()), temp); + + // Guard that the object has the same representation as the one produced for + // SIMD value-type. + Address clasp(temp, types::TypeObject::offsetOfClasp()); + static_assert(!SimdTypeDescr::Opaque, "SIMD objects are transparent"); + masm.branchPtr(Assembler::NotEqual, clasp, ImmPtr(&InlineTransparentTypedObject::class_), + &bail); + + // obj->type()->typeDescr() + // The previous class pointer comparison implies that the addendumKind is + // Addendum_TypeDescr. + masm.loadPtr(Address(temp, types::TypeObject::offsetOfAddendum()), temp); + + // Check for the /Kind/ reserved slot of the TypeDescr. This is an Int32 + // Value which is equivalent to the object class check. + static_assert(JS_DESCR_SLOT_KIND < NativeObject::MAX_FIXED_SLOTS, "Load from fixed slots"); + Address typeDescrKind(temp, NativeObject::getFixedSlotOffset(JS_DESCR_SLOT_KIND)); + masm.assertTestInt32(Assembler::Equal, typeDescrKind, + "MOZ_ASSERT(obj->type()->typeDescr()->getReservedSlot(JS_DESCR_SLOT_KIND).isInt32())"); + masm.branch32(Assembler::NotEqual, masm.ToPayload(typeDescrKind), Imm32(js::type::Simd), &bail); + + // Convert the SIMD MIRType to a SimdTypeDescr::Type. + js::SimdTypeDescr::Type type; + switch (lir->mir()->type()) { + case MIRType_Int32x4: + type = js::SimdTypeDescr::TYPE_INT32; + break; + case MIRType_Float32x4: + type = js::SimdTypeDescr::TYPE_FLOAT32; + break; + default: + MOZ_CRASH("Unexpected SIMD Type."); + } + + // Check if the SimdTypeDescr /Type/ match the specialization of this + // MSimdUnbox instruction. + static_assert(JS_DESCR_SLOT_TYPE < NativeObject::MAX_FIXED_SLOTS, "Load from fixed slots"); + Address typeDescrType(temp, NativeObject::getFixedSlotOffset(JS_DESCR_SLOT_TYPE)); + masm.assertTestInt32(Assembler::Equal, typeDescrType, + "MOZ_ASSERT(obj->type()->typeDescr()->getReservedSlot(JS_DESCR_SLOT_TYPE).isInt32())"); + masm.branch32(Assembler::NotEqual, masm.ToPayload(typeDescrType), Imm32(type), &bail); + + // Load the value from the data of the InlineTypedObject. + Address objectData(object, InlineTypedObject::offsetOfDataStart()); + switch (lir->mir()->type()) { + case MIRType_Int32x4: + masm.loadUnalignedInt32x4(objectData, simd); + break; + case MIRType_Float32x4: + masm.loadUnalignedFloat32x4(objectData, simd); + break; + default: + MOZ_CRASH("The impossible happened!"); + } + + bailoutFrom(&bail, lir->snapshot()); +} + typedef js::DeclEnvObject *(*NewDeclEnvObjectFn)(JSContext *, HandleFunction, gc::InitialHeap); static const VMFunction NewDeclEnvObjectInfo = FunctionInfo(DeclEnvObject::createTemplateObject); diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 60dacf547459..5c5824a32cc5 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -156,6 +156,7 @@ class CodeGenerator : public CodeGeneratorSpecific void visitOutOfLineNewObject(OutOfLineNewObject *ool); void visitNewTypedObject(LNewTypedObject *lir); void visitSimdBox(LSimdBox *lir); + void visitSimdUnbox(LSimdUnbox *lir); void visitNewDeclEnvObject(LNewDeclEnvObject *lir); void visitNewCallObject(LNewCallObject *lir); void visitNewSingletonCallObject(LNewSingletonCallObject *lir); diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index d58c97dac0f7..a0eae739cd05 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -157,6 +157,26 @@ class LSimdBox : public LInstructionHelper<1, 1, 1> } }; +class LSimdUnbox : public LInstructionHelper<1, 1, 1> +{ + public: + LIR_HEADER(SimdUnbox) + + LSimdUnbox(const LAllocation &obj, const LDefinition &temp) + { + setOperand(0, obj); + setTemp(0, temp); + } + + const LDefinition *temp() { + return getTemp(0); + } + + MSimdUnbox *mir() const { + return mir_->toSimdUnbox(); + } +}; + // Constructs a SIMD value with 4 equal components (e.g. int32x4, float32x4). class LSimdSplatX4 : public LInstructionHelper<1, 1, 0> { diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index 6df9f485a41b..916fa64f04bd 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -18,6 +18,7 @@ _(Double) \ _(Float32) \ _(SimdBox) \ + _(SimdUnbox) \ _(SimdSplatX4) \ _(Int32x4) \ _(Float32x4) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index d647f4d75cbb..5f276c119699 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -3734,6 +3734,30 @@ LIRGenerator::visitSimdBox(MSimdBox *ins) define(lir, ins); } +void +LIRGenerator::visitSimdUnbox(MSimdUnbox *ins) +{ + MOZ_ASSERT(ins->input()->type() == MIRType_Object); + MOZ_ASSERT(IsSimdType(ins->type())); + LUse in = useRegister(ins->input()); + + BailoutKind kind; + switch (ins->type()) { + case MIRType_Int32x4: + kind = Bailout_NonSimdInt32x4Input; + break; + case MIRType_Float32x4: + kind = Bailout_NonSimdFloat32x4Input; + break; + default: + MOZ_CRASH("Unexpected SIMD Type."); + }; + + LSimdUnbox *lir = new(alloc()) LSimdUnbox(in, temp()); + assignSnapshot(lir, kind); + define(lir, ins); +} + void LIRGenerator::visitSimdConstant(MSimdConstant *ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index 69f8ed575328..8eb8f588de63 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -266,6 +266,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitRecompileCheck(MRecompileCheck *ins); void visitMemoryBarrier(MMemoryBarrier *ins); void visitSimdBox(MSimdBox *ins); + void visitSimdUnbox(MSimdUnbox *ins); void visitSimdExtractElement(MSimdExtractElement *ins); void visitSimdInsertElement(MSimdInsertElement *ins); void visitSimdSignMask(MSimdSignMask *ins); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index a88172b288f1..25aa92107caf 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -2984,6 +2984,33 @@ class MSimdBox } }; +class MSimdUnbox + : public MUnaryInstruction, + public SingleObjectPolicy::Data +{ + protected: + MSimdUnbox(MDefinition *op, MIRType type) + : MUnaryInstruction(op) + { + MOZ_ASSERT(IsSimdType(type)); + setMovable(); + setResultType(type); + } + + public: + INSTRUCTION_HEADER(SimdUnbox) + ALLOW_CLONE(MSimdUnbox) + + static MSimdUnbox *New(TempAllocator &alloc, MDefinition *op, MIRType type) + { + return new(alloc) MSimdUnbox(op, type); + } + + AliasSet getAliasSet() const MOZ_OVERRIDE { + return AliasSet::None(); + } +}; + // Creates a new derived type object. At runtime, this is just a call // to `BinaryBlock::createDerived()`. That is, the MIR itself does not // compile to particularly optimized code. However, using a distinct diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index 3b8c18d278c6..789908f5ea8d 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -13,6 +13,7 @@ namespace jit { #define MIR_OPCODE_LIST(_) \ _(Constant) \ _(SimdBox) \ + _(SimdUnbox) \ _(SimdValueX4) \ _(SimdSplatX4) \ _(SimdConstant) \ diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index da4ba853bebd..4e0718df9f50 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -1356,6 +1356,20 @@ MacroAssembler::assumeUnreachable(const char *output) breakpoint(); } +template +void +MacroAssembler::assertTestInt32(Condition cond, const T &value, const char *output) +{ +#ifdef DEBUG + Label ok; + branchTestInt32(cond, value, &ok); + assumeUnreachable(output); + bind(&ok); +#endif +} + +template void MacroAssembler::assertTestInt32(Condition, const Address &, const char *); + static void Printf0_(const char *output) { // Use stderr instead of stdout because this is only used for debug diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index 16fc75324f3b..5b6133724396 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -976,6 +976,10 @@ class MacroAssembler : public MacroAssemblerSpecific void link(JitCode *code); void assumeUnreachable(const char *output); + + template + void assertTestInt32(Condition cond, const T &value, const char *output); + void printf(const char *output); void printf(const char *output, Register value); diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h index 1ee51fcf3ae8..8b136666e6bb 100644 --- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -38,6 +38,7 @@ class MacroAssemblerARM : public Assembler // address. Register secondScratchReg_; + public: // Higher level tag testing code. Operand ToPayload(Operand base) { return Operand(Register::FromCode(base.base()), base.disp()); @@ -45,6 +46,8 @@ class MacroAssemblerARM : public Assembler Address ToPayload(Address base) { return ToPayload(Operand(base)).toAddress(); } + + protected: Operand ToType(Operand base) { return Operand(Register::FromCode(base.base()), base.disp() + sizeof(void *)); } diff --git a/js/src/jit/mips/MacroAssembler-mips.h b/js/src/jit/mips/MacroAssembler-mips.h index 68243c39b10d..4fa2cec01930 100644 --- a/js/src/jit/mips/MacroAssembler-mips.h +++ b/js/src/jit/mips/MacroAssembler-mips.h @@ -67,12 +67,13 @@ static_assert(1 << defaultShift == sizeof(jsval), "The defaultShift is wrong"); class MacroAssemblerMIPS : public Assembler { - protected: + public: // higher level tag testing code Operand ToPayload(Operand base); Address ToPayload(Address base) { return ToPayload(Operand(base)).toAddress(); } + protected: Operand ToType(Operand base); Address ToType(Address base) { return ToType(Operand(base)).toAddress(); diff --git a/js/src/jit/x64/MacroAssembler-x64.h b/js/src/jit/x64/MacroAssembler-x64.h index d618d69be256..83f1d79f8d49 100644 --- a/js/src/jit/x64/MacroAssembler-x64.h +++ b/js/src/jit/x64/MacroAssembler-x64.h @@ -161,6 +161,10 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared ///////////////////////////////////////////////////////////////// // X86/X64-common interface. ///////////////////////////////////////////////////////////////// + Address ToPayload(Address value) { + return value; + } + void storeValue(ValueOperand val, Operand dest) { movq(val.valueReg(), dest); }