summaryrefslogtreecommitdiffstats
path: root/js/src/jit/MacroAssembler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/jit/MacroAssembler.cpp')
-rw-r--r--js/src/jit/MacroAssembler.cpp309
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), &notNull);
+
+ moveValue(NullValue(), output.valueReg());
+ jump(&done);
+
+ bind(&notNull);
+ 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, &notInt32);
+ int32ValueToDouble(reg, ScratchDoubleReg);
+ storeDouble(ScratchDoubleReg, address);
+ jump(&end);
+ bind(&notInt32);
+ 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)
{