blob: 85ddd491135e9e242df99d2f15c49bc4034e3cc7 [file] [log] [blame]
/* -*- 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 &reg)
{
passABIArg(MoveOperand(reg));
}
void
MacroAssemblerX64::passABIArg(const FloatRegister &reg)
{
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 &reg, const Register &scratch)
{
movqsd(reg, scratch);
subq(Imm32(1), scratch);
return Overflow;
}