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/x64/MacroAssembler-x64.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/JitCompartment.h" michael@0: #include "jit/MoveEmitter.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: void michael@0: MacroAssemblerX64::loadConstantDouble(double d, const FloatRegister &dest) michael@0: { michael@0: if (maybeInlineDouble(d, dest)) michael@0: return; michael@0: michael@0: if (!doubleMap_.initialized()) { michael@0: enoughMemory_ &= doubleMap_.init(); michael@0: if (!enoughMemory_) michael@0: return; michael@0: } michael@0: size_t doubleIndex; michael@0: if (DoubleMap::AddPtr p = doubleMap_.lookupForAdd(d)) { 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; michael@0: } michael@0: Double &dbl = doubles_[doubleIndex]; michael@0: JS_ASSERT(!dbl.uses.bound()); michael@0: michael@0: // The constants will be stored in a pool appended to the text (see michael@0: // finish()), so they will always be a fixed distance from the michael@0: // instructions which reference them. This allows the instructions to use michael@0: // PC-relative addressing. Use "jump" label support code, because we need michael@0: // the same PC-relative address patching that jumps use. michael@0: JmpSrc j = masm.movsd_ripr(dest.code()); michael@0: JmpSrc prev = JmpSrc(dbl.uses.use(j.offset())); michael@0: masm.setNextJump(j, prev); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX64::loadConstantFloat32(float f, const FloatRegister &dest) michael@0: { michael@0: if (maybeInlineFloat(f, dest)) michael@0: return; michael@0: michael@0: if (!floatMap_.initialized()) { michael@0: enoughMemory_ &= floatMap_.init(); michael@0: if (!enoughMemory_) michael@0: return; michael@0: } michael@0: size_t floatIndex; michael@0: if (FloatMap::AddPtr p = floatMap_.lookupForAdd(f)) { 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; michael@0: } michael@0: Float &flt = floats_[floatIndex]; michael@0: JS_ASSERT(!flt.uses.bound()); michael@0: michael@0: // See comment in loadConstantDouble michael@0: JmpSrc j = masm.movss_ripr(dest.code()); michael@0: JmpSrc prev = JmpSrc(flt.uses.use(j.offset())); michael@0: masm.setNextJump(j, prev); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX64::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: Double &dbl = doubles_[i]; michael@0: bind(&dbl.uses); michael@0: masm.doubleConstant(dbl.value); 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: Float &flt = floats_[i]; michael@0: bind(&flt.uses); michael@0: masm.floatConstant(flt.value); michael@0: } michael@0: michael@0: MacroAssemblerX86Shared::finish(); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX64::setupABICall(uint32_t args) michael@0: { michael@0: JS_ASSERT(!inCall_); michael@0: inCall_ = true; michael@0: michael@0: args_ = args; michael@0: passedIntArgs_ = 0; michael@0: passedFloatArgs_ = 0; michael@0: stackForCall_ = ShadowStackSpace; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX64::setupAlignedABICall(uint32_t args) michael@0: { michael@0: setupABICall(args); michael@0: dynamicAlignment_ = false; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX64::setupUnalignedABICall(uint32_t args, const Register &scratch) michael@0: { michael@0: setupABICall(args); michael@0: dynamicAlignment_ = true; michael@0: michael@0: movq(rsp, scratch); michael@0: andq(Imm32(~(StackAlignment - 1)), rsp); michael@0: push(scratch); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX64::passABIArg(const MoveOperand &from, MoveOp::Type type) michael@0: { michael@0: MoveOperand to; michael@0: switch (type) { michael@0: case MoveOp::FLOAT32: michael@0: case MoveOp::DOUBLE: { michael@0: FloatRegister dest; michael@0: if (GetFloatArgReg(passedIntArgs_, passedFloatArgs_++, &dest)) { michael@0: if (from.isFloatReg() && from.floatReg() == dest) { michael@0: // Nothing to do; the value is in the right register already michael@0: return; michael@0: } michael@0: to = MoveOperand(dest); michael@0: } else { michael@0: 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: default: MOZ_ASSUME_UNREACHABLE("Unexpected float register class argument type"); michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: case MoveOp::GENERAL: { michael@0: Register dest; michael@0: if (GetIntArgReg(passedIntArgs_++, passedFloatArgs_, &dest)) { michael@0: if (from.isGeneralReg() && from.reg() == dest) { michael@0: // Nothing to do; the value is in the right register already michael@0: return; michael@0: } michael@0: to = MoveOperand(dest); michael@0: } else { michael@0: to = MoveOperand(StackPointer, stackForCall_); michael@0: stackForCall_ += sizeof(int64_t); michael@0: } michael@0: break; michael@0: } michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unexpected argument type"); michael@0: } michael@0: michael@0: enoughMemory_ = moveResolver_.addMove(from, to, type); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX64::passABIArg(const Register ®) michael@0: { michael@0: passABIArg(MoveOperand(reg), MoveOp::GENERAL); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX64::passABIArg(const FloatRegister ®, MoveOp::Type type) michael@0: { michael@0: passABIArg(MoveOperand(reg), type); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX64::callWithABIPre(uint32_t *stackAdjust) michael@0: { michael@0: JS_ASSERT(inCall_); michael@0: JS_ASSERT(args_ == passedIntArgs_ + passedFloatArgs_); 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: Label good; michael@0: testq(rsp, 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: MacroAssemblerX64::callWithABIPost(uint32_t stackAdjust, MoveOp::Type result) michael@0: { michael@0: freeStack(stackAdjust); michael@0: if (dynamicAlignment_) michael@0: pop(rsp); michael@0: michael@0: JS_ASSERT(inCall_); michael@0: inCall_ = false; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX64::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: MacroAssemblerX64::callWithABI(AsmJSImmPtr imm, MoveOp::Type result) michael@0: { michael@0: uint32_t stackAdjust; michael@0: callWithABIPre(&stackAdjust); michael@0: call(imm); michael@0: callWithABIPost(stackAdjust, result); michael@0: } michael@0: michael@0: static bool michael@0: IsIntArgReg(Register reg) michael@0: { michael@0: for (uint32_t i = 0; i < NumIntArgRegs; i++) { michael@0: if (IntArgRegs[i] == reg) michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX64::callWithABI(Address fun, MoveOp::Type result) michael@0: { michael@0: if (IsIntArgReg(fun.base)) { michael@0: // Callee register may be clobbered for an argument. Move the callee to michael@0: // r10, a volatile, non-argument register. michael@0: moveResolver_.addMove(MoveOperand(fun.base), MoveOperand(r10), MoveOp::GENERAL); michael@0: fun.base = r10; michael@0: } michael@0: michael@0: JS_ASSERT(!IsIntArgReg(fun.base)); 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: MacroAssemblerX64::handleFailureWithHandler(void *handler) michael@0: { michael@0: // Reserve space for exception information. michael@0: subq(Imm32(sizeof(ResumeFromException)), rsp); michael@0: movq(rsp, rax); michael@0: michael@0: // Ask for an exception handler. michael@0: setupUnalignedABICall(1, rcx); michael@0: passABIArg(rax); 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: MacroAssemblerX64::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(rsp, offsetof(ResumeFromException, kind)), rax); michael@0: branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame); michael@0: branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_CATCH), &catch_); michael@0: branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_FINALLY), &finally); michael@0: branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_); michael@0: branch32(Assembler::Equal, rax, 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(rsp, offsetof(ResumeFromException, stackPointer)), rsp); 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(rsp, offsetof(ResumeFromException, target)), rax); michael@0: loadPtr(Address(rsp, offsetof(ResumeFromException, framePointer)), rbp); michael@0: loadPtr(Address(rsp, offsetof(ResumeFromException, stackPointer)), rsp); michael@0: jmp(Operand(rax)); 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(rcx); michael@0: loadValue(Address(esp, offsetof(ResumeFromException, exception)), exception); michael@0: michael@0: loadPtr(Address(rsp, offsetof(ResumeFromException, target)), rax); michael@0: loadPtr(Address(rsp, offsetof(ResumeFromException, framePointer)), rbp); michael@0: loadPtr(Address(rsp, offsetof(ResumeFromException, stackPointer)), rsp); michael@0: michael@0: pushValue(BooleanValue(true)); michael@0: pushValue(exception); michael@0: jmp(Operand(rax)); michael@0: michael@0: // Only used in debug mode. Return BaselineFrame->returnValue() to the caller. michael@0: bind(&return_); michael@0: loadPtr(Address(rsp, offsetof(ResumeFromException, framePointer)), rbp); michael@0: loadPtr(Address(rsp, offsetof(ResumeFromException, stackPointer)), rsp); michael@0: loadValue(Address(rbp, BaselineFrame::reverseOffsetOfReturnValue()), JSReturnOperand); michael@0: movq(rbp, rsp); michael@0: pop(rbp); 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)), r9); michael@0: mov(ImmWord(BAILOUT_RETURN_OK), rax); michael@0: jmp(Operand(rsp, offsetof(ResumeFromException, target))); michael@0: } michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: michael@0: void michael@0: MacroAssemblerX64::branchPtrInNurseryRange(Register ptr, Register temp, Label *label) michael@0: { michael@0: JS_ASSERT(ptr != temp); michael@0: JS_ASSERT(ptr != ScratchReg); michael@0: michael@0: const Nursery &nursery = GetIonContext()->runtime->gcNursery(); michael@0: movePtr(ImmWord(-ptrdiff_t(nursery.start())), ScratchReg); michael@0: addPtr(ptr, ScratchReg); michael@0: branchPtr(Assembler::Below, ScratchReg, Imm32(Nursery::NurserySize), label); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX64::branchValueIsNurseryObject(ValueOperand value, Register temp, Label *label) michael@0: { michael@0: // 'Value' representing the start of the nursery tagged as a JSObject michael@0: const Nursery &nursery = GetIonContext()->runtime->gcNursery(); michael@0: Value start = ObjectValue(*reinterpret_cast(nursery.start())); michael@0: michael@0: movePtr(ImmWord(-ptrdiff_t(start.asRawBits())), ScratchReg); michael@0: addPtr(value.valueReg(), ScratchReg); michael@0: branchPtr(Assembler::Below, ScratchReg, Imm32(Nursery::NurserySize), label); michael@0: } michael@0: michael@0: #endif