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/x86/CodeGenerator-x86.h" michael@0: michael@0: #include "mozilla/DebugOnly.h" michael@0: michael@0: #include "jsnum.h" michael@0: michael@0: #include "jit/IonCaches.h" michael@0: #include "jit/MIR.h" michael@0: #include "jit/MIRGraph.h" michael@0: #include "vm/Shape.h" michael@0: michael@0: #include "jsscriptinlines.h" michael@0: michael@0: #include "jit/ExecutionMode-inl.h" michael@0: #include "jit/shared/CodeGenerator-shared-inl.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: using mozilla::DebugOnly; michael@0: using mozilla::FloatingPoint; michael@0: using JS::GenericNaN; michael@0: michael@0: CodeGeneratorX86::CodeGeneratorX86(MIRGenerator *gen, LIRGraph *graph, MacroAssembler *masm) michael@0: : CodeGeneratorX86Shared(gen, graph, masm) michael@0: { michael@0: } michael@0: michael@0: static const uint32_t FrameSizes[] = { 128, 256, 512, 1024 }; michael@0: michael@0: FrameSizeClass michael@0: FrameSizeClass::FromDepth(uint32_t frameDepth) michael@0: { michael@0: for (uint32_t i = 0; i < JS_ARRAY_LENGTH(FrameSizes); i++) { michael@0: if (frameDepth < FrameSizes[i]) michael@0: return FrameSizeClass(i); michael@0: } michael@0: michael@0: return FrameSizeClass::None(); michael@0: } michael@0: michael@0: FrameSizeClass michael@0: FrameSizeClass::ClassLimit() michael@0: { michael@0: return FrameSizeClass(JS_ARRAY_LENGTH(FrameSizes)); michael@0: } michael@0: michael@0: uint32_t michael@0: FrameSizeClass::frameSize() const michael@0: { michael@0: JS_ASSERT(class_ != NO_FRAME_SIZE_CLASS_ID); michael@0: JS_ASSERT(class_ < JS_ARRAY_LENGTH(FrameSizes)); michael@0: michael@0: return FrameSizes[class_]; michael@0: } michael@0: michael@0: ValueOperand michael@0: CodeGeneratorX86::ToValue(LInstruction *ins, size_t pos) michael@0: { michael@0: Register typeReg = ToRegister(ins->getOperand(pos + TYPE_INDEX)); michael@0: Register payloadReg = ToRegister(ins->getOperand(pos + PAYLOAD_INDEX)); michael@0: return ValueOperand(typeReg, payloadReg); michael@0: } michael@0: michael@0: ValueOperand michael@0: CodeGeneratorX86::ToOutValue(LInstruction *ins) michael@0: { michael@0: Register typeReg = ToRegister(ins->getDef(TYPE_INDEX)); michael@0: Register payloadReg = ToRegister(ins->getDef(PAYLOAD_INDEX)); michael@0: return ValueOperand(typeReg, payloadReg); michael@0: } michael@0: michael@0: ValueOperand michael@0: CodeGeneratorX86::ToTempValue(LInstruction *ins, size_t pos) michael@0: { michael@0: Register typeReg = ToRegister(ins->getTemp(pos + TYPE_INDEX)); michael@0: Register payloadReg = ToRegister(ins->getTemp(pos + PAYLOAD_INDEX)); michael@0: return ValueOperand(typeReg, payloadReg); michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitValue(LValue *value) michael@0: { michael@0: const ValueOperand out = ToOutValue(value); michael@0: masm.moveValue(value->value(), out); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitBox(LBox *box) michael@0: { michael@0: const LDefinition *type = box->getDef(TYPE_INDEX); michael@0: michael@0: DebugOnly a = box->getOperand(0); michael@0: JS_ASSERT(!a->isConstant()); michael@0: michael@0: // On x86, the input operand and the output payload have the same michael@0: // virtual register. All that needs to be written is the type tag for michael@0: // the type definition. michael@0: masm.mov(ImmWord(MIRTypeToTag(box->type())), ToRegister(type)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitBoxFloatingPoint(LBoxFloatingPoint *box) michael@0: { michael@0: const LAllocation *in = box->getOperand(0); michael@0: const ValueOperand out = ToOutValue(box); michael@0: michael@0: FloatRegister reg = ToFloatRegister(in); michael@0: if (box->type() == MIRType_Float32) { michael@0: masm.convertFloat32ToDouble(reg, ScratchFloatReg); michael@0: reg = ScratchFloatReg; michael@0: } michael@0: masm.boxDouble(reg, out); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitUnbox(LUnbox *unbox) michael@0: { michael@0: // Note that for unbox, the type and payload indexes are switched on the michael@0: // inputs. michael@0: MUnbox *mir = unbox->mir(); michael@0: michael@0: if (mir->fallible()) { michael@0: masm.cmpl(ToOperand(unbox->type()), Imm32(MIRTypeToTag(mir->type()))); michael@0: if (!bailoutIf(Assembler::NotEqual, unbox->snapshot())) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitLoadSlotV(LLoadSlotV *load) michael@0: { michael@0: const ValueOperand out = ToOutValue(load); michael@0: Register base = ToRegister(load->input()); michael@0: int32_t offset = load->mir()->slot() * sizeof(js::Value); michael@0: michael@0: masm.loadValue(Address(base, offset), out); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitLoadSlotT(LLoadSlotT *load) michael@0: { michael@0: Register base = ToRegister(load->input()); michael@0: int32_t offset = load->mir()->slot() * sizeof(js::Value); michael@0: michael@0: if (load->mir()->type() == MIRType_Double) michael@0: masm.loadInt32OrDouble(Operand(base, offset), ToFloatRegister(load->output())); michael@0: else michael@0: masm.load32(Address(base, offset + NUNBOX32_PAYLOAD_OFFSET), ToRegister(load->output())); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitStoreSlotT(LStoreSlotT *store) michael@0: { michael@0: Register base = ToRegister(store->slots()); michael@0: int32_t offset = store->mir()->slot() * sizeof(js::Value); michael@0: michael@0: const LAllocation *value = store->value(); michael@0: MIRType valueType = store->mir()->value()->type(); michael@0: michael@0: if (store->mir()->needsBarrier()) michael@0: emitPreBarrier(Address(base, offset), store->mir()->slotType()); michael@0: michael@0: if (valueType == MIRType_Double) { michael@0: masm.storeDouble(ToFloatRegister(value), Operand(base, offset)); michael@0: return true; michael@0: } michael@0: michael@0: // Store the type tag if needed. michael@0: if (valueType != store->mir()->slotType()) michael@0: masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), Operand(base, offset)); michael@0: michael@0: // Store the payload. michael@0: if (value->isConstant()) michael@0: masm.storePayload(*value->toConstant(), Operand(base, offset)); michael@0: else michael@0: masm.storePayload(ToRegister(value), Operand(base, offset)); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitLoadElementT(LLoadElementT *load) michael@0: { michael@0: Operand source = createArrayElementOperand(ToRegister(load->elements()), load->index()); michael@0: michael@0: if (load->mir()->needsHoleCheck()) { michael@0: Assembler::Condition cond = masm.testMagic(Assembler::Equal, source); michael@0: if (!bailoutIf(cond, load->snapshot())) michael@0: return false; michael@0: } michael@0: michael@0: if (load->mir()->type() == MIRType_Double) { michael@0: FloatRegister fpreg = ToFloatRegister(load->output()); michael@0: if (load->mir()->loadDoubles()) { michael@0: if (source.kind() == Operand::MEM_REG_DISP) michael@0: masm.loadDouble(source.toAddress(), fpreg); michael@0: else michael@0: masm.loadDouble(source.toBaseIndex(), fpreg); michael@0: } else { michael@0: masm.loadInt32OrDouble(source, fpreg); michael@0: } michael@0: } else { michael@0: masm.movl(masm.ToPayload(source), ToRegister(load->output())); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: CodeGeneratorX86::storeElementTyped(const LAllocation *value, MIRType valueType, MIRType elementType, michael@0: const Register &elements, const LAllocation *index) michael@0: { michael@0: Operand dest = createArrayElementOperand(elements, index); michael@0: michael@0: if (valueType == MIRType_Double) { michael@0: masm.storeDouble(ToFloatRegister(value), dest); michael@0: return; michael@0: } michael@0: michael@0: // Store the type tag if needed. michael@0: if (valueType != elementType) michael@0: masm.storeTypeTag(ImmType(ValueTypeFromMIRType(valueType)), dest); michael@0: michael@0: // Store the payload. michael@0: if (value->isConstant()) michael@0: masm.storePayload(*value->toConstant(), dest); michael@0: else michael@0: masm.storePayload(ToRegister(value), dest); michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitImplicitThis(LImplicitThis *lir) michael@0: { michael@0: Register callee = ToRegister(lir->callee()); michael@0: const ValueOperand out = ToOutValue(lir); michael@0: michael@0: // The implicit |this| is always |undefined| if the function's environment michael@0: // is the current global. michael@0: GlobalObject *global = &gen->info().script()->global(); michael@0: masm.cmpPtr(Operand(callee, JSFunction::offsetOfEnvironment()), ImmGCPtr(global)); michael@0: michael@0: // TODO: OOL stub path. michael@0: if (!bailoutIf(Assembler::NotEqual, lir->snapshot())) michael@0: return false; michael@0: michael@0: masm.moveValue(UndefinedValue(), out); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitInterruptCheck(LInterruptCheck *lir) michael@0: { michael@0: OutOfLineCode *ool = oolCallVM(InterruptCheckInfo, lir, (ArgList()), StoreNothing()); michael@0: if (!ool) michael@0: return false; michael@0: michael@0: masm.cmpl(Operand(AbsoluteAddress(GetIonContext()->runtime->addressOfInterrupt())), Imm32(0)); michael@0: masm.j(Assembler::NonZero, ool->entry()); michael@0: masm.bind(ool->rejoin()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitCompareB(LCompareB *lir) michael@0: { michael@0: MCompare *mir = lir->mir(); michael@0: michael@0: const ValueOperand lhs = ToValue(lir, LCompareB::Lhs); michael@0: const LAllocation *rhs = lir->rhs(); michael@0: const Register output = ToRegister(lir->output()); michael@0: michael@0: JS_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE); michael@0: michael@0: Label notBoolean, done; michael@0: masm.branchTestBoolean(Assembler::NotEqual, lhs, ¬Boolean); michael@0: { michael@0: if (rhs->isConstant()) michael@0: masm.cmp32(lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean())); michael@0: else michael@0: masm.cmp32(lhs.payloadReg(), ToRegister(rhs)); michael@0: masm.emitSet(JSOpToCondition(mir->compareType(), mir->jsop()), output); michael@0: masm.jump(&done); michael@0: } michael@0: masm.bind(¬Boolean); michael@0: { michael@0: masm.move32(Imm32(mir->jsop() == JSOP_STRICTNE), output); michael@0: } michael@0: michael@0: masm.bind(&done); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitCompareBAndBranch(LCompareBAndBranch *lir) michael@0: { michael@0: MCompare *mir = lir->cmpMir(); michael@0: const ValueOperand lhs = ToValue(lir, LCompareBAndBranch::Lhs); michael@0: const LAllocation *rhs = lir->rhs(); michael@0: michael@0: JS_ASSERT(mir->jsop() == JSOP_STRICTEQ || mir->jsop() == JSOP_STRICTNE); michael@0: michael@0: Assembler::Condition cond = masm.testBoolean(Assembler::NotEqual, lhs); michael@0: jumpToBlock((mir->jsop() == JSOP_STRICTEQ) ? lir->ifFalse() : lir->ifTrue(), cond); michael@0: michael@0: if (rhs->isConstant()) michael@0: masm.cmp32(lhs.payloadReg(), Imm32(rhs->toConstant()->toBoolean())); michael@0: else michael@0: masm.cmp32(lhs.payloadReg(), ToRegister(rhs)); michael@0: emitBranch(JSOpToCondition(mir->compareType(), mir->jsop()), lir->ifTrue(), lir->ifFalse()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitCompareV(LCompareV *lir) michael@0: { michael@0: MCompare *mir = lir->mir(); michael@0: Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop()); michael@0: const ValueOperand lhs = ToValue(lir, LCompareV::LhsInput); michael@0: const ValueOperand rhs = ToValue(lir, LCompareV::RhsInput); michael@0: const Register output = ToRegister(lir->output()); michael@0: michael@0: JS_ASSERT(IsEqualityOp(mir->jsop())); michael@0: michael@0: Label notEqual, done; michael@0: masm.cmp32(lhs.typeReg(), rhs.typeReg()); michael@0: masm.j(Assembler::NotEqual, ¬Equal); michael@0: { michael@0: masm.cmp32(lhs.payloadReg(), rhs.payloadReg()); michael@0: masm.emitSet(cond, output); michael@0: masm.jump(&done); michael@0: } michael@0: masm.bind(¬Equal); michael@0: { michael@0: masm.move32(Imm32(cond == Assembler::NotEqual), output); michael@0: } michael@0: michael@0: masm.bind(&done); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitCompareVAndBranch(LCompareVAndBranch *lir) michael@0: { michael@0: MCompare *mir = lir->cmpMir(); michael@0: Assembler::Condition cond = JSOpToCondition(mir->compareType(), mir->jsop()); michael@0: const ValueOperand lhs = ToValue(lir, LCompareVAndBranch::LhsInput); michael@0: const ValueOperand rhs = ToValue(lir, LCompareVAndBranch::RhsInput); michael@0: michael@0: JS_ASSERT(mir->jsop() == JSOP_EQ || mir->jsop() == JSOP_STRICTEQ || michael@0: mir->jsop() == JSOP_NE || mir->jsop() == JSOP_STRICTNE); michael@0: michael@0: MBasicBlock *notEqual = (cond == Assembler::Equal) ? lir->ifFalse() : lir->ifTrue(); michael@0: michael@0: masm.cmp32(lhs.typeReg(), rhs.typeReg()); michael@0: jumpToBlock(notEqual, Assembler::NotEqual); michael@0: masm.cmp32(lhs.payloadReg(), rhs.payloadReg()); michael@0: emitBranch(cond, lir->ifTrue(), lir->ifFalse()); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble *lir) michael@0: { michael@0: Register input = ToRegister(lir->input()); michael@0: Register temp = ToRegister(lir->temp()); michael@0: michael@0: if (input != temp) michael@0: masm.mov(input, temp); michael@0: michael@0: // Beware: convertUInt32ToDouble clobbers input. michael@0: masm.convertUInt32ToDouble(temp, ToFloatRegister(lir->output())); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitAsmJSUInt32ToFloat32(LAsmJSUInt32ToFloat32 *lir) michael@0: { michael@0: Register input = ToRegister(lir->input()); michael@0: Register temp = ToRegister(lir->temp()); michael@0: FloatRegister output = ToFloatRegister(lir->output()); michael@0: michael@0: if (input != temp) michael@0: masm.mov(input, temp); michael@0: michael@0: // Beware: convertUInt32ToFloat32 clobbers input. michael@0: masm.convertUInt32ToFloat32(temp, output); michael@0: return true; michael@0: } michael@0: michael@0: // Load a NaN or zero into a register for an out of bounds AsmJS or static michael@0: // typed array load. michael@0: class jit::OutOfLineLoadTypedArrayOutOfBounds : public OutOfLineCodeBase michael@0: { michael@0: AnyRegister dest_; michael@0: bool isFloat32Load_; michael@0: public: michael@0: OutOfLineLoadTypedArrayOutOfBounds(AnyRegister dest, bool isFloat32Load) michael@0: : dest_(dest), isFloat32Load_(isFloat32Load) michael@0: {} michael@0: michael@0: const AnyRegister &dest() const { return dest_; } michael@0: bool isFloat32Load() const { return isFloat32Load_; } michael@0: bool accept(CodeGeneratorX86 *codegen) { return codegen->visitOutOfLineLoadTypedArrayOutOfBounds(this); } michael@0: }; michael@0: michael@0: template michael@0: void michael@0: CodeGeneratorX86::loadViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr, michael@0: const LDefinition *out) michael@0: { michael@0: switch (vt) { michael@0: case ArrayBufferView::TYPE_INT8: masm.movsblWithPatch(srcAddr, ToRegister(out)); break; michael@0: case ArrayBufferView::TYPE_UINT8_CLAMPED: michael@0: case ArrayBufferView::TYPE_UINT8: masm.movzblWithPatch(srcAddr, ToRegister(out)); break; michael@0: case ArrayBufferView::TYPE_INT16: masm.movswlWithPatch(srcAddr, ToRegister(out)); break; michael@0: case ArrayBufferView::TYPE_UINT16: masm.movzwlWithPatch(srcAddr, ToRegister(out)); break; michael@0: case ArrayBufferView::TYPE_INT32: michael@0: case ArrayBufferView::TYPE_UINT32: masm.movlWithPatch(srcAddr, ToRegister(out)); break; michael@0: case ArrayBufferView::TYPE_FLOAT32: masm.movssWithPatch(srcAddr, ToFloatRegister(out)); break; michael@0: case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(srcAddr, ToFloatRegister(out)); break; michael@0: default: MOZ_ASSUME_UNREACHABLE("unexpected array type"); michael@0: } michael@0: } michael@0: michael@0: template michael@0: bool michael@0: CodeGeneratorX86::loadAndNoteViewTypeElement(ArrayBufferView::ViewType vt, const T &srcAddr, michael@0: const LDefinition *out) michael@0: { michael@0: uint32_t before = masm.size(); michael@0: loadViewTypeElement(vt, srcAddr, out); michael@0: uint32_t after = masm.size(); michael@0: return masm.append(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out))); michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitLoadTypedArrayElementStatic(LLoadTypedArrayElementStatic *ins) michael@0: { michael@0: const MLoadTypedArrayElementStatic *mir = ins->mir(); michael@0: ArrayBufferView::ViewType vt = mir->viewType(); michael@0: JS_ASSERT_IF(vt == ArrayBufferView::TYPE_FLOAT32, mir->type() == MIRType_Float32); michael@0: michael@0: Register ptr = ToRegister(ins->ptr()); michael@0: const LDefinition *out = ins->output(); michael@0: michael@0: OutOfLineLoadTypedArrayOutOfBounds *ool = nullptr; michael@0: bool isFloat32Load = (vt == ArrayBufferView::TYPE_FLOAT32); michael@0: if (!mir->fallible()) { michael@0: ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), isFloat32Load); michael@0: if (!addOutOfLineCode(ool)) michael@0: return false; michael@0: } michael@0: michael@0: masm.cmpl(ptr, Imm32(mir->length())); michael@0: if (ool) michael@0: masm.j(Assembler::AboveOrEqual, ool->entry()); michael@0: else if (!bailoutIf(Assembler::AboveOrEqual, ins->snapshot())) michael@0: return false; michael@0: michael@0: Address srcAddr(ptr, (int32_t) mir->base()); michael@0: loadViewTypeElement(vt, srcAddr, out); michael@0: if (vt == ArrayBufferView::TYPE_FLOAT64) michael@0: masm.canonicalizeDouble(ToFloatRegister(out)); michael@0: if (vt == ArrayBufferView::TYPE_FLOAT32) michael@0: masm.canonicalizeFloat(ToFloatRegister(out)); michael@0: if (ool) michael@0: masm.bind(ool->rejoin()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitAsmJSLoadHeap(LAsmJSLoadHeap *ins) michael@0: { michael@0: const MAsmJSLoadHeap *mir = ins->mir(); michael@0: ArrayBufferView::ViewType vt = mir->viewType(); michael@0: const LAllocation *ptr = ins->ptr(); michael@0: const LDefinition *out = ins->output(); michael@0: michael@0: if (ptr->isConstant()) { michael@0: // The constant displacement still needs to be added to the as-yet-unknown michael@0: // base address of the heap. For now, embed the displacement as an michael@0: // immediate in the instruction. This displacement will fixed up when the michael@0: // base address is known during dynamic linking (AsmJSModule::initHeap). michael@0: PatchedAbsoluteAddress srcAddr((void *) ptr->toConstant()->toInt32()); michael@0: return loadAndNoteViewTypeElement(vt, srcAddr, out); michael@0: } michael@0: michael@0: Register ptrReg = ToRegister(ptr); michael@0: Address srcAddr(ptrReg, 0); michael@0: michael@0: if (mir->skipBoundsCheck()) michael@0: return loadAndNoteViewTypeElement(vt, srcAddr, out); michael@0: michael@0: bool isFloat32Load = vt == ArrayBufferView::TYPE_FLOAT32; michael@0: OutOfLineLoadTypedArrayOutOfBounds *ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), isFloat32Load); michael@0: if (!addOutOfLineCode(ool)) michael@0: return false; michael@0: michael@0: CodeOffsetLabel cmp = masm.cmplWithPatch(ptrReg, Imm32(0)); michael@0: masm.j(Assembler::AboveOrEqual, ool->entry()); michael@0: michael@0: uint32_t before = masm.size(); michael@0: loadViewTypeElement(vt, srcAddr, out); michael@0: uint32_t after = masm.size(); michael@0: masm.bind(ool->rejoin()); michael@0: return masm.append(AsmJSHeapAccess(before, after, vt, ToAnyRegister(out), cmp.offset())); michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitOutOfLineLoadTypedArrayOutOfBounds(OutOfLineLoadTypedArrayOutOfBounds *ool) michael@0: { michael@0: if (ool->dest().isFloat()) { michael@0: if (ool->isFloat32Load()) michael@0: masm.loadConstantFloat32(float(GenericNaN()), ool->dest().fpu()); michael@0: else michael@0: masm.loadConstantDouble(GenericNaN(), ool->dest().fpu()); michael@0: } else { michael@0: Register destReg = ool->dest().gpr(); michael@0: masm.mov(ImmWord(0), destReg); michael@0: } michael@0: masm.jmp(ool->rejoin()); michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: void michael@0: CodeGeneratorX86::storeViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value, michael@0: const T &dstAddr) michael@0: { michael@0: switch (vt) { michael@0: case ArrayBufferView::TYPE_INT8: michael@0: case ArrayBufferView::TYPE_UINT8_CLAMPED: michael@0: case ArrayBufferView::TYPE_UINT8: masm.movbWithPatch(ToRegister(value), dstAddr); break; michael@0: case ArrayBufferView::TYPE_INT16: michael@0: case ArrayBufferView::TYPE_UINT16: masm.movwWithPatch(ToRegister(value), dstAddr); break; michael@0: case ArrayBufferView::TYPE_INT32: michael@0: case ArrayBufferView::TYPE_UINT32: masm.movlWithPatch(ToRegister(value), dstAddr); break; michael@0: case ArrayBufferView::TYPE_FLOAT32: masm.movssWithPatch(ToFloatRegister(value), dstAddr); break; michael@0: case ArrayBufferView::TYPE_FLOAT64: masm.movsdWithPatch(ToFloatRegister(value), dstAddr); break; michael@0: default: MOZ_ASSUME_UNREACHABLE("unexpected array type"); michael@0: } michael@0: } michael@0: michael@0: template michael@0: bool michael@0: CodeGeneratorX86::storeAndNoteViewTypeElement(ArrayBufferView::ViewType vt, const LAllocation *value, michael@0: const T &dstAddr) michael@0: { michael@0: uint32_t before = masm.size(); michael@0: storeViewTypeElement(vt, value, dstAddr); michael@0: uint32_t after = masm.size(); michael@0: return masm.append(AsmJSHeapAccess(before, after)); michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitStoreTypedArrayElementStatic(LStoreTypedArrayElementStatic *ins) michael@0: { michael@0: MStoreTypedArrayElementStatic *mir = ins->mir(); michael@0: ArrayBufferView::ViewType vt = mir->viewType(); michael@0: michael@0: Register ptr = ToRegister(ins->ptr()); michael@0: const LAllocation *value = ins->value(); michael@0: michael@0: masm.cmpl(ptr, Imm32(mir->length())); michael@0: Label rejoin; michael@0: masm.j(Assembler::AboveOrEqual, &rejoin); michael@0: michael@0: Address dstAddr(ptr, (int32_t) mir->base()); michael@0: storeViewTypeElement(vt, value, dstAddr); michael@0: masm.bind(&rejoin); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitAsmJSStoreHeap(LAsmJSStoreHeap *ins) michael@0: { michael@0: MAsmJSStoreHeap *mir = ins->mir(); michael@0: ArrayBufferView::ViewType vt = mir->viewType(); michael@0: const LAllocation *value = ins->value(); michael@0: const LAllocation *ptr = ins->ptr(); michael@0: michael@0: if (ptr->isConstant()) { michael@0: // The constant displacement still needs to be added to the as-yet-unknown michael@0: // base address of the heap. For now, embed the displacement as an michael@0: // immediate in the instruction. This displacement will fixed up when the michael@0: // base address is known during dynamic linking (AsmJSModule::initHeap). michael@0: PatchedAbsoluteAddress dstAddr((void *) ptr->toConstant()->toInt32()); michael@0: return storeAndNoteViewTypeElement(vt, value, dstAddr); michael@0: } michael@0: michael@0: Register ptrReg = ToRegister(ptr); michael@0: Address dstAddr(ptrReg, 0); michael@0: michael@0: if (mir->skipBoundsCheck()) michael@0: return storeAndNoteViewTypeElement(vt, value, dstAddr); michael@0: michael@0: CodeOffsetLabel cmp = masm.cmplWithPatch(ptrReg, Imm32(0)); michael@0: Label rejoin; michael@0: masm.j(Assembler::AboveOrEqual, &rejoin); michael@0: michael@0: uint32_t before = masm.size(); michael@0: storeViewTypeElement(vt, value, dstAddr); michael@0: uint32_t after = masm.size(); michael@0: masm.bind(&rejoin); michael@0: return masm.append(AsmJSHeapAccess(before, after, cmp.offset())); michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitAsmJSLoadGlobalVar(LAsmJSLoadGlobalVar *ins) michael@0: { michael@0: MAsmJSLoadGlobalVar *mir = ins->mir(); michael@0: MIRType type = mir->type(); michael@0: JS_ASSERT(IsNumberType(type)); michael@0: michael@0: CodeOffsetLabel label; michael@0: if (type == MIRType_Int32) michael@0: label = masm.movlWithPatch(PatchedAbsoluteAddress(), ToRegister(ins->output())); michael@0: else if (type == MIRType_Float32) michael@0: label = masm.movssWithPatch(PatchedAbsoluteAddress(), ToFloatRegister(ins->output())); michael@0: else michael@0: label = masm.movsdWithPatch(PatchedAbsoluteAddress(), ToFloatRegister(ins->output())); michael@0: michael@0: return masm.append(AsmJSGlobalAccess(label.offset(), mir->globalDataOffset())); michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitAsmJSStoreGlobalVar(LAsmJSStoreGlobalVar *ins) michael@0: { michael@0: MAsmJSStoreGlobalVar *mir = ins->mir(); michael@0: michael@0: MIRType type = mir->value()->type(); michael@0: JS_ASSERT(IsNumberType(type)); michael@0: michael@0: CodeOffsetLabel label; michael@0: if (type == MIRType_Int32) michael@0: label = masm.movlWithPatch(ToRegister(ins->value()), PatchedAbsoluteAddress()); michael@0: else if (type == MIRType_Float32) michael@0: label = masm.movssWithPatch(ToFloatRegister(ins->value()), PatchedAbsoluteAddress()); michael@0: else michael@0: label = masm.movsdWithPatch(ToFloatRegister(ins->value()), PatchedAbsoluteAddress()); michael@0: michael@0: return masm.append(AsmJSGlobalAccess(label.offset(), mir->globalDataOffset())); michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitAsmJSLoadFuncPtr(LAsmJSLoadFuncPtr *ins) michael@0: { michael@0: MAsmJSLoadFuncPtr *mir = ins->mir(); michael@0: michael@0: Register index = ToRegister(ins->index()); michael@0: Register out = ToRegister(ins->output()); michael@0: CodeOffsetLabel label = masm.movlWithPatch(PatchedAbsoluteAddress(), index, TimesFour, out); michael@0: michael@0: return masm.append(AsmJSGlobalAccess(label.offset(), mir->globalDataOffset())); michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitAsmJSLoadFFIFunc(LAsmJSLoadFFIFunc *ins) michael@0: { michael@0: MAsmJSLoadFFIFunc *mir = ins->mir(); michael@0: michael@0: Register out = ToRegister(ins->output()); michael@0: CodeOffsetLabel label = masm.movlWithPatch(PatchedAbsoluteAddress(), out); michael@0: michael@0: return masm.append(AsmJSGlobalAccess(label.offset(), mir->globalDataOffset())); michael@0: } michael@0: michael@0: void michael@0: CodeGeneratorX86::postAsmJSCall(LAsmJSCall *lir) michael@0: { michael@0: MAsmJSCall *mir = lir->mir(); michael@0: if (!IsFloatingPointType(mir->type()) || mir->callee().which() != MAsmJSCall::Callee::Builtin) michael@0: return; michael@0: michael@0: if (mir->type() == MIRType_Float32) { michael@0: masm.reserveStack(sizeof(float)); michael@0: Operand op(esp, 0); michael@0: masm.fstp32(op); michael@0: masm.loadFloat32(op, ReturnFloatReg); michael@0: masm.freeStack(sizeof(float)); michael@0: } else { michael@0: masm.reserveStack(sizeof(double)); michael@0: Operand op(esp, 0); michael@0: masm.fstp(op); michael@0: masm.loadDouble(op, ReturnFloatReg); michael@0: masm.freeStack(sizeof(double)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: DispatchIonCache::initializeAddCacheState(LInstruction *ins, AddCacheState *addState) michael@0: { michael@0: // On x86, where there is no general purpose scratch register available, michael@0: // child cache classes must manually specify a dispatch scratch register. michael@0: MOZ_ASSUME_UNREACHABLE("x86 needs manual assignment of dispatchScratch"); michael@0: } michael@0: michael@0: void michael@0: GetPropertyParIC::initializeAddCacheState(LInstruction *ins, AddCacheState *addState) michael@0: { michael@0: // We don't have a scratch register, but only use the temp if we needed michael@0: // one, it's BogusTemp otherwise. michael@0: JS_ASSERT(ins->isGetPropertyCacheV() || ins->isGetPropertyCacheT()); michael@0: if (ins->isGetPropertyCacheV() || ins->toGetPropertyCacheT()->temp()->isBogusTemp()) michael@0: addState->dispatchScratch = output_.scratchReg().gpr(); michael@0: else michael@0: addState->dispatchScratch = ToRegister(ins->toGetPropertyCacheT()->temp()); michael@0: } michael@0: michael@0: void michael@0: GetElementParIC::initializeAddCacheState(LInstruction *ins, AddCacheState *addState) michael@0: { michael@0: // We don't have a scratch register, but only use the temp if we needed michael@0: // one, it's BogusTemp otherwise. michael@0: JS_ASSERT(ins->isGetElementCacheV() || ins->isGetElementCacheT()); michael@0: if (ins->isGetElementCacheV() || ins->toGetElementCacheT()->temp()->isBogusTemp()) michael@0: addState->dispatchScratch = output_.scratchReg().gpr(); michael@0: else michael@0: addState->dispatchScratch = ToRegister(ins->toGetElementCacheT()->temp()); michael@0: } michael@0: michael@0: void michael@0: SetPropertyParIC::initializeAddCacheState(LInstruction *ins, AddCacheState *addState) michael@0: { michael@0: // We don't have an output register to reuse, so we always need a temp. michael@0: JS_ASSERT(ins->isSetPropertyCacheV() || ins->isSetPropertyCacheT()); michael@0: if (ins->isSetPropertyCacheV()) michael@0: addState->dispatchScratch = ToRegister(ins->toSetPropertyCacheV()->tempForDispatchCache()); michael@0: else michael@0: addState->dispatchScratch = ToRegister(ins->toSetPropertyCacheT()->tempForDispatchCache()); michael@0: } michael@0: michael@0: void michael@0: SetElementParIC::initializeAddCacheState(LInstruction *ins, AddCacheState *addState) michael@0: { michael@0: // We don't have an output register to reuse, but luckily SetElementCache michael@0: // already needs a temp. michael@0: JS_ASSERT(ins->isSetElementCacheV() || ins->isSetElementCacheT()); michael@0: if (ins->isSetElementCacheV()) michael@0: addState->dispatchScratch = ToRegister(ins->toSetElementCacheV()->temp()); michael@0: else michael@0: addState->dispatchScratch = ToRegister(ins->toSetElementCacheT()->temp()); michael@0: } michael@0: michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: class OutOfLineTruncate : public OutOfLineCodeBase michael@0: { michael@0: LTruncateDToInt32 *ins_; michael@0: michael@0: public: michael@0: OutOfLineTruncate(LTruncateDToInt32 *ins) michael@0: : ins_(ins) michael@0: { } michael@0: michael@0: bool accept(CodeGeneratorX86 *codegen) { michael@0: return codegen->visitOutOfLineTruncate(this); michael@0: } michael@0: LTruncateDToInt32 *ins() const { michael@0: return ins_; michael@0: } michael@0: }; michael@0: michael@0: class OutOfLineTruncateFloat32 : public OutOfLineCodeBase michael@0: { michael@0: LTruncateFToInt32 *ins_; michael@0: michael@0: public: michael@0: OutOfLineTruncateFloat32(LTruncateFToInt32 *ins) michael@0: : ins_(ins) michael@0: { } michael@0: michael@0: bool accept(CodeGeneratorX86 *codegen) { michael@0: return codegen->visitOutOfLineTruncateFloat32(this); michael@0: } michael@0: LTruncateFToInt32 *ins() const { michael@0: return ins_; michael@0: } michael@0: }; michael@0: michael@0: } // namespace jit michael@0: } // namespace js michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitTruncateDToInt32(LTruncateDToInt32 *ins) michael@0: { michael@0: FloatRegister input = ToFloatRegister(ins->input()); michael@0: Register output = ToRegister(ins->output()); michael@0: michael@0: OutOfLineTruncate *ool = new(alloc()) OutOfLineTruncate(ins); michael@0: if (!addOutOfLineCode(ool)) michael@0: return false; michael@0: michael@0: masm.branchTruncateDouble(input, output, ool->entry()); michael@0: masm.bind(ool->rejoin()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitTruncateFToInt32(LTruncateFToInt32 *ins) michael@0: { michael@0: FloatRegister input = ToFloatRegister(ins->input()); michael@0: Register output = ToRegister(ins->output()); michael@0: michael@0: OutOfLineTruncateFloat32 *ool = new(alloc()) OutOfLineTruncateFloat32(ins); michael@0: if (!addOutOfLineCode(ool)) michael@0: return false; michael@0: michael@0: masm.branchTruncateFloat32(input, output, ool->entry()); michael@0: masm.bind(ool->rejoin()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitOutOfLineTruncate(OutOfLineTruncate *ool) michael@0: { michael@0: LTruncateDToInt32 *ins = ool->ins(); michael@0: FloatRegister input = ToFloatRegister(ins->input()); michael@0: Register output = ToRegister(ins->output()); michael@0: michael@0: Label fail; michael@0: michael@0: if (Assembler::HasSSE3()) { michael@0: // Push double. michael@0: masm.subl(Imm32(sizeof(double)), esp); michael@0: masm.storeDouble(input, Operand(esp, 0)); michael@0: michael@0: static const uint32_t EXPONENT_MASK = 0x7ff00000; michael@0: static const uint32_t EXPONENT_SHIFT = FloatingPoint::ExponentShift - 32; michael@0: static const uint32_t TOO_BIG_EXPONENT = (FloatingPoint::ExponentBias + 63) michael@0: << EXPONENT_SHIFT; michael@0: michael@0: // Check exponent to avoid fp exceptions. michael@0: Label failPopDouble; michael@0: masm.load32(Address(esp, 4), output); michael@0: masm.and32(Imm32(EXPONENT_MASK), output); michael@0: masm.branch32(Assembler::GreaterThanOrEqual, output, Imm32(TOO_BIG_EXPONENT), &failPopDouble); michael@0: michael@0: // Load double, perform 64-bit truncation. michael@0: masm.fld(Operand(esp, 0)); michael@0: masm.fisttp(Operand(esp, 0)); michael@0: michael@0: // Load low word, pop double and jump back. michael@0: masm.load32(Address(esp, 0), output); michael@0: masm.addl(Imm32(sizeof(double)), esp); michael@0: masm.jump(ool->rejoin()); michael@0: michael@0: masm.bind(&failPopDouble); michael@0: masm.addl(Imm32(sizeof(double)), esp); michael@0: masm.jump(&fail); michael@0: } else { michael@0: FloatRegister temp = ToFloatRegister(ins->tempFloat()); michael@0: michael@0: // Try to convert doubles representing integers within 2^32 of a signed michael@0: // integer, by adding/subtracting 2^32 and then trying to convert to int32. michael@0: // This has to be an exact conversion, as otherwise the truncation works michael@0: // incorrectly on the modified value. michael@0: masm.xorpd(ScratchFloatReg, ScratchFloatReg); michael@0: masm.ucomisd(input, ScratchFloatReg); michael@0: masm.j(Assembler::Parity, &fail); michael@0: michael@0: { michael@0: Label positive; michael@0: masm.j(Assembler::Above, &positive); michael@0: michael@0: masm.loadConstantDouble(4294967296.0, temp); michael@0: Label skip; michael@0: masm.jmp(&skip); michael@0: michael@0: masm.bind(&positive); michael@0: masm.loadConstantDouble(-4294967296.0, temp); michael@0: masm.bind(&skip); michael@0: } michael@0: michael@0: masm.addsd(input, temp); michael@0: masm.cvttsd2si(temp, output); michael@0: masm.cvtsi2sd(output, ScratchFloatReg); michael@0: michael@0: masm.ucomisd(temp, ScratchFloatReg); michael@0: masm.j(Assembler::Parity, &fail); michael@0: masm.j(Assembler::Equal, ool->rejoin()); michael@0: } michael@0: michael@0: masm.bind(&fail); michael@0: { michael@0: saveVolatile(output); michael@0: michael@0: masm.setupUnalignedABICall(1, output); michael@0: masm.passABIArg(input, MoveOp::DOUBLE); michael@0: if (gen->compilingAsmJS()) michael@0: masm.callWithABI(AsmJSImm_ToInt32); michael@0: else michael@0: masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32)); michael@0: masm.storeCallResult(output); michael@0: michael@0: restoreVolatile(output); michael@0: } michael@0: michael@0: masm.jump(ool->rejoin()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CodeGeneratorX86::visitOutOfLineTruncateFloat32(OutOfLineTruncateFloat32 *ool) michael@0: { michael@0: LTruncateFToInt32 *ins = ool->ins(); michael@0: FloatRegister input = ToFloatRegister(ins->input()); michael@0: Register output = ToRegister(ins->output()); michael@0: michael@0: Label fail; michael@0: michael@0: if (Assembler::HasSSE3()) { michael@0: // Push float32, but subtracts 64 bits so that the value popped by fisttp fits michael@0: masm.subl(Imm32(sizeof(uint64_t)), esp); michael@0: masm.storeFloat32(input, Operand(esp, 0)); michael@0: michael@0: static const uint32_t EXPONENT_MASK = FloatingPoint::ExponentBits; michael@0: static const uint32_t EXPONENT_SHIFT = FloatingPoint::ExponentShift; michael@0: // Integers are still 64 bits long, so we can still test for an exponent > 63. michael@0: static const uint32_t TOO_BIG_EXPONENT = (FloatingPoint::ExponentBias + 63) michael@0: << EXPONENT_SHIFT; michael@0: michael@0: // Check exponent to avoid fp exceptions. michael@0: Label failPopFloat; michael@0: masm.movl(Operand(esp, 0), output); michael@0: masm.and32(Imm32(EXPONENT_MASK), output); michael@0: masm.branch32(Assembler::GreaterThanOrEqual, output, Imm32(TOO_BIG_EXPONENT), &failPopFloat); michael@0: michael@0: // Load float, perform 32-bit truncation. michael@0: masm.fld32(Operand(esp, 0)); michael@0: masm.fisttp(Operand(esp, 0)); michael@0: michael@0: // Load low word, pop 64bits and jump back. michael@0: masm.movl(Operand(esp, 0), output); michael@0: masm.addl(Imm32(sizeof(uint64_t)), esp); michael@0: masm.jump(ool->rejoin()); michael@0: michael@0: masm.bind(&failPopFloat); michael@0: masm.addl(Imm32(sizeof(uint64_t)), esp); michael@0: masm.jump(&fail); michael@0: } else { michael@0: FloatRegister temp = ToFloatRegister(ins->tempFloat()); michael@0: michael@0: // Try to convert float32 representing integers within 2^32 of a signed michael@0: // integer, by adding/subtracting 2^32 and then trying to convert to int32. michael@0: // This has to be an exact conversion, as otherwise the truncation works michael@0: // incorrectly on the modified value. michael@0: masm.xorps(ScratchFloatReg, ScratchFloatReg); michael@0: masm.ucomiss(input, ScratchFloatReg); michael@0: masm.j(Assembler::Parity, &fail); michael@0: michael@0: { michael@0: Label positive; michael@0: masm.j(Assembler::Above, &positive); michael@0: michael@0: masm.loadConstantFloat32(4294967296.f, temp); michael@0: Label skip; michael@0: masm.jmp(&skip); michael@0: michael@0: masm.bind(&positive); michael@0: masm.loadConstantFloat32(-4294967296.f, temp); michael@0: masm.bind(&skip); michael@0: } michael@0: michael@0: masm.addss(input, temp); michael@0: masm.cvttss2si(temp, output); michael@0: masm.cvtsi2ss(output, ScratchFloatReg); michael@0: michael@0: masm.ucomiss(temp, ScratchFloatReg); michael@0: masm.j(Assembler::Parity, &fail); michael@0: masm.j(Assembler::Equal, ool->rejoin()); michael@0: } michael@0: michael@0: masm.bind(&fail); michael@0: { michael@0: saveVolatile(output); michael@0: michael@0: masm.push(input); michael@0: masm.setupUnalignedABICall(1, output); michael@0: masm.cvtss2sd(input, input); michael@0: masm.passABIArg(input, MoveOp::DOUBLE); michael@0: michael@0: if (gen->compilingAsmJS()) michael@0: masm.callWithABI(AsmJSImm_ToInt32); michael@0: else michael@0: masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, js::ToInt32)); michael@0: michael@0: masm.storeCallResult(output); michael@0: masm.pop(input); michael@0: michael@0: restoreVolatile(output); michael@0: } michael@0: michael@0: masm.jump(ool->rejoin()); michael@0: return true; michael@0: }