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/shared/MacroAssembler-x86-shared.h" michael@0: michael@0: #include "jit/IonFrames.h" michael@0: #include "jit/IonMacroAssembler.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: void michael@0: MacroAssembler::PushRegsInMask(RegisterSet set) michael@0: { michael@0: int32_t diffF = set.fpus().size() * sizeof(double); michael@0: int32_t diffG = set.gprs().size() * sizeof(intptr_t); michael@0: michael@0: // On x86, always use push to push the integer registers, as it's fast michael@0: // on modern hardware and it's a small instruction. michael@0: for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) { michael@0: diffG -= sizeof(intptr_t); michael@0: Push(*iter); michael@0: } michael@0: JS_ASSERT(diffG == 0); michael@0: michael@0: reserveStack(diffF); michael@0: for (FloatRegisterBackwardIterator iter(set.fpus()); iter.more(); iter++) { michael@0: diffF -= sizeof(double); michael@0: storeDouble(*iter, Address(StackPointer, diffF)); michael@0: } michael@0: JS_ASSERT(diffF == 0); michael@0: } michael@0: michael@0: void michael@0: MacroAssembler::PopRegsInMaskIgnore(RegisterSet set, RegisterSet ignore) michael@0: { michael@0: int32_t diffG = set.gprs().size() * sizeof(intptr_t); michael@0: int32_t diffF = set.fpus().size() * sizeof(double); michael@0: const int32_t reservedG = diffG; michael@0: const int32_t reservedF = diffF; michael@0: michael@0: for (FloatRegisterBackwardIterator iter(set.fpus()); iter.more(); iter++) { michael@0: diffF -= sizeof(double); michael@0: if (!ignore.has(*iter)) michael@0: loadDouble(Address(StackPointer, diffF), *iter); michael@0: } michael@0: freeStack(reservedF); michael@0: JS_ASSERT(diffF == 0); michael@0: michael@0: // On x86, use pop to pop the integer registers, if we're not going to michael@0: // ignore any slots, as it's fast on modern hardware and it's a small michael@0: // instruction. michael@0: if (ignore.empty(false)) { michael@0: for (GeneralRegisterForwardIterator iter(set.gprs()); iter.more(); iter++) { michael@0: diffG -= sizeof(intptr_t); michael@0: Pop(*iter); michael@0: } michael@0: } else { michael@0: for (GeneralRegisterBackwardIterator iter(set.gprs()); iter.more(); iter++) { michael@0: diffG -= sizeof(intptr_t); michael@0: if (!ignore.has(*iter)) michael@0: loadPtr(Address(StackPointer, diffG), *iter); michael@0: } michael@0: freeStack(reservedG); michael@0: } michael@0: JS_ASSERT(diffG == 0); michael@0: } michael@0: michael@0: // Note: this function clobbers the input register. michael@0: void michael@0: MacroAssembler::clampDoubleToUint8(FloatRegister input, Register output) michael@0: { michael@0: JS_ASSERT(input != ScratchFloatReg); michael@0: Label positive, done; michael@0: michael@0: // <= 0 or NaN --> 0 michael@0: zeroDouble(ScratchFloatReg); michael@0: branchDouble(DoubleGreaterThan, input, ScratchFloatReg, &positive); michael@0: { michael@0: move32(Imm32(0), output); michael@0: jump(&done); michael@0: } michael@0: michael@0: bind(&positive); michael@0: michael@0: // Add 0.5 and truncate. michael@0: loadConstantDouble(0.5, ScratchFloatReg); michael@0: addDouble(ScratchFloatReg, input); michael@0: michael@0: Label outOfRange; michael@0: michael@0: // Truncate to int32 and ensure the result <= 255. This relies on the michael@0: // processor setting output to a value > 255 for doubles outside the int32 michael@0: // range (for instance 0x80000000). michael@0: cvttsd2si(input, output); michael@0: branch32(Assembler::Above, output, Imm32(255), &outOfRange); michael@0: { michael@0: // Check if we had a tie. michael@0: convertInt32ToDouble(output, ScratchFloatReg); michael@0: branchDouble(DoubleNotEqual, input, ScratchFloatReg, &done); michael@0: michael@0: // It was a tie. Mask out the ones bit to get an even value. michael@0: // See also js_TypedArray_uint8_clamp_double. michael@0: and32(Imm32(~1), output); michael@0: jump(&done); michael@0: } michael@0: michael@0: // > 255 --> 255 michael@0: bind(&outOfRange); michael@0: { michael@0: move32(Imm32(255), output); michael@0: } michael@0: michael@0: bind(&done); michael@0: } michael@0: michael@0: // Builds an exit frame on the stack, with a return address to an internal michael@0: // non-function. Returns offset to be passed to markSafepointAt(). michael@0: bool michael@0: MacroAssemblerX86Shared::buildFakeExitFrame(const Register &scratch, uint32_t *offset) michael@0: { michael@0: mozilla::DebugOnly initialDepth = framePushed(); michael@0: michael@0: CodeLabel cl; michael@0: mov(cl.dest(), scratch); michael@0: michael@0: uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS); michael@0: Push(Imm32(descriptor)); michael@0: Push(scratch); michael@0: michael@0: bind(cl.src()); michael@0: *offset = currentOffset(); michael@0: michael@0: JS_ASSERT(framePushed() == initialDepth + IonExitFrameLayout::Size()); michael@0: return addCodeLabel(cl); michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86Shared::callWithExitFrame(JitCode *target) michael@0: { michael@0: uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS); michael@0: Push(Imm32(descriptor)); michael@0: call(target); michael@0: } michael@0: michael@0: bool michael@0: MacroAssemblerX86Shared::buildOOLFakeExitFrame(void *fakeReturnAddr) michael@0: { michael@0: uint32_t descriptor = MakeFrameDescriptor(framePushed(), JitFrame_IonJS); michael@0: Push(Imm32(descriptor)); michael@0: Push(ImmPtr(fakeReturnAddr)); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86Shared::branchNegativeZero(const FloatRegister ®, michael@0: const Register &scratch, michael@0: Label *label) michael@0: { michael@0: // Determines whether the low double contained in the XMM register reg michael@0: // is equal to -0.0. michael@0: michael@0: #if defined(JS_CODEGEN_X86) michael@0: Label nonZero; michael@0: michael@0: // Compare to zero. Lets through {0, -0}. michael@0: xorpd(ScratchFloatReg, ScratchFloatReg); michael@0: michael@0: // If reg is non-zero, jump to nonZero. michael@0: branchDouble(DoubleNotEqual, reg, ScratchFloatReg, &nonZero); michael@0: michael@0: // Input register is either zero or negative zero. Retrieve sign of input. michael@0: movmskpd(reg, scratch); michael@0: michael@0: // If reg is 1 or 3, input is negative zero. michael@0: // If reg is 0 or 2, input is a normal zero. michael@0: branchTest32(NonZero, scratch, Imm32(1), label); michael@0: michael@0: bind(&nonZero); michael@0: #elif defined(JS_CODEGEN_X64) michael@0: movq(reg, scratch); michael@0: cmpq(scratch, Imm32(1)); michael@0: j(Overflow, label); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: MacroAssemblerX86Shared::branchNegativeZeroFloat32(const FloatRegister ®, michael@0: const Register &scratch, michael@0: Label *label) michael@0: { michael@0: movd(reg, scratch); michael@0: cmpl(scratch, Imm32(1)); michael@0: j(Overflow, label); michael@0: }