| /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
| * vim: set ts=8 sts=4 et sw=4 tw=99: |
| * This Source Code Form is subject to the terms of the Mozilla Public |
| * License, v. 2.0. If a copy of the MPL was not distributed with this |
| * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #include "MacroAssembler-x64.h" |
| #include "jit/MoveEmitter.h" |
| #include "jit/IonFrames.h" |
| |
| #include "jsscriptinlines.h" |
| |
| using namespace js; |
| using namespace js::jit; |
| |
| void |
| MacroAssemblerX64::setupABICall(uint32_t args) |
| { |
| JS_ASSERT(!inCall_); |
| inCall_ = true; |
| |
| args_ = args; |
| passedIntArgs_ = 0; |
| passedFloatArgs_ = 0; |
| stackForCall_ = ShadowStackSpace; |
| } |
| |
| void |
| MacroAssemblerX64::setupAlignedABICall(uint32_t args) |
| { |
| setupABICall(args); |
| dynamicAlignment_ = false; |
| } |
| |
| void |
| MacroAssemblerX64::setupUnalignedABICall(uint32_t args, const Register &scratch) |
| { |
| setupABICall(args); |
| dynamicAlignment_ = true; |
| |
| movq(rsp, scratch); |
| andq(Imm32(~(StackAlignment - 1)), rsp); |
| push(scratch); |
| } |
| |
| void |
| MacroAssemblerX64::passABIArg(const MoveOperand &from) |
| { |
| MoveOperand to; |
| if (from.isDouble()) { |
| FloatRegister dest; |
| if (GetFloatArgReg(passedIntArgs_, passedFloatArgs_++, &dest)) { |
| if (from.isFloatReg() && from.floatReg() == dest) { |
| // Nothing to do; the value is in the right register already |
| return; |
| } |
| to = MoveOperand(dest); |
| } else { |
| to = MoveOperand(StackPointer, stackForCall_); |
| stackForCall_ += sizeof(double); |
| } |
| enoughMemory_ = moveResolver_.addMove(from, to, Move::DOUBLE); |
| } else { |
| Register dest; |
| if (GetIntArgReg(passedIntArgs_++, passedFloatArgs_, &dest)) { |
| if (from.isGeneralReg() && from.reg() == dest) { |
| // Nothing to do; the value is in the right register already |
| return; |
| } |
| to = MoveOperand(dest); |
| } else { |
| to = MoveOperand(StackPointer, stackForCall_); |
| stackForCall_ += sizeof(int64_t); |
| } |
| enoughMemory_ = moveResolver_.addMove(from, to, Move::GENERAL); |
| } |
| } |
| |
| void |
| MacroAssemblerX64::passABIArg(const Register ®) |
| { |
| passABIArg(MoveOperand(reg)); |
| } |
| |
| void |
| MacroAssemblerX64::passABIArg(const FloatRegister ®) |
| { |
| passABIArg(MoveOperand(reg)); |
| } |
| |
| void |
| MacroAssemblerX64::callWithABIPre(uint32_t *stackAdjust) |
| { |
| JS_ASSERT(inCall_); |
| JS_ASSERT(args_ == passedIntArgs_ + passedFloatArgs_); |
| |
| if (dynamicAlignment_) { |
| *stackAdjust = stackForCall_ |
| + ComputeByteAlignment(stackForCall_ + STACK_SLOT_SIZE, |
| StackAlignment); |
| } else { |
| *stackAdjust = stackForCall_ |
| + ComputeByteAlignment(stackForCall_ + framePushed_, |
| StackAlignment); |
| } |
| |
| reserveStack(*stackAdjust); |
| |
| // Position all arguments. |
| { |
| enoughMemory_ &= moveResolver_.resolve(); |
| if (!enoughMemory_) |
| return; |
| |
| MoveEmitter emitter(*this); |
| emitter.emit(moveResolver_); |
| emitter.finish(); |
| } |
| |
| #ifdef DEBUG |
| { |
| Label good; |
| testq(rsp, Imm32(StackAlignment - 1)); |
| j(Equal, &good); |
| breakpoint(); |
| bind(&good); |
| } |
| #endif |
| } |
| |
| void |
| MacroAssemblerX64::callWithABIPost(uint32_t stackAdjust, Result result) |
| { |
| freeStack(stackAdjust); |
| if (dynamicAlignment_) |
| pop(rsp); |
| |
| JS_ASSERT(inCall_); |
| inCall_ = false; |
| } |
| |
| void |
| MacroAssemblerX64::callWithABI(void *fun, Result result) |
| { |
| uint32_t stackAdjust; |
| callWithABIPre(&stackAdjust); |
| call(ImmWord(fun)); |
| callWithABIPost(stackAdjust, result); |
| } |
| |
| static bool |
| IsIntArgReg(Register reg) |
| { |
| for (uint32_t i = 0; i < NumIntArgRegs; i++) { |
| if (IntArgRegs[i] == reg) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| void |
| MacroAssemblerX64::callWithABI(Address fun, Result result) |
| { |
| if (IsIntArgReg(fun.base)) { |
| // Callee register may be clobbered for an argument. Move the callee to |
| // r10, a volatile, non-argument register. |
| moveResolver_.addMove(MoveOperand(fun.base), MoveOperand(r10), Move::GENERAL); |
| fun.base = r10; |
| } |
| |
| JS_ASSERT(!IsIntArgReg(fun.base)); |
| |
| uint32_t stackAdjust; |
| callWithABIPre(&stackAdjust); |
| call(Operand(fun)); |
| callWithABIPost(stackAdjust, result); |
| } |
| |
| void |
| MacroAssemblerX64::handleFailureWithHandler(void *handler) |
| { |
| // Reserve space for exception information. |
| subq(Imm32(sizeof(ResumeFromException)), rsp); |
| movq(rsp, rax); |
| |
| // Ask for an exception handler. |
| setupUnalignedABICall(1, rcx); |
| passABIArg(rax); |
| callWithABI(handler); |
| |
| Label entryFrame; |
| Label catch_; |
| Label finally; |
| Label return_; |
| |
| loadPtr(Address(rsp, offsetof(ResumeFromException, kind)), rax); |
| branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_ENTRY_FRAME), &entryFrame); |
| branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_CATCH), &catch_); |
| branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_FINALLY), &finally); |
| branch32(Assembler::Equal, rax, Imm32(ResumeFromException::RESUME_FORCED_RETURN), &return_); |
| |
| breakpoint(); // Invalid kind. |
| |
| // No exception handler. Load the error value, load the new stack pointer |
| // and return from the entry frame. |
| bind(&entryFrame); |
| moveValue(MagicValue(JS_ION_ERROR), JSReturnOperand); |
| movq(Operand(rsp, offsetof(ResumeFromException, stackPointer)), rsp); |
| ret(); |
| |
| // If we found a catch handler, this must be a baseline frame. Restore state |
| // and jump to the catch block. |
| bind(&catch_); |
| movq(Operand(rsp, offsetof(ResumeFromException, target)), rax); |
| movq(Operand(rsp, offsetof(ResumeFromException, framePointer)), rbp); |
| movq(Operand(rsp, offsetof(ResumeFromException, stackPointer)), rsp); |
| jmp(Operand(rax)); |
| |
| // If we found a finally block, this must be a baseline frame. Push |
| // two values expected by JSOP_RETSUB: BooleanValue(true) and the |
| // exception. |
| bind(&finally); |
| ValueOperand exception = ValueOperand(rcx); |
| loadValue(Operand(esp, offsetof(ResumeFromException, exception)), exception); |
| |
| movq(Operand(rsp, offsetof(ResumeFromException, target)), rax); |
| movq(Operand(rsp, offsetof(ResumeFromException, framePointer)), rbp); |
| movq(Operand(rsp, offsetof(ResumeFromException, stackPointer)), rsp); |
| |
| pushValue(BooleanValue(true)); |
| pushValue(exception); |
| jmp(Operand(rax)); |
| |
| // Only used in debug mode. Return BaselineFrame->returnValue() to the caller. |
| bind(&return_); |
| movq(Operand(rsp, offsetof(ResumeFromException, framePointer)), rbp); |
| movq(Operand(rsp, offsetof(ResumeFromException, stackPointer)), rsp); |
| loadValue(Address(rbp, BaselineFrame::reverseOffsetOfReturnValue()), JSReturnOperand); |
| movq(rbp, rsp); |
| pop(rbp); |
| ret(); |
| } |
| |
| Assembler::Condition |
| MacroAssemblerX64::testNegativeZero(const FloatRegister ®, const Register &scratch) |
| { |
| movqsd(reg, scratch); |
| subq(Imm32(1), scratch); |
| return Overflow; |
| } |