☠☠ backed out by 90f74b01a4a5 ☠ ☠ | |
author | Nicholas D. Matsakis <nmatsakis@mozilla.com> |
Thu, 20 Mar 2014 10:04:46 -0400 | |
changeset 176931 | 2daa537b62e6ec4a213061d0a8edbf4c1796cf9f |
parent 176930 | d87a13b71afc89264e66a87f09a8904ec4886df6 |
child 176974 | 6a9a14232208275ff2a9df4b119c06960e53ae0d |
push id | 41879 |
push user | nmatsakis@mozilla.com |
push date | Thu, 03 Apr 2014 18:54:34 +0000 |
treeherder | mozilla-inbound@2daa537b62e6 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | shu |
bugs | 977126 |
milestone | 31.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -2210,28 +2210,34 @@ TypedObject::obj_enumerate(JSContext *cx } break; } return true; } /* static */ size_t -TypedObject::ownerOffset() +TypedObject::offsetOfOwnerSlot() { return JSObject::getFixedSlotOffset(JS_TYPEDOBJ_SLOT_OWNER); } /* static */ size_t -TypedObject::dataOffset() +TypedObject::offsetOfDataSlot() { // the offset of 7 is based on the alloc kind return JSObject::getPrivateDataOffset(JS_TYPEDOBJ_SLOT_DATA); } +/* static */ size_t +TypedObject::offsetOfByteOffsetSlot() +{ + return JSObject::getFixedSlotOffset(JS_TYPEDOBJ_SLOT_BYTEOFFSET); +} + void TypedObject::neuter(void *newData) { setSlot(JS_TYPEDOBJ_SLOT_LENGTH, Int32Value(0)); setSlot(JS_TYPEDOBJ_SLOT_BYTELENGTH, Int32Value(0)); setSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(0)); setPrivate(newData); } @@ -2707,22 +2713,31 @@ js::SetTypedObjectOffset(ThreadSafeConte TypedObject &typedObj = args[0].toObject().as<TypedObject>(); int32_t offset = args[1].toInt32(); JS_ASSERT(!typedObj.owner().isNeutered()); JS_ASSERT(typedObj.typedMem() != nullptr); // must be attached already typedObj.setPrivate(typedObj.owner().dataPointer() + offset); typedObj.setReservedSlot(JS_TYPEDOBJ_SLOT_BYTEOFFSET, Int32Value(offset)); + args.rval().setUndefined(); return true; } -JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::SetTypedObjectOffsetJitInfo, +bool +js::intrinsic_SetTypedObjectOffset(JSContext *cx, unsigned argc, Value *vp) +{ + // Do not use JSNativeThreadSafeWrapper<> so that ion can reference + // this function more easily when inlining. + return SetTypedObjectOffset(cx, argc, vp); +} + +JS_JITINFO_NATIVE_PARALLEL_THREADSAFE(js::intrinsic_SetTypedObjectOffsetJitInfo, SetTypedObjectJitInfo, - js::SetTypedObjectOffset); + SetTypedObjectOffset); bool js::ObjectIsTypeDescr(ThreadSafeContext *, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); JS_ASSERT(args.length() == 1); JS_ASSERT(args[0].isObject()); args.rval().setBoolean(args[0].toObject().is<TypeDescr>());
--- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -580,25 +580,28 @@ class TypedObject : public ArrayBufferVi bool *succeeded); static bool obj_deleteElement(JSContext *cx, HandleObject obj, uint32_t index, bool *succeeded); static bool obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op, MutableHandleValue statep, MutableHandleId idp); public: - static size_t ownerOffset(); + static size_t offsetOfOwnerSlot(); // Each typed object contains a void* pointer pointing at the // binary data that it represents. (That data may be owned by this // object or this object may alias data owned by someone else.) // This function returns the offset in bytes within the object // where the `void*` pointer can be found. It is intended for use // by the JIT. - static size_t dataOffset(); + static size_t offsetOfDataSlot(); + + // Offset of the byte offset slot. + static size_t offsetOfByteOffsetSlot(); // Helper for createUnattached() static TypedObject *createUnattachedWithClass(JSContext *cx, const Class *clasp, HandleTypeDescr type, int32_t length); // Creates an unattached typed object or handle (depending on the @@ -737,18 +740,19 @@ bool AttachTypedObject(ThreadSafeContext extern const JSJitInfo AttachTypedObjectJitInfo; /* * Usage: SetTypedObjectOffset(typedObj, offset) * * Changes the offset for `typedObj` within its buffer to `offset`. * `typedObj` must already be attached. */ -bool SetTypedObjectOffset(ThreadSafeContext *cx, unsigned argc, Value *vp); -extern const JSJitInfo SetTypedObjectOffsetJitInfo; +bool intrinsic_SetTypedObjectOffset(JSContext *cx, unsigned argc, Value *vp); +bool SetTypedObjectOffset(ThreadSafeContext *, unsigned argc, Value *vp); +extern const JSJitInfo intrinsic_SetTypedObjectOffsetJitInfo; /* * Usage: ObjectIsTypeDescr(obj) * * True if `obj` is a type object. */ bool ObjectIsTypeDescr(ThreadSafeContext *cx, unsigned argc, Value *vp); extern const JSJitInfo ObjectIsTypeDescrJitInfo;
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -4107,31 +4107,80 @@ CodeGenerator::visitTypedArrayElements(L } bool CodeGenerator::visitNeuterCheck(LNeuterCheck *lir) { Register obj = ToRegister(lir->object()); Register temp = ToRegister(lir->temp()); - masm.extractObject(Address(obj, TypedObject::ownerOffset()), temp); + masm.extractObject(Address(obj, TypedObject::offsetOfOwnerSlot()), temp); masm.unboxInt32(Address(temp, ArrayBufferObject::flagsOffset()), temp); masm.and32(Imm32(ArrayBufferObject::neuteredFlag()), temp); if (!bailoutIf(Assembler::NonZero, lir->snapshot())) return false; return true; } bool CodeGenerator::visitTypedObjectElements(LTypedObjectElements *lir) { Register obj = ToRegister(lir->object()); Register out = ToRegister(lir->output()); - masm.loadPtr(Address(obj, TypedObject::dataOffset()), out); + masm.loadPtr(Address(obj, TypedObject::offsetOfDataSlot()), out); + return true; +} + +bool +CodeGenerator::visitSetTypedObjectOffset(LSetTypedObjectOffset *lir) +{ + Register object = ToRegister(lir->object()); + Register offset = ToRegister(lir->offset()); + Register temp0 = ToRegister(lir->temp0()); + + // `offset` is an absolute offset into the base buffer. One way + // to implement this instruction would be load the base address + // from the buffer and add `offset`. But that'd be an extra load. + // We can instead load the current base pointer and current + // offset, compute the difference with `offset`, and then adjust + // the current base pointer. This is two loads but to adjacent + // fields in the same object, which should come in the same cache + // line. + // + // The C code I would probably write is the following: + // + // void SetTypedObjectOffset(TypedObject *obj, int32_t offset) { + // int32_t temp0 = obj->byteOffset; + // obj->pointer = obj->pointer - temp0 + offset; + // obj->byteOffset = offset; + // } + // + // But what we actually compute is more like this, because it + // saves us a temporary to do it this way: + // + // void SetTypedObjectOffset(TypedObject *obj, int32_t offset) { + // int32_t temp0 = obj->byteOffset; + // obj->pointer = obj->pointer - (temp0 - offset); + // obj->byteOffset = offset; + // } + + // temp0 = typedObj->byteOffset; + masm.unboxInt32(Address(object, TypedObject::offsetOfByteOffsetSlot()), temp0); + + // temp0 -= offset; + masm.subPtr(offset, temp0); + + // obj->pointer -= temp0; + masm.subPtr(temp0, Address(object, TypedObject::offsetOfDataSlot())); + + // obj->byteOffset = offset; + masm.storeValue(JSVAL_TYPE_INT32, offset, + Address(object, TypedObject::offsetOfByteOffsetSlot())); + return true; } bool CodeGenerator::visitStringLength(LStringLength *lir) { Register input = ToRegister(lir->string()); Register output = ToRegister(lir->output());
--- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -162,16 +162,17 @@ class CodeGenerator : public CodeGenerat bool visitComputeThis(LComputeThis *lir); bool visitLoadArrowThis(LLoadArrowThis *lir); bool visitArrayLength(LArrayLength *lir); bool visitSetArrayLength(LSetArrayLength *lir); bool visitTypedArrayLength(LTypedArrayLength *lir); bool visitTypedArrayElements(LTypedArrayElements *lir); bool visitNeuterCheck(LNeuterCheck *lir); bool visitTypedObjectElements(LTypedObjectElements *lir); + bool visitSetTypedObjectOffset(LSetTypedObjectOffset *lir); bool visitStringLength(LStringLength *lir); bool visitInitializedLength(LInitializedLength *lir); bool visitSetInitializedLength(LSetInitializedLength *lir); bool visitNotO(LNotO *ins); bool visitNotV(LNotV *ins); bool visitBoundsCheck(LBoundsCheck *lir); bool visitBoundsCheckRange(LBoundsCheckRange *lir); bool visitBoundsCheckLower(LBoundsCheckLower *lir);
--- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -691,16 +691,17 @@ class IonBuilder : public MIRGenerator InliningStatus inlineUnsafeSetReservedSlot(CallInfo &callInfo); InliningStatus inlineUnsafeGetReservedSlot(CallInfo &callInfo); // ForkJoin intrinsics InliningStatus inlineForkJoinGetSlice(CallInfo &callInfo); // TypedObject intrinsics. InliningStatus inlineObjectIsTypeDescr(CallInfo &callInfo); + InliningStatus inlineSetTypedObjectOffset(CallInfo &callInfo); bool elementAccessIsTypedObjectArrayOfScalarType(MDefinition* obj, MDefinition* id, ScalarTypeDescr::Type *arrayType); // Utility intrinsics. InliningStatus inlineIsCallable(CallInfo &callInfo); InliningStatus inlineHaveSameClass(CallInfo &callInfo); InliningStatus inlineToObject(CallInfo &callInfo); InliningStatus inlineDump(CallInfo &callInfo);
--- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -3720,16 +3720,41 @@ class LTypedObjectElements : public LIns const LAllocation *object() { return getOperand(0); } const MTypedObjectElements *mir() const { return mir_->toTypedObjectElements(); } }; +// Load a typed array's elements vector. +class LSetTypedObjectOffset : public LInstructionHelper<0, 2, 1> +{ + public: + LIR_HEADER(SetTypedObjectOffset) + + LSetTypedObjectOffset(const LAllocation &object, + const LAllocation &offset, + const LDefinition &temp0) + { + setOperand(0, object); + setOperand(1, offset); + setTemp(0, temp0); + } + const LAllocation *object() { + return getOperand(0); + } + const LAllocation *offset() { + return getOperand(1); + } + const LDefinition *temp0() { + return getTemp(0); + } +}; + // Check whether a typed object has a neutered owner buffer. class LNeuterCheck : public LInstructionHelper<0, 1, 1> { public: LIR_HEADER(NeuterCheck) LNeuterCheck(const LAllocation &object, const LDefinition &temp) { setOperand(0, object);
--- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -244,16 +244,17 @@ _(IteratorNext) \ _(IteratorMore) \ _(IteratorEnd) \ _(ArrayLength) \ _(SetArrayLength) \ _(TypedArrayLength) \ _(TypedArrayElements) \ _(TypedObjectElements) \ + _(SetTypedObjectOffset) \ _(StringLength) \ _(ArgumentsLength) \ _(GetFrameArgument) \ _(SetFrameArgumentT) \ _(SetFrameArgumentC) \ _(SetFrameArgumentV) \ _(RunOncePrologue) \ _(Rest) \
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2374,16 +2374,26 @@ LIRGenerator::visitTypedArrayElements(MT bool LIRGenerator::visitTypedObjectElements(MTypedObjectElements *ins) { JS_ASSERT(ins->type() == MIRType_Elements); return define(new(alloc()) LTypedObjectElements(useRegisterAtStart(ins->object())), ins); } bool +LIRGenerator::visitSetTypedObjectOffset(MSetTypedObjectOffset *ins) +{ + return add(new(alloc()) LSetTypedObjectOffset( + useRegister(ins->object()), + useRegister(ins->offset()), + temp()), + ins); +} + +bool LIRGenerator::visitInitializedLength(MInitializedLength *ins) { JS_ASSERT(ins->elements()->type() == MIRType_Elements); return define(new(alloc()) LInitializedLength(useRegisterAtStart(ins->elements())), ins); } bool LIRGenerator::visitSetInitializedLength(MSetInitializedLength *ins)
--- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -173,16 +173,17 @@ class LIRGenerator : public LIRGenerator bool visitMonitorTypes(MMonitorTypes *ins); bool visitPostWriteBarrier(MPostWriteBarrier *ins); bool visitArrayLength(MArrayLength *ins); bool visitSetArrayLength(MSetArrayLength *ins); bool visitTypedArrayLength(MTypedArrayLength *ins); bool visitTypedArrayElements(MTypedArrayElements *ins); bool visitNeuterCheck(MNeuterCheck *lir); bool visitTypedObjectElements(MTypedObjectElements *ins); + bool visitSetTypedObjectOffset(MSetTypedObjectOffset *ins); bool visitInitializedLength(MInitializedLength *ins); bool visitSetInitializedLength(MSetInitializedLength *ins); bool visitNot(MNot *ins); bool visitBoundsCheck(MBoundsCheck *ins); bool visitBoundsCheckLower(MBoundsCheckLower *ins); bool visitLoadElement(MLoadElement *ins); bool visitLoadElementHole(MLoadElementHole *ins); bool visitStoreElement(MStoreElement *ins);
--- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -2,16 +2,17 @@ * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "jsmath.h" #include "builtin/TestingFunctions.h" +#include "builtin/TypedObject.h" #include "jit/BaselineInspector.h" #include "jit/IonBuilder.h" #include "jit/Lowering.h" #include "jit/MIR.h" #include "jit/MIRGraph.h" #include "jsscriptinlines.h" @@ -172,16 +173,18 @@ IonBuilder::inlineNativeCall(CallInfo &c &ScalarTypeDescr::class_, &ReferenceTypeDescr::class_); if (native == intrinsic_TypeDescrIsArrayType) return inlineHasClasses(callInfo, &SizedArrayTypeDescr::class_, &UnsizedArrayTypeDescr::class_); if (native == intrinsic_TypeDescrIsSizedArrayType) return inlineHasClass(callInfo, &SizedArrayTypeDescr::class_); if (native == intrinsic_TypeDescrIsUnsizedArrayType) return inlineHasClass(callInfo, &UnsizedArrayTypeDescr::class_); + if (native == intrinsic_SetTypedObjectOffset) + return inlineSetTypedObjectOffset(callInfo); // Testing Functions if (native == testingFunc_inParallelSection) return inlineForceSequentialOrInParallelSection(callInfo); if (native == testingFunc_bailout) return inlineBailout(callInfo); if (native == testingFunc_assertFloat32) return inlineAssertFloat32(callInfo); @@ -1645,16 +1648,58 @@ IonBuilder::inlineObjectIsTypeDescr(Call pushConstant(BooleanValue(result)); callInfo.setImplicitlyUsedUnchecked(); return InliningStatus_Inlined; } IonBuilder::InliningStatus +IonBuilder::inlineSetTypedObjectOffset(CallInfo &callInfo) +{ + if (callInfo.argc() != 2 || callInfo.constructing()) + return InliningStatus_NotInlined; + + MDefinition *typedObj = callInfo.getArg(0); + MDefinition *offset = callInfo.getArg(1); + + // Return type should be undefined or something wacky is going on. + if (getInlineReturnType() != MIRType_Undefined) + return InliningStatus_NotInlined; + + // Check typedObj is a, well, typed object. Go ahead and use TI + // data. If this check should fail, that is almost certainly a bug + // in self-hosted code -- either because it's not being careful + // with TI or because of something else -- but we'll just let it + // fall through to the SetTypedObjectOffset intrinsic in such + // cases. + types::TemporaryTypeSet *types = typedObj->resultTypeSet(); + if (typedObj->type() != MIRType_Object || !types) + return InliningStatus_NotInlined; + switch (types->forAllClasses(IsTypedObjectClass)) { + case types::TemporaryTypeSet::ForAllResult::ALL_FALSE: + case types::TemporaryTypeSet::ForAllResult::EMPTY: + case types::TemporaryTypeSet::ForAllResult::MIXED: + return InliningStatus_NotInlined; + case types::TemporaryTypeSet::ForAllResult::ALL_TRUE: + break; + } + + // Check type of offset argument is an integer. + if (offset->type() != MIRType_Int32) + return InliningStatus_NotInlined; + + callInfo.setImplicitlyUsedUnchecked(); + MInstruction *ins = MSetTypedObjectOffset::New(alloc(), typedObj, offset); + current->add(ins); + current->push(ins); + return InliningStatus_Inlined; +} + +IonBuilder::InliningStatus IonBuilder::inlineUnsafeSetReservedSlot(CallInfo &callInfo) { if (callInfo.argc() != 3 || callInfo.constructing()) return InliningStatus_NotInlined; if (getInlineReturnType() != MIRType_Undefined) return InliningStatus_NotInlined; if (callInfo.getArg(0)->type() != MIRType_Object) return InliningStatus_NotInlined;
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -1556,17 +1556,17 @@ class MNewDerivedTypedObject }; // Abort parallel execution. class MAbortPar : public MAryControlInstruction<0, 0> { MAbortPar() : MAryControlInstruction<0, 0>() { - setResultType(MIRType_Undefined); + setResultType(MIRType_None); setGuard(); } public: INSTRUCTION_HEADER(AbortPar); static MAbortPar *New(TempAllocator &alloc) { return new(alloc) MAbortPar(); @@ -5588,16 +5588,54 @@ class MTypedObjectElements bool congruentTo(MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { return AliasSet::Load(AliasSet::ObjectFields); } }; +// Inlined version of the js::SetTypedObjectOffset() intrinsic. +class MSetTypedObjectOffset + : public MBinaryInstruction +{ + private: + MSetTypedObjectOffset(MDefinition *object, MDefinition *offset) + : MBinaryInstruction(object, offset) + { + JS_ASSERT(object->type() == MIRType_Object); + JS_ASSERT(offset->type() == MIRType_Int32); + setResultType(MIRType_None); + } + + public: + INSTRUCTION_HEADER(SetTypedObjectOffset) + + static MSetTypedObjectOffset *New(TempAllocator &alloc, + MDefinition *object, + MDefinition *offset) + { + return new(alloc) MSetTypedObjectOffset(object, offset); + } + + MDefinition *object() const { + return getOperand(0); + } + + MDefinition *offset() const { + return getOperand(1); + } + + AliasSet getAliasSet() const { + // This affects the result of MTypedObjectElements, + // which is described as a load of ObjectFields. + return AliasSet::Store(AliasSet::ObjectFields); + } +}; + // Perform !-operation class MNot : public MUnaryInstruction, public TestPolicy { bool operandMightEmulateUndefined_; bool operandIsNeverNaN_;
--- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -130,16 +130,17 @@ namespace jit { _(GuardObjectType) \ _(GuardObjectIdentity) \ _(GuardClass) \ _(ArrayLength) \ _(SetArrayLength) \ _(TypedArrayLength) \ _(TypedArrayElements) \ _(TypedObjectElements) \ + _(SetTypedObjectOffset) \ _(InitializedLength) \ _(SetInitializedLength) \ _(Not) \ _(NeuterCheck) \ _(BoundsCheck) \ _(BoundsCheckLower) \ _(InArray) \ _(LoadElement) \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp +++ b/js/src/jit/ParallelSafetyAnalysis.cpp @@ -218,16 +218,17 @@ class ParallelSafetyVisitor : public MIn SAFE_OP(GuardObjectIdentity) SAFE_OP(GuardClass) SAFE_OP(AssertRange) SAFE_OP(ArrayLength) WRITE_GUARDED_OP(SetArrayLength, elements) SAFE_OP(TypedArrayLength) SAFE_OP(TypedArrayElements) SAFE_OP(TypedObjectElements) + SAFE_OP(SetTypedObjectOffset) SAFE_OP(InitializedLength) WRITE_GUARDED_OP(SetInitializedLength, elements) SAFE_OP(Not) SAFE_OP(NeuterCheck) SAFE_OP(BoundsCheck) SAFE_OP(BoundsCheckLower) SAFE_OP(LoadElement) SAFE_OP(LoadElementHole)
--- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -743,18 +743,18 @@ static const JSFunctionSpec intrinsic_fu 1, 0), JS_FN("NewDerivedTypedObject", js::NewDerivedTypedObject, 3, 0), JS_FNINFO("AttachTypedObject", JSNativeThreadSafeWrapper<js::AttachTypedObject>, &js::AttachTypedObjectJitInfo, 3, 0), JS_FNINFO("SetTypedObjectOffset", - JSNativeThreadSafeWrapper<js::SetTypedObjectOffset>, - &js::SetTypedObjectOffsetJitInfo, 2, 0), + intrinsic_SetTypedObjectOffset, + &js::intrinsic_SetTypedObjectOffsetJitInfo, 2, 0), JS_FNINFO("ObjectIsTypeDescr", intrinsic_ObjectIsTypeDescr, &js::ObjectIsTypeDescrJitInfo, 1, 0), JS_FNINFO("ObjectIsTypedObject", intrinsic_ObjectIsTypedObject, &js::ObjectIsTypedObjectJitInfo, 1, 0), JS_FNINFO("ObjectIsTransparentTypedObject", intrinsic_ObjectIsTransparentTypedObject,