diff --git a/js/src/builtin/TypedObject.cpp b/js/src/builtin/TypedObject.cpp index 62f1bca7d2c..b2cc4fa0d94 100644 --- a/js/src/builtin/TypedObject.cpp +++ b/js/src/builtin/TypedObject.cpp @@ -2215,18 +2215,24 @@ TypedObject::obj_enumerate(JSContext *cx, HandleObject obj, JSIterateOp enum_op, } /* 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) { @@ -2712,12 +2718,21 @@ js::SetTypedObjectOffset(ThreadSafeContext *, unsigned argc, Value *vp) 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) diff --git a/js/src/builtin/TypedObject.h b/js/src/builtin/TypedObject.h index 2574fa308ae..a0bcdaca135 100644 --- a/js/src/builtin/TypedObject.h +++ b/js/src/builtin/TypedObject.h @@ -585,7 +585,7 @@ class TypedObject : public ArrayBufferViewObject 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 @@ -593,7 +593,10 @@ class TypedObject : public ArrayBufferViewObject // 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, @@ -742,8 +745,9 @@ extern const JSJitInfo AttachTypedObjectJitInfo; * 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) diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 697e3c4ace5..6ccef14d9bb 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -4112,7 +4112,7 @@ 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); @@ -4126,7 +4126,56 @@ 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; } diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 9580ec3c58f..3a0482c6673 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -167,6 +167,7 @@ class CodeGenerator : public CodeGeneratorSpecific 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); diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index a81a2a59116..285faa02e39 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -696,6 +696,7 @@ class IonBuilder : public MIRGenerator // TypedObject intrinsics. InliningStatus inlineObjectIsTypeDescr(CallInfo &callInfo); + InliningStatus inlineSetTypedObjectOffset(CallInfo &callInfo); bool elementAccessIsTypedObjectArrayOfScalarType(MDefinition* obj, MDefinition* id, ScalarTypeDescr::Type *arrayType); diff --git a/js/src/jit/LIR-Common.h b/js/src/jit/LIR-Common.h index 5cdfc389c28..0605e4203b7 100644 --- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -3725,6 +3725,31 @@ class LTypedObjectElements : public LInstructionHelper<1, 1, 0> } }; +// 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> { diff --git a/js/src/jit/LOpcodes.h b/js/src/jit/LOpcodes.h index c47b8941219..54f2ce16227 100644 --- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -249,6 +249,7 @@ _(TypedArrayLength) \ _(TypedArrayElements) \ _(TypedObjectElements) \ + _(SetTypedObjectOffset) \ _(StringLength) \ _(ArgumentsLength) \ _(GetFrameArgument) \ diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 7f171215b88..17aae384194 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2378,6 +2378,16 @@ LIRGenerator::visitTypedObjectElements(MTypedObjectElements *ins) 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) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index d4b5e1de060..b64332a9f6f 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -178,6 +178,7 @@ class LIRGenerator : public LIRGeneratorSpecific 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); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 3872ecfea2a..86205c43316 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -7,6 +7,7 @@ #include "jsmath.h" #include "builtin/TestingFunctions.h" +#include "builtin/TypedObject.h" #include "jit/BaselineInspector.h" #include "jit/IonBuilder.h" #include "jit/Lowering.h" @@ -177,6 +178,8 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSNative native) 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) @@ -1649,6 +1652,48 @@ IonBuilder::inlineObjectIsTypeDescr(CallInfo &callInfo) 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) { diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 0d1f5d29b11..60c0ebee4a3 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -1561,7 +1561,7 @@ class MAbortPar : public MAryControlInstruction<0, 0> MAbortPar() : MAryControlInstruction<0, 0>() { - setResultType(MIRType_Undefined); + setResultType(MIRType_None); setGuard(); } @@ -5593,6 +5593,44 @@ class MTypedObjectElements } }; +// 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, diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index aca841f7307..f003b9d6c84 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -135,6 +135,7 @@ namespace jit { _(TypedArrayLength) \ _(TypedArrayElements) \ _(TypedObjectElements) \ + _(SetTypedObjectOffset) \ _(InitializedLength) \ _(SetInitializedLength) \ _(Not) \ diff --git a/js/src/jit/ParallelSafetyAnalysis.cpp b/js/src/jit/ParallelSafetyAnalysis.cpp index 09b6230b0ca..b5e5fed17ae 100644 --- a/js/src/jit/ParallelSafetyAnalysis.cpp +++ b/js/src/jit/ParallelSafetyAnalysis.cpp @@ -223,6 +223,7 @@ class ParallelSafetyVisitor : public MInstructionVisitor SAFE_OP(TypedArrayLength) SAFE_OP(TypedArrayElements) SAFE_OP(TypedObjectElements) + SAFE_OP(SetTypedObjectOffset) SAFE_OP(InitializedLength) WRITE_GUARDED_OP(SetInitializedLength, elements) SAFE_OP(Not) diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index ddd9d255563..a6e8d77f0f0 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -748,8 +748,8 @@ static const JSFunctionSpec intrinsic_functions[] = { JSNativeThreadSafeWrapper, &js::AttachTypedObjectJitInfo, 3, 0), JS_FNINFO("SetTypedObjectOffset", - JSNativeThreadSafeWrapper, - &js::SetTypedObjectOffsetJitInfo, 2, 0), + intrinsic_SetTypedObjectOffset, + &js::intrinsic_SetTypedObjectOffsetJitInfo, 2, 0), JS_FNINFO("ObjectIsTypeDescr", intrinsic_ObjectIsTypeDescr, &js::ObjectIsTypeDescrJitInfo, 1, 0),