diff options
Diffstat (limited to 'js/src/jit/MacroAssembler.cpp')
-rw-r--r-- | js/src/jit/MacroAssembler.cpp | 309 |
1 files changed, 307 insertions, 2 deletions
diff --git a/js/src/jit/MacroAssembler.cpp b/js/src/jit/MacroAssembler.cpp index a739b9325..f633b9b7b 100644 --- a/js/src/jit/MacroAssembler.cpp +++ b/js/src/jit/MacroAssembler.cpp @@ -126,14 +126,20 @@ MacroAssembler::guardTypeSetMightBeIncomplete(TypeSet* types, Register obj, Regi { // Type set guards might miss when an object's group changes. In this case // either its old group's properties will become unknown, or it will change - // to a native object. Jump to label if this might have happened for the - // input object. + // to a native object with an original unboxed group. Jump to label if this + // might have happened for the input object. if (types->unknownObject()) { jump(label); return; } + loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch); + load32(Address(scratch, ObjectGroup::offsetOfFlags()), scratch); + and32(Imm32(OBJECT_FLAG_ADDENDUM_MASK), scratch); + branch32(Assembler::Equal, + scratch, Imm32(ObjectGroup::addendumOriginalUnboxedGroupValue()), label); + for (size_t i = 0; i < types->getObjectCount(); i++) { if (JSObject* singleton = types->getSingletonNoBarrier(i)) { movePtr(ImmGCPtr(singleton), scratch); @@ -462,6 +468,268 @@ template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const A template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const BaseIndex& src, const ValueOperand& dest, bool allowDouble, Register temp, Label* fail); +template <typename T> +void +MacroAssembler::loadUnboxedProperty(T address, JSValueType type, TypedOrValueRegister output) +{ + switch (type) { + case JSVAL_TYPE_INT32: { + // Handle loading an int32 into a double reg. + if (output.type() == MIRType::Double) { + convertInt32ToDouble(address, output.typedReg().fpu()); + break; + } + MOZ_FALLTHROUGH; + } + + case JSVAL_TYPE_BOOLEAN: + case JSVAL_TYPE_STRING: { + Register outReg; + if (output.hasValue()) { + outReg = output.valueReg().scratchReg(); + } else { + MOZ_ASSERT(output.type() == MIRTypeFromValueType(type)); + outReg = output.typedReg().gpr(); + } + + switch (type) { + case JSVAL_TYPE_BOOLEAN: + load8ZeroExtend(address, outReg); + break; + case JSVAL_TYPE_INT32: + load32(address, outReg); + break; + case JSVAL_TYPE_STRING: + loadPtr(address, outReg); + break; + default: + MOZ_CRASH(); + } + + if (output.hasValue()) + tagValue(type, outReg, output.valueReg()); + break; + } + + case JSVAL_TYPE_OBJECT: + if (output.hasValue()) { + Register scratch = output.valueReg().scratchReg(); + loadPtr(address, scratch); + + Label notNull, done; + branchPtr(Assembler::NotEqual, scratch, ImmWord(0), ¬Null); + + moveValue(NullValue(), output.valueReg()); + jump(&done); + + bind(¬Null); + tagValue(JSVAL_TYPE_OBJECT, scratch, output.valueReg()); + + bind(&done); + } else { + // Reading null can't be possible here, as otherwise the result + // would be a value (either because null has been read before or + // because there is a barrier). + Register reg = output.typedReg().gpr(); + loadPtr(address, reg); +#ifdef DEBUG + Label ok; + branchTestPtr(Assembler::NonZero, reg, reg, &ok); + assumeUnreachable("Null not possible"); + bind(&ok); +#endif + } + break; + + case JSVAL_TYPE_DOUBLE: + // Note: doubles in unboxed objects are not accessed through other + // views and do not need canonicalization. + if (output.hasValue()) + loadValue(address, output.valueReg()); + else + loadDouble(address, output.typedReg().fpu()); + break; + + default: + MOZ_CRASH(); + } +} + +template void +MacroAssembler::loadUnboxedProperty(Address address, JSValueType type, + TypedOrValueRegister output); + +template void +MacroAssembler::loadUnboxedProperty(BaseIndex address, JSValueType type, + TypedOrValueRegister output); + +static void +StoreUnboxedFailure(MacroAssembler& masm, Label* failure) +{ + // Storing a value to an unboxed property is a fallible operation and + // the caller must provide a failure label if a particular unboxed store + // might fail. Sometimes, however, a store that cannot succeed (such as + // storing a string to an int32 property) will be marked as infallible. + // This can only happen if the code involved is unreachable. + if (failure) + masm.jump(failure); + else + masm.assumeUnreachable("Incompatible write to unboxed property"); +} + +template <typename T> +void +MacroAssembler::storeUnboxedProperty(T address, JSValueType type, + const ConstantOrRegister& value, Label* failure) +{ + switch (type) { + case JSVAL_TYPE_BOOLEAN: + if (value.constant()) { + if (value.value().isBoolean()) + store8(Imm32(value.value().toBoolean()), address); + else + StoreUnboxedFailure(*this, failure); + } else if (value.reg().hasTyped()) { + if (value.reg().type() == MIRType::Boolean) + store8(value.reg().typedReg().gpr(), address); + else + StoreUnboxedFailure(*this, failure); + } else { + if (failure) + branchTestBoolean(Assembler::NotEqual, value.reg().valueReg(), failure); + storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ 1); + } + break; + + case JSVAL_TYPE_INT32: + if (value.constant()) { + if (value.value().isInt32()) + store32(Imm32(value.value().toInt32()), address); + else + StoreUnboxedFailure(*this, failure); + } else if (value.reg().hasTyped()) { + if (value.reg().type() == MIRType::Int32) + store32(value.reg().typedReg().gpr(), address); + else + StoreUnboxedFailure(*this, failure); + } else { + if (failure) + branchTestInt32(Assembler::NotEqual, value.reg().valueReg(), failure); + storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ 4); + } + break; + + case JSVAL_TYPE_DOUBLE: + if (value.constant()) { + if (value.value().isNumber()) { + loadConstantDouble(value.value().toNumber(), ScratchDoubleReg); + storeDouble(ScratchDoubleReg, address); + } else { + StoreUnboxedFailure(*this, failure); + } + } else if (value.reg().hasTyped()) { + if (value.reg().type() == MIRType::Int32) { + convertInt32ToDouble(value.reg().typedReg().gpr(), ScratchDoubleReg); + storeDouble(ScratchDoubleReg, address); + } else if (value.reg().type() == MIRType::Double) { + storeDouble(value.reg().typedReg().fpu(), address); + } else { + StoreUnboxedFailure(*this, failure); + } + } else { + ValueOperand reg = value.reg().valueReg(); + Label notInt32, end; + branchTestInt32(Assembler::NotEqual, reg, ¬Int32); + int32ValueToDouble(reg, ScratchDoubleReg); + storeDouble(ScratchDoubleReg, address); + jump(&end); + bind(¬Int32); + if (failure) + branchTestDouble(Assembler::NotEqual, reg, failure); + storeValue(reg, address); + bind(&end); + } + break; + + case JSVAL_TYPE_OBJECT: + if (value.constant()) { + if (value.value().isObjectOrNull()) + storePtr(ImmGCPtr(value.value().toObjectOrNull()), address); + else + StoreUnboxedFailure(*this, failure); + } else if (value.reg().hasTyped()) { + MOZ_ASSERT(value.reg().type() != MIRType::Null); + if (value.reg().type() == MIRType::Object) + storePtr(value.reg().typedReg().gpr(), address); + else + StoreUnboxedFailure(*this, failure); + } else { + if (failure) { + Label ok; + branchTestNull(Assembler::Equal, value.reg().valueReg(), &ok); + branchTestObject(Assembler::NotEqual, value.reg().valueReg(), failure); + bind(&ok); + } + storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ sizeof(uintptr_t)); + } + break; + + case JSVAL_TYPE_STRING: + if (value.constant()) { + if (value.value().isString()) + storePtr(ImmGCPtr(value.value().toString()), address); + else + StoreUnboxedFailure(*this, failure); + } else if (value.reg().hasTyped()) { + if (value.reg().type() == MIRType::String) + storePtr(value.reg().typedReg().gpr(), address); + else + StoreUnboxedFailure(*this, failure); + } else { + if (failure) + branchTestString(Assembler::NotEqual, value.reg().valueReg(), failure); + storeUnboxedPayload(value.reg().valueReg(), address, /* width = */ sizeof(uintptr_t)); + } + break; + + default: + MOZ_CRASH(); + } +} + +template void +MacroAssembler::storeUnboxedProperty(Address address, JSValueType type, + const ConstantOrRegister& value, Label* failure); + +template void +MacroAssembler::storeUnboxedProperty(BaseIndex address, JSValueType type, + const ConstantOrRegister& value, Label* failure); + +void +MacroAssembler::checkUnboxedArrayCapacity(Register obj, const RegisterOrInt32Constant& index, + Register temp, Label* failure) +{ + Address initLengthAddr(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength()); + Address lengthAddr(obj, UnboxedArrayObject::offsetOfLength()); + + Label capacityIsIndex, done; + load32(initLengthAddr, temp); + branchTest32(Assembler::NonZero, temp, Imm32(UnboxedArrayObject::CapacityMask), &capacityIsIndex); + branch32(Assembler::BelowOrEqual, lengthAddr, index, failure); + jump(&done); + bind(&capacityIsIndex); + + // Do a partial shift so that we can get an absolute offset from the base + // of CapacityArray to use. + JS_STATIC_ASSERT(sizeof(UnboxedArrayObject::CapacityArray[0]) == 4); + rshiftPtr(Imm32(UnboxedArrayObject::CapacityShift - 2), temp); + and32(Imm32(~0x3), temp); + + addPtr(ImmPtr(&UnboxedArrayObject::CapacityArray), temp); + branch32(Assembler::BelowOrEqual, Address(temp, 0), index, failure); + bind(&done); +} + // Inlined version of gc::CheckAllocatorState that checks the bare essentials // and bails for anything that cannot be handled with our jit allocators. void @@ -1009,6 +1277,20 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj, nbytes = (nbytes < sizeof(uintptr_t)) ? 0 : nbytes - sizeof(uintptr_t); offset += sizeof(uintptr_t); } + } else if (templateObj->is<UnboxedPlainObject>()) { + storePtr(ImmWord(0), Address(obj, UnboxedPlainObject::offsetOfExpando())); + if (initContents) + initUnboxedObjectContents(obj, &templateObj->as<UnboxedPlainObject>()); + } else if (templateObj->is<UnboxedArrayObject>()) { + MOZ_ASSERT(templateObj->as<UnboxedArrayObject>().hasInlineElements()); + int elementsOffset = UnboxedArrayObject::offsetOfInlineElements(); + computeEffectiveAddress(Address(obj, elementsOffset), temp); + storePtr(temp, Address(obj, UnboxedArrayObject::offsetOfElements())); + store32(Imm32(templateObj->as<UnboxedArrayObject>().length()), + Address(obj, UnboxedArrayObject::offsetOfLength())); + uint32_t capacityIndex = templateObj->as<UnboxedArrayObject>().capacityIndex(); + store32(Imm32(capacityIndex << UnboxedArrayObject::CapacityShift), + Address(obj, UnboxedArrayObject::offsetOfCapacityIndexAndInitializedLength())); } else { MOZ_CRASH("Unknown object"); } @@ -1030,6 +1312,29 @@ MacroAssembler::initGCThing(Register obj, Register temp, JSObject* templateObj, } void +MacroAssembler::initUnboxedObjectContents(Register object, UnboxedPlainObject* templateObject) +{ + const UnboxedLayout& layout = templateObject->layoutDontCheckGeneration(); + + // Initialize reference fields of the object, per UnboxedPlainObject::create. + if (const int32_t* list = layout.traceList()) { + while (*list != -1) { + storePtr(ImmGCPtr(GetJitContext()->runtime->names().empty), + Address(object, UnboxedPlainObject::offsetOfData() + *list)); + list++; + } + list++; + while (*list != -1) { + storePtr(ImmWord(0), + Address(object, UnboxedPlainObject::offsetOfData() + *list)); + list++; + } + // Unboxed objects don't have Values to initialize. + MOZ_ASSERT(*(list + 1) == -1); + } +} + +void MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register result, Label* fail) { |