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/MacroAssembler-x86.h" michael@0: michael@0: #include "mozilla/Casting.h" michael@0: michael@0: #include "jit/Bailouts.h" michael@0: #include "jit/BaselineFrame.h" michael@0: #include "jit/IonFrames.h" michael@0: #include "jit/MoveEmitter.h" michael@0: michael@0: #include "jsscriptinlines.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: MacroAssemblerX86::Double * michael@0: MacroAssemblerX86::getDouble(double d) michael@0: { michael@0: if (!doubleMap_.initialized()) { michael@0: enoughMemory_ &= doubleMap_.init(); michael@0: if (!enoughMemory_) michael@0: return nullptr; michael@0: } michael@0: size_t doubleIndex; michael@0: DoubleMap::AddPtr p = doubleMap_.lookupForAdd(d); michael@0: if (p) { michael@0: doubleIndex = p->value(); michael@0: } else { michael@0: doubleIndex = doubles_.length(); michael@0: enoughMemory_ &= doubles_.append(Double(d)); michael@0: enoughMemory_ &= doubleMap_.add(p, d, doubleIndex); michael@0: if (!enoughMemory_) michael@0: return nullptr; michael@0: } michael@0: Double &dbl = doubles_[doubleIndex]; michael@0: JS_ASSERT(!dbl.uses.bound()); michael@0: return &dbl; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::loadConstantDouble(double d, const FloatRegister &dest) michael@0: { michael@0: if (maybeInlineDouble(d, dest)) michael@0: return; michael@0: Double *dbl = getDouble(d); michael@0: if (!dbl) michael@0: return; michael@0: masm.movsd_mr(reinterpret_cast(dbl->uses.prev()), dest.code()); michael@0: dbl->uses.setPrev(masm.size()); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::addConstantDouble(double d, const FloatRegister &dest) michael@0: { michael@0: Double *dbl = getDouble(d); michael@0: if (!dbl) michael@0: return; michael@0: masm.addsd_mr(reinterpret_cast(dbl->uses.prev()), dest.code()); michael@0: dbl->uses.setPrev(masm.size()); michael@0: } michael@0: michael@0: MacroAssemblerX86::Float * michael@0: MacroAssemblerX86::getFloat(float f) michael@0: { michael@0: if (!floatMap_.initialized()) { michael@0: enoughMemory_ &= floatMap_.init(); michael@0: if (!enoughMemory_) michael@0: return nullptr; michael@0: } michael@0: size_t floatIndex; michael@0: FloatMap::AddPtr p = floatMap_.lookupForAdd(f); michael@0: if (p) { michael@0: floatIndex = p->value(); michael@0: } else { michael@0: floatIndex = floats_.length(); michael@0: enoughMemory_ &= floats_.append(Float(f)); michael@0: enoughMemory_ &= floatMap_.add(p, f, floatIndex); michael@0: if (!enoughMemory_) michael@0: return nullptr; michael@0: } michael@0: Float &flt = floats_[floatIndex]; michael@0: JS_ASSERT(!flt.uses.bound()); michael@0: return &flt; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::loadConstantFloat32(float f, const FloatRegister &dest) michael@0: { michael@0: if (maybeInlineFloat(f, dest)) michael@0: return; michael@0: Float *flt = getFloat(f); michael@0: if (!flt) michael@0: return; michael@0: masm.movss_mr(reinterpret_cast(flt->uses.prev()), dest.code()); michael@0: flt->uses.setPrev(masm.size()); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::addConstantFloat32(float f, const FloatRegister &dest) michael@0: { michael@0: Float *flt = getFloat(f); michael@0: if (!flt) michael@0: return; michael@0: masm.addss_mr(reinterpret_cast(flt->uses.prev()), dest.code()); michael@0: flt->uses.setPrev(masm.size()); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::finish() michael@0: { michael@0: if (!doubles_.empty()) michael@0: masm.align(sizeof(double)); michael@0: for (size_t i = 0; i < doubles_.length(); i++) { michael@0: CodeLabel cl(doubles_[i].uses); michael@0: writeDoubleConstant(doubles_[i].value, cl.src()); michael@0: enoughMemory_ &= addCodeLabel(cl); michael@0: if (!enoughMemory_) michael@0: return; michael@0: } michael@0: michael@0: if (!floats_.empty()) michael@0: masm.align(sizeof(float)); michael@0: for (size_t i = 0; i < floats_.length(); i++) { michael@0: CodeLabel cl(floats_[i].uses); michael@0: writeFloatConstant(floats_[i].value, cl.src()); michael@0: enoughMemory_ &= addCodeLabel(cl); michael@0: if (!enoughMemory_) michael@0: return; michael@0: } michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::setupABICall(uint32_t args) michael@0: { michael@0: JS_ASSERT(!inCall_); michael@0: inCall_ = true; michael@0: michael@0: args_ = args; michael@0: passedArgs_ = 0; michael@0: stackForCall_ = 0; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::setupAlignedABICall(uint32_t args) michael@0: { michael@0: setupABICall(args); michael@0: dynamicAlignment_ = false; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::setupUnalignedABICall(uint32_t args, const Register &scratch) michael@0: { michael@0: setupABICall(args); michael@0: dynamicAlignment_ = true; michael@0: michael@0: movl(esp, scratch); michael@0: andl(Imm32(~(StackAlignment - 1)), esp); michael@0: push(scratch); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::passABIArg(const MoveOperand &from, MoveOp::Type type) michael@0: { michael@0: ++passedArgs_; michael@0: MoveOperand to = MoveOperand(StackPointer, stackForCall_); michael@0: switch (type) { michael@0: case MoveOp::FLOAT32: stackForCall_ += sizeof(float); break; michael@0: case MoveOp::DOUBLE: stackForCall_ += sizeof(double); break; michael@0: case MoveOp::INT32: stackForCall_ += sizeof(int32_t); break; michael@0: case MoveOp::GENERAL: stackForCall_ += sizeof(intptr_t); break; michael@0: default: MOZ_ASSUME_UNREACHABLE("Unexpected argument type"); michael@0: } michael@0: enoughMemory_ &= moveResolver_.addMove(from, to, type); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::passABIArg(const Register ®) michael@0: { michael@0: passABIArg(MoveOperand(reg), MoveOp::GENERAL); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::passABIArg(const FloatRegister ®, MoveOp::Type type) michael@0: { michael@0: passABIArg(MoveOperand(reg), type); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::callWithABIPre(uint32_t *stackAdjust) michael@0: { michael@0: JS_ASSERT(inCall_); michael@0: JS_ASSERT(args_ == passedArgs_); michael@0: michael@0: if (dynamicAlignment_) { michael@0: *stackAdjust = stackForCall_ michael@0: + ComputeByteAlignment(stackForCall_ + sizeof(intptr_t), michael@0: StackAlignment); michael@0: } else { michael@0: *stackAdjust = stackForCall_ michael@0: + ComputeByteAlignment(stackForCall_ + framePushed_, michael@0: StackAlignment); michael@0: } michael@0: michael@0: reserveStack(*stackAdjust); michael@0: michael@0: // Position all arguments. michael@0: { michael@0: enoughMemory_ &= moveResolver_.resolve(); michael@0: if (!enoughMemory_) michael@0: return; michael@0: michael@0: MoveEmitter emitter(*this); michael@0: emitter.emit(moveResolver_); michael@0: emitter.finish(); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: { michael@0: // Check call alignment. michael@0: Label good; michael@0: testl(esp, Imm32(StackAlignment - 1)); michael@0: j(Equal, &good); michael@0: breakpoint(); michael@0: bind(&good); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result) michael@0: { michael@0: freeStack(stackAdjust); michael@0: if (result == MoveOp::DOUBLE) { michael@0: reserveStack(sizeof(double)); michael@0: fstp(Operand(esp, 0)); michael@0: loadDouble(Operand(esp, 0), ReturnFloatReg); michael@0: freeStack(sizeof(double)); michael@0: } else if (result == MoveOp::FLOAT32) { michael@0: reserveStack(sizeof(float)); michael@0: fstp32(Operand(esp, 0)); michael@0: loadFloat32(Operand(esp, 0), ReturnFloatReg); michael@0: freeStack(sizeof(float)); michael@0: } michael@0: if (dynamicAlignment_) michael@0: pop(esp); michael@0: michael@0: JS_ASSERT(inCall_); michael@0: inCall_ = false; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::callWithABI(void *fun, MoveOp::Type result) michael@0: { michael@0: uint32_t stackAdjust; michael@0: callWithABIPre(&stackAdjust); michael@0: call(ImmPtr(fun)); michael@0: callWithABIPost(stackAdjust, result); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::callWithABI(AsmJSImmPtr fun, MoveOp::Type result) michael@0: { michael@0: uint32_t stackAdjust; michael@0: callWithABIPre(&stackAdjust); michael@0: call(fun); michael@0: callWithABIPost(stackAdjust, result); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::callWithABI(const Address &fun, MoveOp::Type result) michael@0: { michael@0: uint32_t stackAdjust; michael@0: callWithABIPre(&stackAdjust); michael@0: call(Operand(fun)); michael@0: callWithABIPost(stackAdjust, result); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::handleFailureWithHandler(void *handler) michael@0: { michael@0: // Reserve space for exception information. michael@0: subl(Imm32(sizeof(ResumeFromException)), esp); michael@0: movl(esp, eax); michael@0: michael@0: // Ask for an exception handler. michael@0: setupUnalignedABICall(1, ecx); michael@0: passABIArg(eax); michael@0: callWithABI(handler); michael@0: michael@0: JitCode *excTail = GetIonContext()->runtime->jitRuntime()->getExceptionTail(); michael@0: jmp(excTail); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::handleFailureWithHandlerTail() michael@0: { michael@0: Label entryFrame; michael@0: Label catch_; michael@0: Label finally; michael@0: Label return_; michael@0: Label bailout; michael@0: michael@0: loadPtr(Address(esp, offsetof(ResumeFromException, kind)), eax); michael@0: branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame); michael@0: branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_CATCH), &catch_); michael@0: branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_FINALLY), &finally); michael@0: branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_); michael@0: branch32(Assembler::Equal, eax, Imm32(ResumeFromException::RESUME_BAILOUT), &bailout); michael@0: michael@0: breakpoint(); // Invalid kind. michael@0: michael@0: // No exception handler. Load the error value, load the new stack pointer michael@0: // and return from the entry frame. michael@0: bind(&entryFrame); michael@0: moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); michael@0: loadPtr(Address(esp, offsetof(ResumeFromException, stackPointer)), esp); michael@0: ret(); michael@0: michael@0: // If we found a catch handler, this must be a baseline frame. Restore state michael@0: // and jump to the catch block. michael@0: bind(&catch_); michael@0: loadPtr(Address(esp, offsetof(ResumeFromException, target)), eax); michael@0: loadPtr(Address(esp, offsetof(ResumeFromException, framePointer)), ebp); michael@0: loadPtr(Address(esp, offsetof(ResumeFromException, stackPointer)), esp); michael@0: jmp(Operand(eax)); michael@0: michael@0: // If we found a finally block, this must be a baseline frame. Push michael@0: // two values expected by JSOP_RETSUB: BooleanValue(true) and the michael@0: // exception. michael@0: bind(&finally); michael@0: ValueOperand exception = ValueOperand(ecx, edx); michael@0: loadValue(Address(esp, offsetof(ResumeFromException, exception)), exception); michael@0: michael@0: loadPtr(Address(esp, offsetof(ResumeFromException, target)), eax); michael@0: loadPtr(Address(esp, offsetof(ResumeFromException, framePointer)), ebp); michael@0: loadPtr(Address(esp, offsetof(ResumeFromException, stackPointer)), esp); michael@0: michael@0: pushValue(BooleanValue(true)); michael@0: pushValue(exception); michael@0: jmp(Operand(eax)); michael@0: michael@0: // Only used in debug mode. Return BaselineFrame->returnValue() to the caller. michael@0: bind(&return_); michael@0: loadPtr(Address(esp, offsetof(ResumeFromException, framePointer)), ebp); michael@0: loadPtr(Address(esp, offsetof(ResumeFromException, stackPointer)), esp); michael@0: loadValue(Address(ebp, BaselineFrame::reverseOffsetOfReturnValue()), JSReturnOperand); michael@0: movl(ebp, esp); michael@0: pop(ebp); michael@0: ret(); michael@0: michael@0: // If we are bailing out to baseline to handle an exception, jump to michael@0: // the bailout tail stub. michael@0: bind(&bailout); michael@0: loadPtr(Address(esp, offsetof(ResumeFromException, bailoutInfo)), ecx); michael@0: movl(Imm32(BAILOUT_RETURN_OK), eax); michael@0: jmp(Operand(esp, offsetof(ResumeFromException, target))); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::branchTestValue(Condition cond, const ValueOperand &value, const Value &v, Label *label) michael@0: { michael@0: jsval_layout jv = JSVAL_TO_IMPL(v); michael@0: if (v.isMarkable()) michael@0: cmpl(value.payloadReg(), ImmGCPtr(reinterpret_cast(v.toGCThing()))); michael@0: else michael@0: cmpl(value.payloadReg(), Imm32(jv.s.payload.i32)); michael@0: michael@0: if (cond == Equal) { michael@0: Label done; michael@0: j(NotEqual, &done); michael@0: { michael@0: cmpl(value.typeReg(), Imm32(jv.s.tag)); michael@0: j(Equal, label); michael@0: } michael@0: bind(&done); michael@0: } else { michael@0: JS_ASSERT(cond == NotEqual); michael@0: j(NotEqual, label); michael@0: michael@0: cmpl(value.typeReg(), Imm32(jv.s.tag)); michael@0: j(NotEqual, label); michael@0: } michael@0: } michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: michael@0: void michael@0: MacroAssemblerX86::branchPtrInNurseryRange(Register ptr, Register temp, Label *label) michael@0: { michael@0: JS_ASSERT(ptr != temp); michael@0: JS_ASSERT(temp != InvalidReg); // A temp register is required for x86. michael@0: michael@0: const Nursery &nursery = GetIonContext()->runtime->gcNursery(); michael@0: movePtr(ImmWord(-ptrdiff_t(nursery.start())), temp); michael@0: addPtr(ptr, temp); michael@0: branchPtr(Assembler::Below, temp, Imm32(Nursery::NurserySize), label); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86::branchValueIsNurseryObject(ValueOperand value, Register temp, Label *label) michael@0: { michael@0: Label done; michael@0: michael@0: branchTestObject(Assembler::NotEqual, value, &done); michael@0: branchPtrInNurseryRange(value.payloadReg(), temp, label); michael@0: michael@0: bind(&done); michael@0: } michael@0: michael@0: #endif