michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jit/IonMacroAssembler.h" michael@0: michael@0: #include "jsinfer.h" michael@0: #include "jsprf.h" michael@0: michael@0: #include "builtin/TypedObject.h" michael@0: #include "jit/Bailouts.h" michael@0: #include "jit/BaselineFrame.h" michael@0: #include "jit/BaselineIC.h" michael@0: #include "jit/BaselineJIT.h" michael@0: #include "jit/Lowering.h" michael@0: #include "jit/MIR.h" michael@0: #include "jit/ParallelFunctions.h" michael@0: #include "vm/ForkJoin.h" michael@0: #include "vm/TraceLogging.h" michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: # include "jsgcinlines.h" michael@0: #endif michael@0: #include "jsinferinlines.h" michael@0: #include "jsobjinlines.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: using JS::GenericNaN; michael@0: michael@0: namespace { michael@0: michael@0: // Emulate a TypeSet logic from a Type object to avoid duplicating the guard michael@0: // logic. michael@0: class TypeWrapper { michael@0: types::Type t_; michael@0: michael@0: public: michael@0: TypeWrapper(types::Type t) : t_(t) {} michael@0: michael@0: inline bool unknown() const { michael@0: return t_.isUnknown(); michael@0: } michael@0: inline bool hasType(types::Type t) const { michael@0: if (t == types::Type::Int32Type()) michael@0: return t == t_ || t_ == types::Type::DoubleType(); michael@0: return t == t_; michael@0: } michael@0: inline unsigned getObjectCount() const { michael@0: if (t_.isAnyObject() || t_.isUnknown() || !t_.isObject()) michael@0: return 0; michael@0: return 1; michael@0: } michael@0: inline JSObject *getSingleObject(unsigned) const { michael@0: if (t_.isSingleObject()) michael@0: return t_.singleObject(); michael@0: return nullptr; michael@0: } michael@0: inline types::TypeObject *getTypeObject(unsigned) const { michael@0: if (t_.isTypeObject()) michael@0: return t_.typeObject(); michael@0: return nullptr; michael@0: } michael@0: }; michael@0: michael@0: } /* anonymous namespace */ michael@0: michael@0: template void michael@0: MacroAssembler::guardTypeSet(const Source &address, const TypeSet *types, michael@0: Register scratch, Label *miss) michael@0: { michael@0: JS_ASSERT(!types->unknown()); michael@0: michael@0: Label matched; michael@0: types::Type tests[7] = { michael@0: types::Type::Int32Type(), michael@0: types::Type::UndefinedType(), michael@0: types::Type::BooleanType(), michael@0: types::Type::StringType(), michael@0: types::Type::NullType(), michael@0: types::Type::MagicArgType(), michael@0: types::Type::AnyObjectType() michael@0: }; michael@0: michael@0: // The double type also implies Int32. michael@0: // So replace the int32 test with the double one. michael@0: if (types->hasType(types::Type::DoubleType())) { michael@0: JS_ASSERT(types->hasType(types::Type::Int32Type())); michael@0: tests[0] = types::Type::DoubleType(); michael@0: } michael@0: michael@0: Register tag = extractTag(address, scratch); michael@0: michael@0: // Emit all typed tests. michael@0: BranchType lastBranch; michael@0: for (size_t i = 0; i < 7; i++) { michael@0: if (!types->hasType(tests[i])) michael@0: continue; michael@0: michael@0: if (lastBranch.isInitialized()) michael@0: lastBranch.emit(*this); michael@0: lastBranch = BranchType(Equal, tag, tests[i], &matched); michael@0: } michael@0: michael@0: // If this is the last check, invert the last branch. michael@0: if (types->hasType(types::Type::AnyObjectType()) || !types->getObjectCount()) { michael@0: if (!lastBranch.isInitialized()) { michael@0: jump(miss); michael@0: return; michael@0: } michael@0: michael@0: lastBranch.invertCondition(); michael@0: lastBranch.relink(miss); michael@0: lastBranch.emit(*this); michael@0: michael@0: bind(&matched); michael@0: return; michael@0: } michael@0: michael@0: if (lastBranch.isInitialized()) michael@0: lastBranch.emit(*this); michael@0: michael@0: // Test specific objects. michael@0: JS_ASSERT(scratch != InvalidReg); michael@0: branchTestObject(NotEqual, tag, miss); michael@0: Register obj = extractObject(address, scratch); michael@0: guardObjectType(obj, types, scratch, miss); michael@0: michael@0: bind(&matched); michael@0: } michael@0: michael@0: template void michael@0: MacroAssembler::guardObjectType(Register obj, const TypeSet *types, michael@0: Register scratch, Label *miss) michael@0: { michael@0: JS_ASSERT(!types->unknown()); michael@0: JS_ASSERT(!types->hasType(types::Type::AnyObjectType())); michael@0: JS_ASSERT(types->getObjectCount()); michael@0: JS_ASSERT(scratch != InvalidReg); michael@0: michael@0: Label matched; michael@0: michael@0: BranchGCPtr lastBranch; michael@0: JS_ASSERT(!lastBranch.isInitialized()); michael@0: bool hasTypeObjects = false; michael@0: unsigned count = types->getObjectCount(); michael@0: for (unsigned i = 0; i < count; i++) { michael@0: if (!types->getSingleObject(i)) { michael@0: hasTypeObjects = hasTypeObjects || types->getTypeObject(i); michael@0: continue; michael@0: } michael@0: michael@0: if (lastBranch.isInitialized()) michael@0: lastBranch.emit(*this); michael@0: michael@0: JSObject *object = types->getSingleObject(i); michael@0: lastBranch = BranchGCPtr(Equal, obj, ImmGCPtr(object), &matched); michael@0: } michael@0: michael@0: if (hasTypeObjects) { michael@0: // We are possibly going to overwrite the obj register. So already michael@0: // emit the branch, since branch depends on previous value of obj michael@0: // register and there is definitely a branch following. So no need michael@0: // to invert the condition. michael@0: if (lastBranch.isInitialized()) michael@0: lastBranch.emit(*this); michael@0: lastBranch = BranchGCPtr(); michael@0: michael@0: // Note: Some platforms give the same register for obj and scratch. michael@0: // Make sure when writing to scratch, the obj register isn't used anymore! michael@0: loadPtr(Address(obj, JSObject::offsetOfType()), scratch); michael@0: michael@0: for (unsigned i = 0; i < count; i++) { michael@0: if (!types->getTypeObject(i)) michael@0: continue; michael@0: michael@0: if (lastBranch.isInitialized()) michael@0: lastBranch.emit(*this); michael@0: michael@0: types::TypeObject *object = types->getTypeObject(i); michael@0: lastBranch = BranchGCPtr(Equal, scratch, ImmGCPtr(object), &matched); michael@0: } michael@0: } michael@0: michael@0: if (!lastBranch.isInitialized()) { michael@0: jump(miss); michael@0: return; michael@0: } michael@0: michael@0: lastBranch.invertCondition(); michael@0: lastBranch.relink(miss); michael@0: lastBranch.emit(*this); michael@0: michael@0: bind(&matched); michael@0: return; michael@0: } michael@0: michael@0: template void michael@0: MacroAssembler::guardType(const Source &address, types::Type type, michael@0: Register scratch, Label *miss) michael@0: { michael@0: TypeWrapper wrapper(type); michael@0: guardTypeSet(address, &wrapper, scratch, miss); michael@0: } michael@0: michael@0: template void MacroAssembler::guardTypeSet(const Address &address, const types::TemporaryTypeSet *types, michael@0: Register scratch, Label *miss); michael@0: template void MacroAssembler::guardTypeSet(const ValueOperand &value, const types::TemporaryTypeSet *types, michael@0: Register scratch, Label *miss); michael@0: michael@0: template void MacroAssembler::guardTypeSet(const Address &address, const types::HeapTypeSet *types, michael@0: Register scratch, Label *miss); michael@0: template void MacroAssembler::guardTypeSet(const ValueOperand &value, const types::HeapTypeSet *types, michael@0: Register scratch, Label *miss); michael@0: template void MacroAssembler::guardTypeSet(const TypedOrValueRegister ®, const types::HeapTypeSet *types, michael@0: Register scratch, Label *miss); michael@0: michael@0: template void MacroAssembler::guardTypeSet(const Address &address, const types::TypeSet *types, michael@0: Register scratch, Label *miss); michael@0: template void MacroAssembler::guardTypeSet(const ValueOperand &value, const types::TypeSet *types, michael@0: Register scratch, Label *miss); michael@0: michael@0: template void MacroAssembler::guardTypeSet(const Address &address, const TypeWrapper *types, michael@0: Register scratch, Label *miss); michael@0: template void MacroAssembler::guardTypeSet(const ValueOperand &value, const TypeWrapper *types, michael@0: Register scratch, Label *miss); michael@0: michael@0: template void MacroAssembler::guardObjectType(Register obj, const types::TemporaryTypeSet *types, michael@0: Register scratch, Label *miss); michael@0: template void MacroAssembler::guardObjectType(Register obj, const types::TypeSet *types, michael@0: Register scratch, Label *miss); michael@0: template void MacroAssembler::guardObjectType(Register obj, const TypeWrapper *types, michael@0: Register scratch, Label *miss); michael@0: michael@0: template void MacroAssembler::guardType(const Address &address, types::Type type, michael@0: Register scratch, Label *miss); michael@0: template void MacroAssembler::guardType(const ValueOperand &value, types::Type type, michael@0: Register scratch, Label *miss); michael@0: michael@0: void michael@0: MacroAssembler::branchNurseryPtr(Condition cond, const Address &ptr1, const ImmMaybeNurseryPtr &ptr2, michael@0: Label *label) michael@0: { michael@0: #ifdef JSGC_GENERATIONAL michael@0: if (ptr2.value && gc::IsInsideNursery(GetIonContext()->cx->runtime(), (void *)ptr2.value)) michael@0: embedsNurseryPointers_ = true; michael@0: #endif michael@0: branchPtr(cond, ptr1, ptr2, label); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::moveNurseryPtr(const ImmMaybeNurseryPtr &ptr, Register reg) michael@0: { michael@0: #ifdef JSGC_GENERATIONAL michael@0: if (ptr.value && gc::IsInsideNursery(GetIonContext()->cx->runtime(), (void *)ptr.value)) michael@0: embedsNurseryPointers_ = true; michael@0: #endif michael@0: movePtr(ptr, reg); michael@0: } michael@0: michael@0: template michael@0: static void michael@0: StoreToTypedFloatArray(MacroAssembler &masm, int arrayType, const S &value, const T &dest) michael@0: { michael@0: switch (arrayType) { michael@0: case ScalarTypeDescr::TYPE_FLOAT32: michael@0: if (LIRGenerator::allowFloat32Optimizations()) { michael@0: masm.storeFloat32(value, dest); michael@0: } else { michael@0: #ifdef JS_MORE_DETERMINISTIC michael@0: // See the comment in TypedArrayObjectTemplate::doubleToNative. michael@0: masm.canonicalizeDouble(value); michael@0: #endif michael@0: masm.convertDoubleToFloat32(value, ScratchFloatReg); michael@0: masm.storeFloat32(ScratchFloatReg, dest); michael@0: } michael@0: break; michael@0: case ScalarTypeDescr::TYPE_FLOAT64: michael@0: #ifdef JS_MORE_DETERMINISTIC michael@0: // See the comment in TypedArrayObjectTemplate::doubleToNative. michael@0: masm.canonicalizeDouble(value); michael@0: #endif michael@0: masm.storeDouble(value, dest); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid typed array type"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::storeToTypedFloatArray(int arrayType, const FloatRegister &value, michael@0: const BaseIndex &dest) michael@0: { michael@0: StoreToTypedFloatArray(*this, arrayType, value, dest); michael@0: } michael@0: void michael@0: MacroAssembler::storeToTypedFloatArray(int arrayType, const FloatRegister &value, michael@0: const Address &dest) michael@0: { michael@0: StoreToTypedFloatArray(*this, arrayType, value, dest); michael@0: } michael@0: michael@0: template michael@0: void michael@0: MacroAssembler::loadFromTypedArray(int arrayType, const T &src, AnyRegister dest, Register temp, michael@0: Label *fail) michael@0: { michael@0: switch (arrayType) { michael@0: case ScalarTypeDescr::TYPE_INT8: michael@0: load8SignExtend(src, dest.gpr()); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_UINT8: michael@0: case ScalarTypeDescr::TYPE_UINT8_CLAMPED: michael@0: load8ZeroExtend(src, dest.gpr()); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_INT16: michael@0: load16SignExtend(src, dest.gpr()); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_UINT16: michael@0: load16ZeroExtend(src, dest.gpr()); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_INT32: michael@0: load32(src, dest.gpr()); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_UINT32: michael@0: if (dest.isFloat()) { michael@0: load32(src, temp); michael@0: convertUInt32ToDouble(temp, dest.fpu()); michael@0: } else { michael@0: load32(src, dest.gpr()); michael@0: michael@0: // Bail out if the value doesn't fit into a signed int32 value. This michael@0: // is what allows MLoadTypedArrayElement to have a type() of michael@0: // MIRType_Int32 for UInt32 array loads. michael@0: branchTest32(Assembler::Signed, dest.gpr(), dest.gpr(), fail); michael@0: } michael@0: break; michael@0: case ScalarTypeDescr::TYPE_FLOAT32: michael@0: if (LIRGenerator::allowFloat32Optimizations()) { michael@0: loadFloat32(src, dest.fpu()); michael@0: canonicalizeFloat(dest.fpu()); michael@0: } else { michael@0: loadFloatAsDouble(src, dest.fpu()); michael@0: canonicalizeDouble(dest.fpu()); michael@0: } michael@0: break; michael@0: case ScalarTypeDescr::TYPE_FLOAT64: michael@0: loadDouble(src, dest.fpu()); michael@0: canonicalizeDouble(dest.fpu()); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid typed array type"); michael@0: } michael@0: } michael@0: michael@0: template void MacroAssembler::loadFromTypedArray(int arrayType, const Address &src, AnyRegister dest, michael@0: Register temp, Label *fail); michael@0: template void MacroAssembler::loadFromTypedArray(int arrayType, const BaseIndex &src, AnyRegister dest, michael@0: Register temp, Label *fail); michael@0: michael@0: template michael@0: void michael@0: MacroAssembler::loadFromTypedArray(int arrayType, const T &src, const ValueOperand &dest, michael@0: bool allowDouble, Register temp, Label *fail) michael@0: { michael@0: switch (arrayType) { michael@0: case ScalarTypeDescr::TYPE_INT8: michael@0: case ScalarTypeDescr::TYPE_UINT8: michael@0: case ScalarTypeDescr::TYPE_UINT8_CLAMPED: michael@0: case ScalarTypeDescr::TYPE_INT16: michael@0: case ScalarTypeDescr::TYPE_UINT16: michael@0: case ScalarTypeDescr::TYPE_INT32: michael@0: loadFromTypedArray(arrayType, src, AnyRegister(dest.scratchReg()), InvalidReg, nullptr); michael@0: tagValue(JSVAL_TYPE_INT32, dest.scratchReg(), dest); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_UINT32: michael@0: // Don't clobber dest when we could fail, instead use temp. michael@0: load32(src, temp); michael@0: if (allowDouble) { michael@0: // If the value fits in an int32, store an int32 type tag. michael@0: // Else, convert the value to double and box it. michael@0: Label done, isDouble; michael@0: branchTest32(Assembler::Signed, temp, temp, &isDouble); michael@0: { michael@0: tagValue(JSVAL_TYPE_INT32, temp, dest); michael@0: jump(&done); michael@0: } michael@0: bind(&isDouble); michael@0: { michael@0: convertUInt32ToDouble(temp, ScratchFloatReg); michael@0: boxDouble(ScratchFloatReg, dest); michael@0: } michael@0: bind(&done); michael@0: } else { michael@0: // Bailout if the value does not fit in an int32. michael@0: branchTest32(Assembler::Signed, temp, temp, fail); michael@0: tagValue(JSVAL_TYPE_INT32, temp, dest); michael@0: } michael@0: break; michael@0: case ScalarTypeDescr::TYPE_FLOAT32: michael@0: loadFromTypedArray(arrayType, src, AnyRegister(ScratchFloatReg), dest.scratchReg(), michael@0: nullptr); michael@0: if (LIRGenerator::allowFloat32Optimizations()) michael@0: convertFloat32ToDouble(ScratchFloatReg, ScratchFloatReg); michael@0: boxDouble(ScratchFloatReg, dest); michael@0: break; michael@0: case ScalarTypeDescr::TYPE_FLOAT64: michael@0: loadFromTypedArray(arrayType, src, AnyRegister(ScratchFloatReg), dest.scratchReg(), michael@0: nullptr); michael@0: boxDouble(ScratchFloatReg, dest); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid typed array type"); michael@0: } michael@0: } michael@0: michael@0: template void MacroAssembler::loadFromTypedArray(int arrayType, const Address &src, const ValueOperand &dest, michael@0: bool allowDouble, Register temp, Label *fail); michael@0: template void MacroAssembler::loadFromTypedArray(int arrayType, const BaseIndex &src, const ValueOperand &dest, michael@0: bool allowDouble, Register temp, Label *fail); michael@0: michael@0: void michael@0: MacroAssembler::newGCThing(Register result, Register temp, gc::AllocKind allocKind, Label *fail, michael@0: gc::InitialHeap initialHeap /* = gc::DefaultHeap */) michael@0: { michael@0: // Inlined equivalent of js::gc::NewGCThing() without failure case handling. michael@0: michael@0: int thingSize = int(gc::Arena::thingSize(allocKind)); michael@0: michael@0: #ifdef JS_GC_ZEAL michael@0: // Don't execute the inline path if gcZeal is active. michael@0: branch32(Assembler::NotEqual, michael@0: AbsoluteAddress(GetIonContext()->runtime->addressOfGCZeal()), Imm32(0), michael@0: fail); michael@0: #endif michael@0: michael@0: // Don't execute the inline path if the compartment has an object metadata callback, michael@0: // as the metadata to use for the object may vary between executions of the op. michael@0: if (GetIonContext()->compartment->hasObjectMetadataCallback()) michael@0: jump(fail); michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: // Always use nursery allocation if it is possible to do so. The jit michael@0: // assumes a nursery pointer is returned to avoid barriers. michael@0: if (allocKind <= gc::FINALIZE_OBJECT_LAST && initialHeap != gc::TenuredHeap) { michael@0: // Inline Nursery::allocate. No explicit check for nursery.isEnabled() michael@0: // is needed, as the comparison with the nursery's end will always fail michael@0: // in such cases. michael@0: const Nursery &nursery = GetIonContext()->runtime->gcNursery(); michael@0: loadPtr(AbsoluteAddress(nursery.addressOfPosition()), result); michael@0: computeEffectiveAddress(Address(result, thingSize), temp); michael@0: branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(nursery.addressOfCurrentEnd()), temp, fail); michael@0: storePtr(temp, AbsoluteAddress(nursery.addressOfPosition())); michael@0: return; michael@0: } michael@0: #endif // JSGC_GENERATIONAL michael@0: michael@0: CompileZone *zone = GetIonContext()->compartment->zone(); michael@0: michael@0: // Inline FreeSpan::allocate. michael@0: // There is always exactly one FreeSpan per allocKind per JSCompartment. michael@0: // If a FreeSpan is replaced, its members are updated in the freeLists table, michael@0: // which the code below always re-reads. michael@0: loadPtr(AbsoluteAddress(zone->addressOfFreeListFirst(allocKind)), result); michael@0: branchPtr(Assembler::BelowOrEqual, AbsoluteAddress(zone->addressOfFreeListLast(allocKind)), result, fail); michael@0: computeEffectiveAddress(Address(result, thingSize), temp); michael@0: storePtr(temp, AbsoluteAddress(zone->addressOfFreeListFirst(allocKind))); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::newGCThing(Register result, Register temp, JSObject *templateObject, Label *fail, michael@0: gc::InitialHeap initialHeap) michael@0: { michael@0: gc::AllocKind allocKind = templateObject->tenuredGetAllocKind(); michael@0: JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST); michael@0: michael@0: newGCThing(result, temp, allocKind, fail, initialHeap); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::newGCString(Register result, Register temp, Label *fail) michael@0: { michael@0: newGCThing(result, temp, js::gc::FINALIZE_STRING, fail); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::newGCFatInlineString(Register result, Register temp, Label *fail) michael@0: { michael@0: newGCThing(result, temp, js::gc::FINALIZE_FAT_INLINE_STRING, fail); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::newGCThingPar(Register result, Register cx, Register tempReg1, Register tempReg2, michael@0: gc::AllocKind allocKind, Label *fail) michael@0: { michael@0: // Similar to ::newGCThing(), except that it allocates from a custom michael@0: // Allocator in the ForkJoinContext*, rather than being hardcoded to the michael@0: // compartment allocator. This requires two temporary registers. michael@0: // michael@0: // Subtle: I wanted to reuse `result` for one of the temporaries, but the michael@0: // register allocator was assigning it to the same register as `cx`. michael@0: // Then we overwrite that register which messed up the OOL code. michael@0: michael@0: uint32_t thingSize = (uint32_t)gc::Arena::thingSize(allocKind); michael@0: michael@0: // Load the allocator: michael@0: // tempReg1 = (Allocator*) forkJoinCx->allocator() michael@0: loadPtr(Address(cx, ThreadSafeContext::offsetOfAllocator()), michael@0: tempReg1); michael@0: michael@0: // Get a pointer to the relevant free list: michael@0: // tempReg1 = (FreeSpan*) &tempReg1->arenas.freeLists[(allocKind)] michael@0: uint32_t offset = (offsetof(Allocator, arenas) + michael@0: js::gc::ArenaLists::getFreeListOffset(allocKind)); michael@0: addPtr(Imm32(offset), tempReg1); michael@0: michael@0: // Load first item on the list michael@0: // tempReg2 = tempReg1->first michael@0: loadPtr(Address(tempReg1, offsetof(gc::FreeSpan, first)), tempReg2); michael@0: michael@0: // Check whether list is empty michael@0: // if tempReg1->last <= tempReg2, fail michael@0: branchPtr(Assembler::BelowOrEqual, michael@0: Address(tempReg1, offsetof(gc::FreeSpan, last)), michael@0: tempReg2, michael@0: fail); michael@0: michael@0: // If not, take first and advance pointer by thingSize bytes. michael@0: // result = tempReg2; michael@0: // tempReg2 += thingSize; michael@0: movePtr(tempReg2, result); michael@0: addPtr(Imm32(thingSize), tempReg2); michael@0: michael@0: // Update `first` michael@0: // tempReg1->first = tempReg2; michael@0: storePtr(tempReg2, Address(tempReg1, offsetof(gc::FreeSpan, first))); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::newGCThingPar(Register result, Register cx, Register tempReg1, Register tempReg2, michael@0: JSObject *templateObject, Label *fail) michael@0: { michael@0: gc::AllocKind allocKind = templateObject->tenuredGetAllocKind(); michael@0: JS_ASSERT(allocKind >= gc::FINALIZE_OBJECT0 && allocKind <= gc::FINALIZE_OBJECT_LAST); michael@0: michael@0: newGCThingPar(result, cx, tempReg1, tempReg2, allocKind, fail); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::newGCStringPar(Register result, Register cx, Register tempReg1, Register tempReg2, michael@0: Label *fail) michael@0: { michael@0: newGCThingPar(result, cx, tempReg1, tempReg2, js::gc::FINALIZE_STRING, fail); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::newGCFatInlineStringPar(Register result, Register cx, Register tempReg1, michael@0: Register tempReg2, Label *fail) michael@0: { michael@0: newGCThingPar(result, cx, tempReg1, tempReg2, js::gc::FINALIZE_FAT_INLINE_STRING, fail); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::copySlotsFromTemplate(Register obj, Register temp, const JSObject *templateObj, michael@0: uint32_t start, uint32_t end) michael@0: { michael@0: uint32_t nfixed = Min(templateObj->numFixedSlots(), end); michael@0: for (unsigned i = start; i < nfixed; i++) michael@0: storeValue(templateObj->getFixedSlot(i), Address(obj, JSObject::getFixedSlotOffset(i))); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::fillSlotsWithUndefined(Register obj, Register temp, const JSObject *templateObj, michael@0: uint32_t start, uint32_t end) michael@0: { michael@0: #ifdef JS_NUNBOX32 michael@0: // We only have a single spare register, so do the initialization as two michael@0: // strided writes of the tag and body. michael@0: jsval_layout jv = JSVAL_TO_IMPL(UndefinedValue()); michael@0: uint32_t nfixed = Min(templateObj->numFixedSlots(), end); michael@0: michael@0: mov(ImmWord(jv.s.tag), temp); michael@0: for (unsigned i = start; i < nfixed; i++) michael@0: store32(temp, ToType(Address(obj, JSObject::getFixedSlotOffset(i)))); michael@0: michael@0: mov(ImmWord(jv.s.payload.i32), temp); michael@0: for (unsigned i = start; i < nfixed; i++) michael@0: store32(temp, ToPayload(Address(obj, JSObject::getFixedSlotOffset(i)))); michael@0: #else michael@0: moveValue(UndefinedValue(), temp); michael@0: uint32_t nfixed = Min(templateObj->numFixedSlots(), end); michael@0: for (unsigned i = start; i < nfixed; i++) michael@0: storePtr(temp, Address(obj, JSObject::getFixedSlotOffset(i))); michael@0: #endif michael@0: } michael@0: michael@0: static uint32_t michael@0: FindStartOfUndefinedSlots(JSObject *templateObj, uint32_t nslots) michael@0: { michael@0: JS_ASSERT(nslots == templateObj->lastProperty()->slotSpan(templateObj->getClass())); michael@0: JS_ASSERT(nslots > 0); michael@0: for (uint32_t first = nslots; first != 0; --first) { michael@0: if (templateObj->getSlot(first - 1) != UndefinedValue()) michael@0: return first; michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::initGCSlots(Register obj, Register temp, JSObject *templateObj) michael@0: { michael@0: // Slots of non-array objects are required to be initialized. michael@0: // Use the values currently in the template object. michael@0: uint32_t nslots = templateObj->lastProperty()->slotSpan(templateObj->getClass()); michael@0: if (nslots == 0) michael@0: return; michael@0: michael@0: // Attempt to group slot writes such that we minimize the amount of michael@0: // duplicated data we need to embed in code and load into registers. In michael@0: // general, most template object slots will be undefined except for any michael@0: // reserved slots. Since reserved slots come first, we split the object michael@0: // logically into independent non-UndefinedValue writes to the head and michael@0: // duplicated writes of UndefinedValue to the tail. For the majority of michael@0: // objects, the "tail" will be the entire slot range. michael@0: uint32_t startOfUndefined = FindStartOfUndefinedSlots(templateObj, nslots); michael@0: copySlotsFromTemplate(obj, temp, templateObj, 0, startOfUndefined); michael@0: fillSlotsWithUndefined(obj, temp, templateObj, startOfUndefined, nslots); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::initGCThing(Register obj, Register temp, JSObject *templateObj) michael@0: { michael@0: // Fast initialization of an empty object returned by NewGCThing(). michael@0: michael@0: JS_ASSERT(!templateObj->hasDynamicElements()); michael@0: michael@0: storePtr(ImmGCPtr(templateObj->lastProperty()), Address(obj, JSObject::offsetOfShape())); michael@0: storePtr(ImmGCPtr(templateObj->type()), Address(obj, JSObject::offsetOfType())); michael@0: storePtr(ImmPtr(nullptr), Address(obj, JSObject::offsetOfSlots())); michael@0: michael@0: if (templateObj->is()) { michael@0: JS_ASSERT(!templateObj->getDenseInitializedLength()); michael@0: michael@0: int elementsOffset = JSObject::offsetOfFixedElements(); michael@0: michael@0: computeEffectiveAddress(Address(obj, elementsOffset), temp); michael@0: storePtr(temp, Address(obj, JSObject::offsetOfElements())); michael@0: michael@0: // Fill in the elements header. michael@0: store32(Imm32(templateObj->getDenseCapacity()), michael@0: Address(obj, elementsOffset + ObjectElements::offsetOfCapacity())); michael@0: store32(Imm32(templateObj->getDenseInitializedLength()), michael@0: Address(obj, elementsOffset + ObjectElements::offsetOfInitializedLength())); michael@0: store32(Imm32(templateObj->as().length()), michael@0: Address(obj, elementsOffset + ObjectElements::offsetOfLength())); michael@0: store32(Imm32(templateObj->shouldConvertDoubleElements() michael@0: ? ObjectElements::CONVERT_DOUBLE_ELEMENTS michael@0: : 0), michael@0: Address(obj, elementsOffset + ObjectElements::offsetOfFlags())); michael@0: JS_ASSERT(!templateObj->hasPrivate()); michael@0: } else { michael@0: storePtr(ImmPtr(emptyObjectElements), Address(obj, JSObject::offsetOfElements())); michael@0: michael@0: initGCSlots(obj, temp, templateObj); michael@0: michael@0: if (templateObj->hasPrivate()) { michael@0: uint32_t nfixed = templateObj->numFixedSlots(); michael@0: storePtr(ImmPtr(templateObj->getPrivate()), michael@0: Address(obj, JSObject::getPrivateDataOffset(nfixed))); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::compareStrings(JSOp op, Register left, Register right, Register result, michael@0: Register temp, Label *fail) michael@0: { michael@0: JS_ASSERT(IsEqualityOp(op)); michael@0: michael@0: Label done; michael@0: Label notPointerEqual; michael@0: // Fast path for identical strings. michael@0: branchPtr(Assembler::NotEqual, left, right, ¬PointerEqual); michael@0: move32(Imm32(op == JSOP_EQ || op == JSOP_STRICTEQ), result); michael@0: jump(&done); michael@0: michael@0: bind(¬PointerEqual); michael@0: loadPtr(Address(left, JSString::offsetOfLengthAndFlags()), result); michael@0: loadPtr(Address(right, JSString::offsetOfLengthAndFlags()), temp); michael@0: michael@0: Label notAtom; michael@0: // Optimize the equality operation to a pointer compare for two atoms. michael@0: Imm32 atomBit(JSString::ATOM_BIT); michael@0: branchTest32(Assembler::Zero, result, atomBit, ¬Atom); michael@0: branchTest32(Assembler::Zero, temp, atomBit, ¬Atom); michael@0: michael@0: cmpPtrSet(JSOpToCondition(MCompare::Compare_String, op), left, right, result); michael@0: jump(&done); michael@0: michael@0: bind(¬Atom); michael@0: // Strings of different length can never be equal. michael@0: rshiftPtr(Imm32(JSString::LENGTH_SHIFT), result); michael@0: rshiftPtr(Imm32(JSString::LENGTH_SHIFT), temp); michael@0: branchPtr(Assembler::Equal, result, temp, fail); michael@0: move32(Imm32(op == JSOP_NE || op == JSOP_STRICTNE), result); michael@0: michael@0: bind(&done); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::checkInterruptFlagPar(Register tempReg, Label *fail) michael@0: { michael@0: #ifdef JS_THREADSAFE michael@0: movePtr(ImmPtr(GetIonContext()->runtime->addressOfInterruptPar()), tempReg); michael@0: branch32(Assembler::NonZero, Address(tempReg, 0), Imm32(0), fail); michael@0: #else michael@0: MOZ_ASSUME_UNREACHABLE("JSRuntime::interruptPar doesn't exist on non-threadsafe builds."); michael@0: #endif michael@0: } michael@0: michael@0: static void michael@0: ReportOverRecursed(JSContext *cx) michael@0: { michael@0: js_ReportOverRecursed(cx); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::generateBailoutTail(Register scratch, Register bailoutInfo) michael@0: { michael@0: enterExitFrame(); michael@0: michael@0: Label baseline; michael@0: michael@0: // The return value from Bailout is tagged as: michael@0: // - 0x0: done (enter baseline) michael@0: // - 0x1: error (handle exception) michael@0: // - 0x2: overrecursed michael@0: JS_STATIC_ASSERT(BAILOUT_RETURN_OK == 0); michael@0: JS_STATIC_ASSERT(BAILOUT_RETURN_FATAL_ERROR == 1); michael@0: JS_STATIC_ASSERT(BAILOUT_RETURN_OVERRECURSED == 2); michael@0: michael@0: branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_OK), &baseline); michael@0: branch32(Equal, ReturnReg, Imm32(BAILOUT_RETURN_FATAL_ERROR), exceptionLabel()); michael@0: michael@0: // Fall-through: overrecursed. michael@0: { michael@0: loadJSContext(ReturnReg); michael@0: setupUnalignedABICall(1, scratch); michael@0: passABIArg(ReturnReg); michael@0: callWithABI(JS_FUNC_TO_DATA_PTR(void *, ReportOverRecursed)); michael@0: jump(exceptionLabel()); michael@0: } michael@0: michael@0: bind(&baseline); michael@0: { michael@0: // Prepare a register set for use in this case. michael@0: GeneralRegisterSet regs(GeneralRegisterSet::All()); michael@0: JS_ASSERT(!regs.has(BaselineStackReg)); michael@0: regs.take(bailoutInfo); michael@0: michael@0: // Reset SP to the point where clobbering starts. michael@0: loadPtr(Address(bailoutInfo, offsetof(BaselineBailoutInfo, incomingStack)), michael@0: BaselineStackReg); michael@0: michael@0: Register copyCur = regs.takeAny(); michael@0: Register copyEnd = regs.takeAny(); michael@0: Register temp = regs.takeAny(); michael@0: michael@0: // Copy data onto stack. michael@0: loadPtr(Address(bailoutInfo, offsetof(BaselineBailoutInfo, copyStackTop)), copyCur); michael@0: loadPtr(Address(bailoutInfo, offsetof(BaselineBailoutInfo, copyStackBottom)), copyEnd); michael@0: { michael@0: Label copyLoop; michael@0: Label endOfCopy; michael@0: bind(©Loop); michael@0: branchPtr(Assembler::BelowOrEqual, copyCur, copyEnd, &endOfCopy); michael@0: subPtr(Imm32(4), copyCur); michael@0: subPtr(Imm32(4), BaselineStackReg); michael@0: load32(Address(copyCur, 0), temp); michael@0: store32(temp, Address(BaselineStackReg, 0)); michael@0: jump(©Loop); michael@0: bind(&endOfCopy); michael@0: } michael@0: michael@0: // Enter exit frame for the FinishBailoutToBaseline call. michael@0: loadPtr(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeFramePtr)), temp); michael@0: load32(Address(temp, BaselineFrame::reverseOffsetOfFrameSize()), temp); michael@0: makeFrameDescriptor(temp, JitFrame_BaselineJS); michael@0: push(temp); michael@0: push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeAddr))); michael@0: enterFakeExitFrame(); michael@0: michael@0: // If monitorStub is non-null, handle resumeAddr appropriately. michael@0: Label noMonitor; michael@0: Label done; michael@0: branchPtr(Assembler::Equal, michael@0: Address(bailoutInfo, offsetof(BaselineBailoutInfo, monitorStub)), michael@0: ImmPtr(nullptr), michael@0: &noMonitor); michael@0: michael@0: // michael@0: // Resuming into a monitoring stub chain. michael@0: // michael@0: { michael@0: // Save needed values onto stack temporarily. michael@0: pushValue(Address(bailoutInfo, offsetof(BaselineBailoutInfo, valueR0))); michael@0: push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeFramePtr))); michael@0: push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeAddr))); michael@0: push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, monitorStub))); michael@0: michael@0: // Call a stub to free allocated memory and create arguments objects. michael@0: setupUnalignedABICall(1, temp); michael@0: passABIArg(bailoutInfo); michael@0: callWithABI(JS_FUNC_TO_DATA_PTR(void *, FinishBailoutToBaseline)); michael@0: branchTest32(Zero, ReturnReg, ReturnReg, exceptionLabel()); michael@0: michael@0: // Restore values where they need to be and resume execution. michael@0: GeneralRegisterSet enterMonRegs(GeneralRegisterSet::All()); michael@0: enterMonRegs.take(R0); michael@0: enterMonRegs.take(BaselineStubReg); michael@0: enterMonRegs.take(BaselineFrameReg); michael@0: enterMonRegs.takeUnchecked(BaselineTailCallReg); michael@0: michael@0: pop(BaselineStubReg); michael@0: pop(BaselineTailCallReg); michael@0: pop(BaselineFrameReg); michael@0: popValue(R0); michael@0: michael@0: // Discard exit frame. michael@0: addPtr(Imm32(IonExitFrameLayout::SizeWithFooter()), StackPointer); michael@0: michael@0: #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64) michael@0: push(BaselineTailCallReg); michael@0: #endif michael@0: jump(Address(BaselineStubReg, ICStub::offsetOfStubCode())); michael@0: } michael@0: michael@0: // michael@0: // Resuming into main jitcode. michael@0: // michael@0: bind(&noMonitor); michael@0: { michael@0: // Save needed values onto stack temporarily. michael@0: pushValue(Address(bailoutInfo, offsetof(BaselineBailoutInfo, valueR0))); michael@0: pushValue(Address(bailoutInfo, offsetof(BaselineBailoutInfo, valueR1))); michael@0: push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeFramePtr))); michael@0: push(Address(bailoutInfo, offsetof(BaselineBailoutInfo, resumeAddr))); michael@0: michael@0: // Call a stub to free allocated memory and create arguments objects. michael@0: setupUnalignedABICall(1, temp); michael@0: passABIArg(bailoutInfo); michael@0: callWithABI(JS_FUNC_TO_DATA_PTR(void *, FinishBailoutToBaseline)); michael@0: branchTest32(Zero, ReturnReg, ReturnReg, exceptionLabel()); michael@0: michael@0: // Restore values where they need to be and resume execution. michael@0: GeneralRegisterSet enterRegs(GeneralRegisterSet::All()); michael@0: enterRegs.take(R0); michael@0: enterRegs.take(R1); michael@0: enterRegs.take(BaselineFrameReg); michael@0: Register jitcodeReg = enterRegs.takeAny(); michael@0: michael@0: pop(jitcodeReg); michael@0: pop(BaselineFrameReg); michael@0: popValue(R1); michael@0: popValue(R0); michael@0: michael@0: // Discard exit frame. michael@0: addPtr(Imm32(IonExitFrameLayout::SizeWithFooter()), StackPointer); michael@0: michael@0: jump(jitcodeReg); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::loadBaselineOrIonRaw(Register script, Register dest, ExecutionMode mode, michael@0: Label *failure) michael@0: { michael@0: if (mode == SequentialExecution) { michael@0: loadPtr(Address(script, JSScript::offsetOfBaselineOrIonRaw()), dest); michael@0: if (failure) michael@0: branchTestPtr(Assembler::Zero, dest, dest, failure); michael@0: } else { michael@0: loadPtr(Address(script, JSScript::offsetOfParallelIonScript()), dest); michael@0: if (failure) michael@0: branchPtr(Assembler::BelowOrEqual, dest, ImmPtr(ION_COMPILING_SCRIPT), failure); michael@0: loadPtr(Address(dest, IonScript::offsetOfMethod()), dest); michael@0: loadPtr(Address(dest, JitCode::offsetOfCode()), dest); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::loadBaselineOrIonNoArgCheck(Register script, Register dest, ExecutionMode mode, michael@0: Label *failure) michael@0: { michael@0: if (mode == SequentialExecution) { michael@0: loadPtr(Address(script, JSScript::offsetOfBaselineOrIonSkipArgCheck()), dest); michael@0: if (failure) michael@0: branchTestPtr(Assembler::Zero, dest, dest, failure); michael@0: } else { michael@0: // Find second register to get the offset to skip argument check michael@0: Register offset = script; michael@0: if (script == dest) { michael@0: GeneralRegisterSet regs(GeneralRegisterSet::All()); michael@0: regs.take(dest); michael@0: offset = regs.takeAny(); michael@0: } michael@0: michael@0: loadPtr(Address(script, JSScript::offsetOfParallelIonScript()), dest); michael@0: if (failure) michael@0: branchPtr(Assembler::BelowOrEqual, dest, ImmPtr(ION_COMPILING_SCRIPT), failure); michael@0: michael@0: Push(offset); michael@0: load32(Address(script, IonScript::offsetOfSkipArgCheckEntryOffset()), offset); michael@0: michael@0: loadPtr(Address(dest, IonScript::offsetOfMethod()), dest); michael@0: loadPtr(Address(dest, JitCode::offsetOfCode()), dest); michael@0: addPtr(offset, dest); michael@0: michael@0: Pop(offset); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::loadBaselineFramePtr(Register framePtr, Register dest) michael@0: { michael@0: if (framePtr != dest) michael@0: movePtr(framePtr, dest); michael@0: subPtr(Imm32(BaselineFrame::Size()), dest); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::loadForkJoinContext(Register cx, Register scratch) michael@0: { michael@0: // Load the current ForkJoinContext *. If we need a parallel exit frame, michael@0: // chances are we are about to do something very slow anyways, so just michael@0: // call ForkJoinContextPar again instead of using the cached version. michael@0: setupUnalignedABICall(0, scratch); michael@0: callWithABI(JS_FUNC_TO_DATA_PTR(void *, ForkJoinContextPar)); michael@0: if (ReturnReg != cx) michael@0: movePtr(ReturnReg, cx); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::loadContext(Register cxReg, Register scratch, ExecutionMode executionMode) michael@0: { michael@0: switch (executionMode) { michael@0: case SequentialExecution: michael@0: // The scratch register is not used for sequential execution. michael@0: loadJSContext(cxReg); michael@0: break; michael@0: case ParallelExecution: michael@0: loadForkJoinContext(cxReg, scratch); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("No such execution mode"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::enterParallelExitFrameAndLoadContext(const VMFunction *f, Register cx, michael@0: Register scratch) michael@0: { michael@0: loadForkJoinContext(cx, scratch); michael@0: // Load the PerThreadData from from the cx. michael@0: loadPtr(Address(cx, offsetof(ForkJoinContext, perThreadData)), scratch); michael@0: linkParallelExitFrame(scratch); michael@0: // Push the ioncode. michael@0: exitCodePatch_ = PushWithPatch(ImmWord(-1)); michael@0: // Push the VMFunction pointer, to mark arguments. michael@0: Push(ImmPtr(f)); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::enterFakeParallelExitFrame(Register cx, Register scratch, michael@0: JitCode *codeVal) michael@0: { michael@0: // Load the PerThreadData from from the cx. michael@0: loadPtr(Address(cx, offsetof(ForkJoinContext, perThreadData)), scratch); michael@0: linkParallelExitFrame(scratch); michael@0: Push(ImmPtr(codeVal)); michael@0: Push(ImmPtr(nullptr)); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::enterExitFrameAndLoadContext(const VMFunction *f, Register cxReg, Register scratch, michael@0: ExecutionMode executionMode) michael@0: { michael@0: switch (executionMode) { michael@0: case SequentialExecution: michael@0: // The scratch register is not used for sequential execution. michael@0: enterExitFrame(f); michael@0: loadJSContext(cxReg); michael@0: break; michael@0: case ParallelExecution: michael@0: enterParallelExitFrameAndLoadContext(f, cxReg, scratch); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("No such execution mode"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::enterFakeExitFrame(Register cxReg, Register scratch, michael@0: ExecutionMode executionMode, michael@0: JitCode *codeVal) michael@0: { michael@0: switch (executionMode) { michael@0: case SequentialExecution: michael@0: // The cx and scratch registers are not used for sequential execution. michael@0: enterFakeExitFrame(codeVal); michael@0: break; michael@0: case ParallelExecution: michael@0: enterFakeParallelExitFrame(cxReg, scratch, codeVal); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("No such execution mode"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::handleFailure(ExecutionMode executionMode) michael@0: { michael@0: // Re-entry code is irrelevant because the exception will leave the michael@0: // running function and never come back michael@0: if (sps_) michael@0: sps_->skipNextReenter(); michael@0: leaveSPSFrame(); michael@0: michael@0: void *handler; michael@0: switch (executionMode) { michael@0: case SequentialExecution: michael@0: handler = JS_FUNC_TO_DATA_PTR(void *, jit::HandleException); michael@0: break; michael@0: case ParallelExecution: michael@0: handler = JS_FUNC_TO_DATA_PTR(void *, jit::HandleParallelFailure); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("No such execution mode"); michael@0: } michael@0: MacroAssemblerSpecific::handleFailureWithHandler(handler); michael@0: michael@0: // Doesn't actually emit code, but balances the leave() michael@0: if (sps_) michael@0: sps_->reenter(*this, InvalidReg); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: static inline bool michael@0: IsCompilingAsmJS() michael@0: { michael@0: // asm.js compilation pushes an IonContext with a null JSCompartment. michael@0: IonContext *ictx = MaybeGetIonContext(); michael@0: return ictx && ictx->compartment == nullptr; michael@0: } michael@0: michael@0: static void michael@0: AssumeUnreachable_(const char *output) { michael@0: MOZ_ReportAssertionFailure(output, __FILE__, __LINE__); michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: MacroAssembler::assumeUnreachable(const char *output) michael@0: { michael@0: #ifdef DEBUG michael@0: RegisterSet regs = RegisterSet::Volatile(); michael@0: PushRegsInMask(regs); michael@0: Register temp = regs.takeGeneral(); michael@0: michael@0: // With ASLR, we can't rely on 'output' to point to the michael@0: // same char array after serialization/deserialization. michael@0: // It is not possible until we modify AsmJsImmPtr and michael@0: // the underlying "patching" mechanism. michael@0: if (IsCompilingAsmJS()) { michael@0: setupUnalignedABICall(0, temp); michael@0: callWithABINoProfiling(AsmJSImm_AssumeUnreachable); michael@0: } else { michael@0: setupUnalignedABICall(1, temp); michael@0: movePtr(ImmPtr(output), temp); michael@0: passABIArg(temp); michael@0: callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, AssumeUnreachable_)); michael@0: } michael@0: PopRegsInMask(RegisterSet::Volatile()); michael@0: #endif michael@0: michael@0: breakpoint(); michael@0: } michael@0: michael@0: static void michael@0: Printf0_(const char *output) { michael@0: printf("%s", output); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::printf(const char *output) michael@0: { michael@0: RegisterSet regs = RegisterSet::Volatile(); michael@0: PushRegsInMask(regs); michael@0: michael@0: Register temp = regs.takeGeneral(); michael@0: michael@0: setupUnalignedABICall(1, temp); michael@0: movePtr(ImmPtr(output), temp); michael@0: passABIArg(temp); michael@0: callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, Printf0_)); michael@0: michael@0: PopRegsInMask(RegisterSet::Volatile()); michael@0: } michael@0: michael@0: static void michael@0: Printf1_(const char *output, uintptr_t value) { michael@0: char *line = JS_sprintf_append(nullptr, output, value); michael@0: printf("%s", line); michael@0: js_free(line); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::printf(const char *output, Register value) michael@0: { michael@0: RegisterSet regs = RegisterSet::Volatile(); michael@0: PushRegsInMask(regs); michael@0: michael@0: regs.takeUnchecked(value); michael@0: michael@0: Register temp = regs.takeGeneral(); michael@0: michael@0: setupUnalignedABICall(2, temp); michael@0: movePtr(ImmPtr(output), temp); michael@0: passABIArg(temp); michael@0: passABIArg(value); michael@0: callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, Printf1_)); michael@0: michael@0: PopRegsInMask(RegisterSet::Volatile()); michael@0: } michael@0: michael@0: #ifdef JS_TRACE_LOGGING michael@0: void michael@0: MacroAssembler::tracelogStart(Register logger, uint32_t textId) michael@0: { michael@0: void (&TraceLogFunc)(TraceLogger*, uint32_t) = TraceLogStartEvent; michael@0: michael@0: PushRegsInMask(RegisterSet::Volatile()); michael@0: michael@0: RegisterSet regs = RegisterSet::Volatile(); michael@0: regs.takeUnchecked(logger); michael@0: michael@0: Register temp = regs.takeGeneral(); michael@0: michael@0: setupUnalignedABICall(2, temp); michael@0: passABIArg(logger); michael@0: move32(Imm32(textId), temp); michael@0: passABIArg(temp); michael@0: callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc)); michael@0: michael@0: PopRegsInMask(RegisterSet::Volatile()); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::tracelogStart(Register logger, Register textId) michael@0: { michael@0: void (&TraceLogFunc)(TraceLogger*, uint32_t) = TraceLogStartEvent; michael@0: michael@0: PushRegsInMask(RegisterSet::Volatile()); michael@0: michael@0: RegisterSet regs = RegisterSet::Volatile(); michael@0: regs.takeUnchecked(logger); michael@0: regs.takeUnchecked(textId); michael@0: michael@0: Register temp = regs.takeGeneral(); michael@0: michael@0: setupUnalignedABICall(2, temp); michael@0: passABIArg(logger); michael@0: passABIArg(textId); michael@0: callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc)); michael@0: michael@0: regs.add(temp); michael@0: michael@0: PopRegsInMask(RegisterSet::Volatile()); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::tracelogStop(Register logger, uint32_t textId) michael@0: { michael@0: void (&TraceLogFunc)(TraceLogger*, uint32_t) = TraceLogStopEvent; michael@0: michael@0: PushRegsInMask(RegisterSet::Volatile()); michael@0: michael@0: RegisterSet regs = RegisterSet::Volatile(); michael@0: regs.takeUnchecked(logger); michael@0: michael@0: Register temp = regs.takeGeneral(); michael@0: michael@0: setupUnalignedABICall(2, temp); michael@0: passABIArg(logger); michael@0: move32(Imm32(textId), temp); michael@0: passABIArg(temp); michael@0: callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc)); michael@0: michael@0: regs.add(temp); michael@0: michael@0: PopRegsInMask(RegisterSet::Volatile()); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::tracelogStop(Register logger, Register textId) michael@0: { michael@0: #ifdef DEBUG michael@0: void (&TraceLogFunc)(TraceLogger*, uint32_t) = TraceLogStopEvent; michael@0: michael@0: PushRegsInMask(RegisterSet::Volatile()); michael@0: michael@0: RegisterSet regs = RegisterSet::Volatile(); michael@0: regs.takeUnchecked(logger); michael@0: regs.takeUnchecked(textId); michael@0: michael@0: Register temp = regs.takeGeneral(); michael@0: michael@0: setupUnalignedABICall(2, temp); michael@0: passABIArg(logger); michael@0: passABIArg(textId); michael@0: callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc)); michael@0: michael@0: regs.add(temp); michael@0: michael@0: PopRegsInMask(RegisterSet::Volatile()); michael@0: #else michael@0: tracelogStop(logger); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::tracelogStop(Register logger) michael@0: { michael@0: void (&TraceLogFunc)(TraceLogger*) = TraceLogStopEvent; michael@0: michael@0: PushRegsInMask(RegisterSet::Volatile()); michael@0: michael@0: RegisterSet regs = RegisterSet::Volatile(); michael@0: regs.takeUnchecked(logger); michael@0: michael@0: Register temp = regs.takeGeneral(); michael@0: michael@0: setupUnalignedABICall(1, temp); michael@0: passABIArg(logger); michael@0: callWithABINoProfiling(JS_FUNC_TO_DATA_PTR(void *, TraceLogFunc)); michael@0: michael@0: regs.add(temp); michael@0: michael@0: PopRegsInMask(RegisterSet::Volatile()); michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: MacroAssembler::convertInt32ValueToDouble(const Address &address, Register scratch, Label *done) michael@0: { michael@0: branchTestInt32(Assembler::NotEqual, address, done); michael@0: unboxInt32(address, scratch); michael@0: convertInt32ToDouble(scratch, ScratchFloatReg); michael@0: storeDouble(ScratchFloatReg, address); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::convertValueToFloatingPoint(ValueOperand value, FloatRegister output, michael@0: Label *fail, MIRType outputType) michael@0: { michael@0: Register tag = splitTagForTest(value); michael@0: michael@0: Label isDouble, isInt32, isBool, isNull, done; michael@0: michael@0: branchTestDouble(Assembler::Equal, tag, &isDouble); michael@0: branchTestInt32(Assembler::Equal, tag, &isInt32); michael@0: branchTestBoolean(Assembler::Equal, tag, &isBool); michael@0: branchTestNull(Assembler::Equal, tag, &isNull); michael@0: branchTestUndefined(Assembler::NotEqual, tag, fail); michael@0: michael@0: // fall-through: undefined michael@0: loadConstantFloatingPoint(GenericNaN(), float(GenericNaN()), output, outputType); michael@0: jump(&done); michael@0: michael@0: bind(&isNull); michael@0: loadConstantFloatingPoint(0.0, 0.0f, output, outputType); michael@0: jump(&done); michael@0: michael@0: bind(&isBool); michael@0: boolValueToFloatingPoint(value, output, outputType); michael@0: jump(&done); michael@0: michael@0: bind(&isInt32); michael@0: int32ValueToFloatingPoint(value, output, outputType); michael@0: jump(&done); michael@0: michael@0: bind(&isDouble); michael@0: unboxDouble(value, output); michael@0: if (outputType == MIRType_Float32) michael@0: convertDoubleToFloat32(output, output); michael@0: bind(&done); michael@0: } michael@0: michael@0: bool michael@0: MacroAssembler::convertValueToFloatingPoint(JSContext *cx, const Value &v, FloatRegister output, michael@0: Label *fail, MIRType outputType) michael@0: { michael@0: if (v.isNumber() || v.isString()) { michael@0: double d; michael@0: if (v.isNumber()) michael@0: d = v.toNumber(); michael@0: else if (!StringToNumber(cx, v.toString(), &d)) michael@0: return false; michael@0: michael@0: loadConstantFloatingPoint(d, (float)d, output, outputType); michael@0: return true; michael@0: } michael@0: michael@0: if (v.isBoolean()) { michael@0: if (v.toBoolean()) michael@0: loadConstantFloatingPoint(1.0, 1.0f, output, outputType); michael@0: else michael@0: loadConstantFloatingPoint(0.0, 0.0f, output, outputType); michael@0: return true; michael@0: } michael@0: michael@0: if (v.isNull()) { michael@0: loadConstantFloatingPoint(0.0, 0.0f, output, outputType); michael@0: return true; michael@0: } michael@0: michael@0: if (v.isUndefined()) { michael@0: loadConstantFloatingPoint(GenericNaN(), float(GenericNaN()), output, outputType); michael@0: return true; michael@0: } michael@0: michael@0: JS_ASSERT(v.isObject()); michael@0: jump(fail); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::PushEmptyRooted(VMFunction::RootType rootType) michael@0: { michael@0: switch (rootType) { michael@0: case VMFunction::RootNone: michael@0: MOZ_ASSUME_UNREACHABLE("Handle must have root type"); michael@0: case VMFunction::RootObject: michael@0: case VMFunction::RootString: michael@0: case VMFunction::RootPropertyName: michael@0: case VMFunction::RootFunction: michael@0: case VMFunction::RootCell: michael@0: Push(ImmPtr(nullptr)); michael@0: break; michael@0: case VMFunction::RootValue: michael@0: Push(UndefinedValue()); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::popRooted(VMFunction::RootType rootType, Register cellReg, michael@0: const ValueOperand &valueReg) michael@0: { michael@0: switch (rootType) { michael@0: case VMFunction::RootNone: michael@0: MOZ_ASSUME_UNREACHABLE("Handle must have root type"); michael@0: case VMFunction::RootObject: michael@0: case VMFunction::RootString: michael@0: case VMFunction::RootPropertyName: michael@0: case VMFunction::RootFunction: michael@0: case VMFunction::RootCell: michael@0: Pop(cellReg); michael@0: break; michael@0: case VMFunction::RootValue: michael@0: Pop(valueReg); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: bool michael@0: MacroAssembler::convertConstantOrRegisterToFloatingPoint(JSContext *cx, ConstantOrRegister src, michael@0: FloatRegister output, Label *fail, michael@0: MIRType outputType) michael@0: { michael@0: if (src.constant()) michael@0: return convertValueToFloatingPoint(cx, src.value(), output, fail, outputType); michael@0: michael@0: convertTypedOrValueToFloatingPoint(src.reg(), output, fail, outputType); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::convertTypedOrValueToFloatingPoint(TypedOrValueRegister src, FloatRegister output, michael@0: Label *fail, MIRType outputType) michael@0: { michael@0: JS_ASSERT(IsFloatingPointType(outputType)); michael@0: michael@0: if (src.hasValue()) { michael@0: convertValueToFloatingPoint(src.valueReg(), output, fail, outputType); michael@0: return; michael@0: } michael@0: michael@0: bool outputIsDouble = outputType == MIRType_Double; michael@0: switch (src.type()) { michael@0: case MIRType_Null: michael@0: loadConstantFloatingPoint(0.0, 0.0f, output, outputType); michael@0: break; michael@0: case MIRType_Boolean: michael@0: case MIRType_Int32: michael@0: convertInt32ToFloatingPoint(src.typedReg().gpr(), output, outputType); michael@0: break; michael@0: case MIRType_Float32: michael@0: if (outputIsDouble) { michael@0: convertFloat32ToDouble(src.typedReg().fpu(), output); michael@0: } else { michael@0: if (src.typedReg().fpu() != output) michael@0: moveFloat32(src.typedReg().fpu(), output); michael@0: } michael@0: break; michael@0: case MIRType_Double: michael@0: if (outputIsDouble) { michael@0: if (src.typedReg().fpu() != output) michael@0: moveDouble(src.typedReg().fpu(), output); michael@0: } else { michael@0: convertDoubleToFloat32(src.typedReg().fpu(), output); michael@0: } michael@0: break; michael@0: case MIRType_Object: michael@0: case MIRType_String: michael@0: jump(fail); michael@0: break; michael@0: case MIRType_Undefined: michael@0: loadConstantFloatingPoint(GenericNaN(), float(GenericNaN()), output, outputType); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Bad MIRType"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::convertDoubleToInt(FloatRegister src, Register output, FloatRegister temp, michael@0: Label *truncateFail, Label *fail, michael@0: IntConversionBehavior behavior) michael@0: { michael@0: switch (behavior) { michael@0: case IntConversion_Normal: michael@0: case IntConversion_NegativeZeroCheck: michael@0: convertDoubleToInt32(src, output, fail, behavior == IntConversion_NegativeZeroCheck); michael@0: break; michael@0: case IntConversion_Truncate: michael@0: branchTruncateDouble(src, output, truncateFail ? truncateFail : fail); michael@0: break; michael@0: case IntConversion_ClampToUint8: michael@0: // Clamping clobbers the input register, so use a temp. michael@0: moveDouble(src, temp); michael@0: clampDoubleToUint8(temp, output); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::convertValueToInt(ValueOperand value, MDefinition *maybeInput, michael@0: Label *handleStringEntry, Label *handleStringRejoin, michael@0: Label *truncateDoubleSlow, michael@0: Register stringReg, FloatRegister temp, Register output, michael@0: Label *fail, IntConversionBehavior behavior, michael@0: IntConversionInputKind conversion) michael@0: { michael@0: Register tag = splitTagForTest(value); michael@0: bool handleStrings = (behavior == IntConversion_Truncate || michael@0: behavior == IntConversion_ClampToUint8) && michael@0: handleStringEntry && michael@0: handleStringRejoin; michael@0: michael@0: JS_ASSERT_IF(handleStrings, conversion == IntConversion_Any); michael@0: michael@0: Label done, isInt32, isBool, isDouble, isNull, isString; michael@0: michael@0: branchEqualTypeIfNeeded(MIRType_Int32, maybeInput, tag, &isInt32); michael@0: if (conversion == IntConversion_Any || conversion == IntConversion_NumbersOrBoolsOnly) michael@0: branchEqualTypeIfNeeded(MIRType_Boolean, maybeInput, tag, &isBool); michael@0: branchEqualTypeIfNeeded(MIRType_Double, maybeInput, tag, &isDouble); michael@0: michael@0: if (conversion == IntConversion_Any) { michael@0: // If we are not truncating, we fail for anything that's not michael@0: // null. Otherwise we might be able to handle strings and objects. michael@0: switch (behavior) { michael@0: case IntConversion_Normal: michael@0: case IntConversion_NegativeZeroCheck: michael@0: branchTestNull(Assembler::NotEqual, tag, fail); michael@0: break; michael@0: michael@0: case IntConversion_Truncate: michael@0: case IntConversion_ClampToUint8: michael@0: branchEqualTypeIfNeeded(MIRType_Null, maybeInput, tag, &isNull); michael@0: if (handleStrings) michael@0: branchEqualTypeIfNeeded(MIRType_String, maybeInput, tag, &isString); michael@0: branchEqualTypeIfNeeded(MIRType_Object, maybeInput, tag, fail); michael@0: branchTestUndefined(Assembler::NotEqual, tag, fail); michael@0: break; michael@0: } michael@0: } else { michael@0: jump(fail); michael@0: } michael@0: michael@0: // The value is null or undefined in truncation contexts - just emit 0. michael@0: if (isNull.used()) michael@0: bind(&isNull); michael@0: mov(ImmWord(0), output); michael@0: jump(&done); michael@0: michael@0: // Try converting a string into a double, then jump to the double case. michael@0: if (handleStrings) { michael@0: bind(&isString); michael@0: unboxString(value, stringReg); michael@0: jump(handleStringEntry); michael@0: } michael@0: michael@0: // Try converting double into integer. michael@0: if (isDouble.used() || handleStrings) { michael@0: if (isDouble.used()) { michael@0: bind(&isDouble); michael@0: unboxDouble(value, temp); michael@0: } michael@0: michael@0: if (handleStrings) michael@0: bind(handleStringRejoin); michael@0: michael@0: convertDoubleToInt(temp, output, temp, truncateDoubleSlow, fail, behavior); michael@0: jump(&done); michael@0: } michael@0: michael@0: // Just unbox a bool, the result is 0 or 1. michael@0: if (isBool.used()) { michael@0: bind(&isBool); michael@0: unboxBoolean(value, output); michael@0: jump(&done); michael@0: } michael@0: michael@0: // Integers can be unboxed. michael@0: if (isInt32.used()) { michael@0: bind(&isInt32); michael@0: unboxInt32(value, output); michael@0: if (behavior == IntConversion_ClampToUint8) michael@0: clampIntToUint8(output); michael@0: } michael@0: michael@0: bind(&done); michael@0: } michael@0: michael@0: bool michael@0: MacroAssembler::convertValueToInt(JSContext *cx, const Value &v, Register output, Label *fail, michael@0: IntConversionBehavior behavior) michael@0: { michael@0: bool handleStrings = (behavior == IntConversion_Truncate || michael@0: behavior == IntConversion_ClampToUint8); michael@0: michael@0: if (v.isNumber() || (handleStrings && v.isString())) { michael@0: double d; michael@0: if (v.isNumber()) michael@0: d = v.toNumber(); michael@0: else if (!StringToNumber(cx, v.toString(), &d)) michael@0: return false; michael@0: michael@0: switch (behavior) { michael@0: case IntConversion_Normal: michael@0: case IntConversion_NegativeZeroCheck: { michael@0: // -0 is checked anyways if we have a constant value. michael@0: int i; michael@0: if (mozilla::NumberIsInt32(d, &i)) michael@0: move32(Imm32(i), output); michael@0: else michael@0: jump(fail); michael@0: break; michael@0: } michael@0: case IntConversion_Truncate: michael@0: move32(Imm32(js::ToInt32(d)), output); michael@0: break; michael@0: case IntConversion_ClampToUint8: michael@0: move32(Imm32(ClampDoubleToUint8(d)), output); michael@0: break; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: if (v.isBoolean()) { michael@0: move32(Imm32(v.toBoolean() ? 1 : 0), output); michael@0: return true; michael@0: } michael@0: michael@0: if (v.isNull() || v.isUndefined()) { michael@0: move32(Imm32(0), output); michael@0: return true; michael@0: } michael@0: michael@0: JS_ASSERT(v.isObject()); michael@0: michael@0: jump(fail); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: MacroAssembler::convertConstantOrRegisterToInt(JSContext *cx, ConstantOrRegister src, michael@0: FloatRegister temp, Register output, michael@0: Label *fail, IntConversionBehavior behavior) michael@0: { michael@0: if (src.constant()) michael@0: return convertValueToInt(cx, src.value(), output, fail, behavior); michael@0: michael@0: convertTypedOrValueToInt(src.reg(), temp, output, fail, behavior); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::convertTypedOrValueToInt(TypedOrValueRegister src, FloatRegister temp, michael@0: Register output, Label *fail, michael@0: IntConversionBehavior behavior) michael@0: { michael@0: if (src.hasValue()) { michael@0: convertValueToInt(src.valueReg(), temp, output, fail, behavior); michael@0: return; michael@0: } michael@0: michael@0: switch (src.type()) { michael@0: case MIRType_Undefined: michael@0: case MIRType_Null: michael@0: move32(Imm32(0), output); michael@0: break; michael@0: case MIRType_Boolean: michael@0: case MIRType_Int32: michael@0: if (src.typedReg().gpr() != output) michael@0: move32(src.typedReg().gpr(), output); michael@0: if (src.type() == MIRType_Int32 && behavior == IntConversion_ClampToUint8) michael@0: clampIntToUint8(output); michael@0: break; michael@0: case MIRType_Double: michael@0: convertDoubleToInt(src.typedReg().fpu(), output, temp, nullptr, fail, behavior); michael@0: break; michael@0: case MIRType_Float32: michael@0: // Conversion to Double simplifies implementation at the expense of performance. michael@0: convertFloat32ToDouble(src.typedReg().fpu(), temp); michael@0: convertDoubleToInt(temp, output, temp, nullptr, fail, behavior); michael@0: break; michael@0: case MIRType_String: michael@0: case MIRType_Object: michael@0: jump(fail); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Bad MIRType"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::finish() michael@0: { michael@0: if (sequentialFailureLabel_.used()) { michael@0: bind(&sequentialFailureLabel_); michael@0: handleFailure(SequentialExecution); michael@0: } michael@0: if (parallelFailureLabel_.used()) { michael@0: bind(¶llelFailureLabel_); michael@0: handleFailure(ParallelExecution); michael@0: } michael@0: michael@0: MacroAssemblerSpecific::finish(); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::branchIfNotInterpretedConstructor(Register fun, Register scratch, Label *label) michael@0: { michael@0: // 16-bit loads are slow and unaligned 32-bit loads may be too so michael@0: // perform an aligned 32-bit load and adjust the bitmask accordingly. michael@0: JS_ASSERT(JSFunction::offsetOfNargs() % sizeof(uint32_t) == 0); michael@0: JS_ASSERT(JSFunction::offsetOfFlags() == JSFunction::offsetOfNargs() + 2); michael@0: JS_STATIC_ASSERT(IS_LITTLE_ENDIAN); michael@0: michael@0: // Emit code for the following test: michael@0: // michael@0: // bool isInterpretedConstructor() const { michael@0: // return isInterpreted() && !isFunctionPrototype() && !isArrow() && michael@0: // (!isSelfHostedBuiltin() || isSelfHostedConstructor()); michael@0: // } michael@0: michael@0: // First, ensure it's a scripted function. michael@0: load32(Address(fun, JSFunction::offsetOfNargs()), scratch); michael@0: branchTest32(Assembler::Zero, scratch, Imm32(JSFunction::INTERPRETED << 16), label); michael@0: michael@0: // Common case: if IS_FUN_PROTO, ARROW and SELF_HOSTED are not set, michael@0: // the function is an interpreted constructor and we're done. michael@0: Label done; michael@0: uint32_t bits = (JSFunction::IS_FUN_PROTO | JSFunction::ARROW | JSFunction::SELF_HOSTED) << 16; michael@0: branchTest32(Assembler::Zero, scratch, Imm32(bits), &done); michael@0: { michael@0: // The callee is either Function.prototype, an arrow function or michael@0: // self-hosted. None of these are constructible, except self-hosted michael@0: // constructors, so branch to |label| if SELF_HOSTED_CTOR is not set. michael@0: branchTest32(Assembler::Zero, scratch, Imm32(JSFunction::SELF_HOSTED_CTOR << 16), label); michael@0: michael@0: #ifdef DEBUG michael@0: // Function.prototype should not have the SELF_HOSTED_CTOR flag. michael@0: branchTest32(Assembler::Zero, scratch, Imm32(JSFunction::IS_FUN_PROTO << 16), &done); michael@0: breakpoint(); michael@0: #endif michael@0: } michael@0: bind(&done); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::branchEqualTypeIfNeeded(MIRType type, MDefinition *maybeDef, Register tag, michael@0: Label *label) michael@0: { michael@0: if (!maybeDef || maybeDef->mightBeType(type)) { michael@0: switch (type) { michael@0: case MIRType_Null: michael@0: branchTestNull(Equal, tag, label); michael@0: break; michael@0: case MIRType_Boolean: michael@0: branchTestBoolean(Equal, tag, label); michael@0: break; michael@0: case MIRType_Int32: michael@0: branchTestInt32(Equal, tag, label); michael@0: break; michael@0: case MIRType_Double: michael@0: branchTestDouble(Equal, tag, label); michael@0: break; michael@0: case MIRType_String: michael@0: branchTestString(Equal, tag, label); michael@0: break; michael@0: case MIRType_Object: michael@0: branchTestObject(Equal, tag, label); michael@0: break; michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unsupported type"); michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: // If a pseudostack frame has this as its label, its stack pointer michael@0: // field points to the registers saved on entry to JIT code. A native michael@0: // stack unwinder could use that information to continue unwinding michael@0: // past that point. michael@0: const char MacroAssembler::enterJitLabel[] = "EnterJIT"; michael@0: michael@0: // Creates an enterJIT pseudostack frame, as described above. Pushes michael@0: // a word to the stack to indicate whether this was done. |framePtr| is michael@0: // the pointer to the machine-dependent saved state. michael@0: void michael@0: MacroAssembler::spsMarkJit(SPSProfiler *p, Register framePtr, Register temp) michael@0: { michael@0: Label spsNotEnabled; michael@0: uint32_t *enabledAddr = p->addressOfEnabled(); michael@0: load32(AbsoluteAddress(enabledAddr), temp); michael@0: push(temp); // +4: Did we push an sps frame. michael@0: branchTest32(Assembler::Equal, temp, temp, &spsNotEnabled); michael@0: michael@0: Label stackFull; michael@0: // We always need the "safe" versions, because these are used in trampolines michael@0: // and won't be regenerated when SPS state changes. michael@0: spsProfileEntryAddressSafe(p, 0, temp, &stackFull); michael@0: michael@0: storePtr(ImmPtr(enterJitLabel), Address(temp, ProfileEntry::offsetOfString())); michael@0: storePtr(framePtr, Address(temp, ProfileEntry::offsetOfStackAddress())); michael@0: storePtr(ImmWord(uintptr_t(0)), Address(temp, ProfileEntry::offsetOfScript())); michael@0: store32(Imm32(ProfileEntry::NullPCIndex), Address(temp, ProfileEntry::offsetOfPCIdx())); michael@0: michael@0: /* Always increment the stack size, whether or not we actually pushed. */ michael@0: bind(&stackFull); michael@0: loadPtr(AbsoluteAddress(p->addressOfSizePointer()), temp); michael@0: add32(Imm32(1), Address(temp, 0)); michael@0: michael@0: bind(&spsNotEnabled); michael@0: } michael@0: michael@0: // Pops the word pushed by spsMarkJit and, if spsMarkJit pushed an SPS michael@0: // frame, pops it. michael@0: void michael@0: MacroAssembler::spsUnmarkJit(SPSProfiler *p, Register temp) michael@0: { michael@0: Label spsNotEnabled; michael@0: pop(temp); // -4: Was the profiler enabled. michael@0: branchTest32(Assembler::Equal, temp, temp, &spsNotEnabled); michael@0: michael@0: spsPopFrameSafe(p, temp); michael@0: michael@0: bind(&spsNotEnabled); michael@0: }