2009-07-10 12:58:34 -07:00
|
|
|
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
|
|
|
|
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
|
2008-06-19 10:47:58 -07:00
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is [Open Source Virtual Machine].
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Adobe System Incorporated.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 2004-2007
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Adobe AS3 Team
|
2008-09-02 10:15:26 -07:00
|
|
|
* Mozilla TraceMonkey Team
|
|
|
|
* Asko Tontti <atontti@cc.hut.fi>
|
2008-06-19 10:47:58 -07:00
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
2009-09-18 13:31:09 -07:00
|
|
|
#include "nanojit.h"
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2009-10-14 13:48:00 -07:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
// disable some specific warnings which are normally useful, but pervasive in the code-gen macros
|
|
|
|
#pragma warning(disable:4310) // cast truncates constant value
|
2008-07-07 02:47:40 -07:00
|
|
|
#endif
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
namespace nanojit
|
|
|
|
{
|
2009-09-18 13:31:09 -07:00
|
|
|
#if defined FEATURE_NANOJIT && defined NANOJIT_IA32
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
#ifdef NJ_VERBOSE
|
|
|
|
const char *regNames[] = {
|
|
|
|
"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
|
|
|
|
"xmm0","xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7",
|
2009-08-31 23:55:15 -07:00
|
|
|
"f0"
|
2009-07-10 12:58:34 -07:00
|
|
|
};
|
|
|
|
#endif
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2009-08-24 16:57:25 -07:00
|
|
|
#define TODO(x) do{ verbose_only(outputf(#x);) NanoAssertMsgf(false, "%s", #x); } while(0)
|
|
|
|
|
2008-06-19 10:47:58 -07:00
|
|
|
const Register Assembler::argRegs[] = { ECX, EDX };
|
|
|
|
const Register Assembler::retRegs[] = { EAX, EDX };
|
2008-10-13 13:29:18 -07:00
|
|
|
const Register Assembler::savedRegs[] = { EBX, ESI, EDI };
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
const static uint8_t max_abi_regs[] = {
|
|
|
|
2, /* ABI_FASTCALL */
|
|
|
|
1, /* ABI_THISCALL */
|
|
|
|
0, /* ABI_STDCALL */
|
|
|
|
0 /* ABI_CDECL */
|
|
|
|
};
|
|
|
|
|
2009-12-01 11:36:07 -08:00
|
|
|
static bool CheckForSSE2()
|
|
|
|
{
|
|
|
|
int features = 0;
|
|
|
|
#if defined _MSC_VER
|
|
|
|
__asm
|
|
|
|
{
|
|
|
|
pushad
|
|
|
|
mov eax, 1
|
|
|
|
cpuid
|
|
|
|
mov features, edx
|
|
|
|
popad
|
|
|
|
}
|
|
|
|
#elif defined __GNUC__
|
|
|
|
asm("xchg %%esi, %%ebx\n" /* we can't clobber ebx on gcc (PIC register) */
|
|
|
|
"mov $0x01, %%eax\n"
|
|
|
|
"cpuid\n"
|
|
|
|
"mov %%edx, %0\n"
|
|
|
|
"xchg %%esi, %%ebx\n"
|
|
|
|
: "=m" (features)
|
|
|
|
: /* We have no inputs */
|
|
|
|
: "%eax", "%esi", "%ecx", "%edx"
|
|
|
|
);
|
|
|
|
#elif defined __SUNPRO_C || defined __SUNPRO_CC
|
2010-01-05 15:28:40 -08:00
|
|
|
asm("push %%ebx\n"
|
2009-12-01 11:36:07 -08:00
|
|
|
"mov $0x01, %%eax\n"
|
|
|
|
"cpuid\n"
|
2010-01-05 15:28:40 -08:00
|
|
|
"pop %%ebx\n"
|
2009-12-01 11:36:07 -08:00
|
|
|
: "=d" (features)
|
|
|
|
: /* We have no inputs */
|
2010-01-05 15:28:40 -08:00
|
|
|
: "%eax", "%ecx"
|
2009-12-01 11:36:07 -08:00
|
|
|
);
|
|
|
|
#endif
|
|
|
|
return (features & (1<<26)) != 0;
|
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
void Assembler::nInit(AvmCore* core)
|
|
|
|
{
|
|
|
|
(void) core;
|
2009-12-01 11:36:07 -08:00
|
|
|
config.sse2 = config.sse2 && CheckForSSE2();
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2009-08-31 16:35:50 -07:00
|
|
|
void Assembler::nBeginAssembly() {
|
2009-10-07 14:25:29 -07:00
|
|
|
max_stk_args = 0;
|
2009-08-31 16:35:50 -07:00
|
|
|
}
|
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
NIns* Assembler::genPrologue()
|
|
|
|
{
|
2009-12-17 17:39:16 -08:00
|
|
|
// Prologue
|
2009-12-21 10:51:57 -08:00
|
|
|
uint32_t stackNeeded = max_stk_args + STACK_GRANULARITY * _activation.stackSlotsNeeded();
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
uint32_t stackPushed =
|
2008-10-13 13:29:18 -07:00
|
|
|
STACK_GRANULARITY + // returnaddr
|
2009-08-27 18:46:45 -07:00
|
|
|
STACK_GRANULARITY; // ebp
|
2009-07-10 12:58:34 -07:00
|
|
|
|
|
|
|
uint32_t aligned = alignUp(stackNeeded + stackPushed, NJ_ALIGN_STACK);
|
|
|
|
uint32_t amt = aligned - stackPushed;
|
|
|
|
|
|
|
|
// Reserve stackNeeded bytes, padded
|
|
|
|
// to preserve NJ_ALIGN_STACK-byte alignment.
|
|
|
|
if (amt)
|
2009-07-23 17:02:22 -07:00
|
|
|
{
|
2009-07-10 12:58:34 -07:00
|
|
|
SUBi(SP, amt);
|
2009-07-23 17:02:22 -07:00
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2009-12-08 07:57:17 -08:00
|
|
|
verbose_only( asm_output("[frag entry]"); )
|
2008-11-04 14:20:19 -08:00
|
|
|
NIns *fragEntry = _nIns;
|
2009-07-10 12:58:34 -07:00
|
|
|
MR(FP, SP); // Establish our own FP.
|
2008-10-13 13:29:18 -07:00
|
|
|
PUSHr(FP); // Save caller's FP.
|
2008-07-01 14:46:10 -07:00
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
return fragEntry;
|
|
|
|
}
|
2009-09-21 17:03:07 -07:00
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
void Assembler::nFragExit(LInsp guard)
|
|
|
|
{
|
|
|
|
SideExit *exit = guard->record()->exit;
|
2009-07-15 16:50:01 -07:00
|
|
|
bool trees = config.tree_opt;
|
2008-06-19 10:47:58 -07:00
|
|
|
Fragment *frag = exit->target;
|
|
|
|
GuardRecord *lr = 0;
|
2009-07-10 12:58:34 -07:00
|
|
|
bool destKnown = (frag && frag->fragEntry);
|
2009-08-31 16:35:50 -07:00
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
// Generate jump to epilog and initialize lr.
|
|
|
|
// If the guard is LIR_xtbl, use a jump table with epilog in every entry
|
|
|
|
if (guard->isop(LIR_xtbl)) {
|
|
|
|
lr = guard->record();
|
2009-08-27 18:46:45 -07:00
|
|
|
Register r = EDX;
|
2009-07-10 12:58:34 -07:00
|
|
|
SwitchInfo* si = guard->record()->exit->switchInfo;
|
2009-08-31 16:35:50 -07:00
|
|
|
if (!_epilogue)
|
|
|
|
_epilogue = genEpilogue();
|
2009-07-10 12:58:34 -07:00
|
|
|
emitJumpTable(si, _epilogue);
|
|
|
|
JMP_indirect(r);
|
|
|
|
LEAmi4(r, si->table, r);
|
|
|
|
} else {
|
|
|
|
// If the guard already exists, use a simple jump.
|
|
|
|
if (destKnown && !trees) {
|
|
|
|
JMP(frag->fragEntry);
|
|
|
|
lr = 0;
|
2009-08-31 16:35:50 -07:00
|
|
|
} else { // Target doesn't exist. Jump to an epilogue for now. This can be patched later.
|
|
|
|
if (!_epilogue)
|
|
|
|
_epilogue = genEpilogue();
|
2009-07-10 12:58:34 -07:00
|
|
|
lr = guard->record();
|
|
|
|
JMP_long(_epilogue);
|
|
|
|
lr->jmp = _nIns;
|
|
|
|
}
|
|
|
|
}
|
2009-07-23 17:02:22 -07:00
|
|
|
|
2009-09-15 15:05:53 -07:00
|
|
|
// profiling for the exit
|
|
|
|
verbose_only(
|
|
|
|
if (_logc->lcbits & LC_FragProfile) {
|
|
|
|
INCLi( &guard->record()->profCount );
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2009-08-31 16:35:50 -07:00
|
|
|
// Restore ESP from EBP, undoing SUBi(SP,amt) in the prologue
|
2008-06-19 10:47:58 -07:00
|
|
|
MR(SP,FP);
|
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
// return value is GuardRecord*
|
2009-11-29 22:26:15 -08:00
|
|
|
asm_int(EAX, int(lr), /*canClobberCCs*/true);
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
NIns *Assembler::genEpilogue()
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
|
|
|
RET();
|
2008-07-03 21:39:34 -07:00
|
|
|
POPr(FP); // Restore caller's FP.
|
2009-08-31 16:35:50 -07:00
|
|
|
|
2008-06-19 10:47:58 -07:00
|
|
|
return _nIns;
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
|
|
|
|
void Assembler::asm_call(LInsp ins)
|
|
|
|
{
|
2009-12-01 18:50:07 -08:00
|
|
|
Register retReg = ( ins->isop(LIR_fcall) ? FST0 : retRegs[0] );
|
|
|
|
prepResultReg(ins, rmask(retReg));
|
|
|
|
|
|
|
|
// Do this after we've handled the call result, so we don't
|
|
|
|
// force the call result to be spilled unnecessarily.
|
|
|
|
|
|
|
|
evictScratchRegs();
|
|
|
|
|
2008-10-08 15:08:33 -07:00
|
|
|
const CallInfo* call = ins->callInfo();
|
2009-07-10 12:58:34 -07:00
|
|
|
// must be signed, not unsigned
|
|
|
|
uint32_t iargs = call->count_iargs();
|
2009-06-19 02:20:26 -07:00
|
|
|
int32_t fargs = call->count_args() - iargs;
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2009-08-02 15:54:02 -07:00
|
|
|
bool indirect = call->isIndirect();
|
|
|
|
if (indirect) {
|
|
|
|
// target arg isn't pushed, its consumed in the call
|
|
|
|
iargs --;
|
|
|
|
}
|
|
|
|
|
2009-10-07 14:25:29 -07:00
|
|
|
AbiKind abi = call->_abi;
|
|
|
|
uint32_t max_regs = max_abi_regs[abi];
|
2008-10-13 13:29:18 -07:00
|
|
|
if (max_regs > iargs)
|
|
|
|
max_regs = iargs;
|
|
|
|
|
|
|
|
int32_t istack = iargs-max_regs; // first 2 4B args are in registers
|
2009-10-23 13:46:09 -07:00
|
|
|
int32_t extra = 0;
|
|
|
|
const int32_t pushsize = 4*istack + 8*fargs; // actual stack space used
|
|
|
|
|
|
|
|
#if _MSC_VER
|
|
|
|
// msc only provides 4-byte alignment, anything more than 4 on windows
|
|
|
|
// x86-32 requires dynamic ESP alignment in prolog/epilog and static
|
|
|
|
// esp-alignment here.
|
|
|
|
uint32_t align = 4;//NJ_ALIGN_STACK;
|
|
|
|
#else
|
|
|
|
uint32_t align = NJ_ALIGN_STACK;
|
|
|
|
#endif
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2009-10-23 13:46:09 -07:00
|
|
|
if (pushsize) {
|
|
|
|
if (config.fixed_esp) {
|
|
|
|
// In case of fastcall, stdcall and thiscall the callee cleans up the stack,
|
|
|
|
// and since we reserve max_stk_args words in the prolog to call functions
|
|
|
|
// and don't adjust the stack pointer individually for each call we have
|
|
|
|
// to undo here any changes the callee just did to the stack.
|
|
|
|
if (abi != ABI_CDECL)
|
|
|
|
SUBi(SP, pushsize);
|
|
|
|
} else {
|
|
|
|
// stack re-alignment
|
|
|
|
// only pop our adjustment amount since callee pops args in FASTCALL mode
|
|
|
|
extra = alignUp(pushsize, align) - pushsize;
|
|
|
|
if (call->_abi == ABI_CDECL) {
|
|
|
|
// with CDECL only, caller pops args
|
|
|
|
ADDi(SP, extra+pushsize);
|
|
|
|
} else if (extra > 0) {
|
|
|
|
ADDi(SP, extra);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-07-16 14:21:31 -07:00
|
|
|
|
2009-08-27 17:52:46 -07:00
|
|
|
NanoAssert(ins->isop(LIR_pcall) || ins->isop(LIR_fcall));
|
2009-08-02 15:54:02 -07:00
|
|
|
if (!indirect) {
|
|
|
|
CALL(call);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// indirect call. x86 Calling conventions don't use EAX as an
|
|
|
|
// argument, and do use EAX as a return value. We need a register
|
|
|
|
// for the address to call, so we use EAX since it will always be
|
|
|
|
// available
|
|
|
|
CALLr(call, EAX);
|
|
|
|
}
|
2008-09-02 11:43:55 -07:00
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
// make sure fpu stack is empty before call (restoreCallerSaved)
|
|
|
|
NanoAssert(_allocator.isFree(FST0));
|
|
|
|
// note: this code requires that ref arguments (ARGSIZE_Q)
|
2008-07-16 14:21:31 -07:00
|
|
|
// be one of the first two arguments
|
2009-07-10 12:58:34 -07:00
|
|
|
// pre-assign registers to the first N 4B args based on the calling convention
|
|
|
|
uint32_t n = 0;
|
2008-07-16 14:21:31 -07:00
|
|
|
|
2009-07-29 17:44:34 -07:00
|
|
|
ArgSize sizes[MAXARGS];
|
2008-07-16 14:21:31 -07:00
|
|
|
uint32_t argc = call->get_sizes(sizes);
|
2009-10-07 14:25:29 -07:00
|
|
|
int32_t stkd = 0;
|
2009-12-21 12:05:48 -08:00
|
|
|
|
2009-08-02 15:54:02 -07:00
|
|
|
if (indirect) {
|
|
|
|
argc--;
|
2009-10-07 14:25:29 -07:00
|
|
|
asm_arg(ARGSIZE_P, ins->arg(argc), EAX, stkd);
|
2009-12-21 12:05:48 -08:00
|
|
|
if (!config.fixed_esp)
|
2009-10-23 13:46:09 -07:00
|
|
|
stkd = 0;
|
2009-08-02 15:54:02 -07:00
|
|
|
}
|
2008-07-16 14:21:31 -07:00
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
for(uint32_t i=0; i < argc; i++)
|
|
|
|
{
|
|
|
|
uint32_t j = argc-i-1;
|
2008-07-16 14:21:31 -07:00
|
|
|
ArgSize sz = sizes[j];
|
|
|
|
Register r = UnknownReg;
|
2009-07-10 12:58:34 -07:00
|
|
|
if (n < max_regs && sz != ARGSIZE_F) {
|
|
|
|
r = argRegs[n++]; // tell asm_arg what reg to use
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
2009-10-07 14:25:29 -07:00
|
|
|
asm_arg(sz, ins->arg(j), r, stkd);
|
2009-12-21 12:05:48 -08:00
|
|
|
if (!config.fixed_esp)
|
2009-10-23 13:46:09 -07:00
|
|
|
stkd = 0;
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
|
2009-10-23 13:46:09 -07:00
|
|
|
if (config.fixed_esp) {
|
|
|
|
if (pushsize > max_stk_args)
|
|
|
|
max_stk_args = pushsize;
|
|
|
|
} else if (extra > 0) {
|
|
|
|
SUBi(SP, extra);
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
|
2009-08-27 13:50:30 -07:00
|
|
|
Register Assembler::nRegisterAllocFromSet(RegisterMask set)
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
|
|
|
Register r;
|
|
|
|
RegAlloc ®s = _allocator;
|
2010-01-18 17:34:13 -08:00
|
|
|
#ifdef _MSC_VER
|
2009-07-10 12:58:34 -07:00
|
|
|
_asm
|
|
|
|
{
|
|
|
|
mov ecx, regs
|
|
|
|
bsf eax, set // i = first bit set
|
|
|
|
btr RegAlloc::free[ecx], eax // free &= ~rmask(i)
|
|
|
|
mov r, eax
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
asm(
|
|
|
|
"bsf %1, %%eax\n\t"
|
|
|
|
"btr %%eax, %2\n\t"
|
2009-07-23 17:02:22 -07:00
|
|
|
"movl %%eax, %0\n\t"
|
2009-07-10 12:58:34 -07:00
|
|
|
: "=m"(r) : "m"(set), "m"(regs.free) : "%eax", "memory" );
|
2010-01-18 17:34:13 -08:00
|
|
|
#endif /* _MSC_VER */
|
2009-07-10 12:58:34 -07:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::nRegisterResetAll(RegAlloc& a)
|
|
|
|
{
|
|
|
|
// add scratch registers to our free list for the allocator
|
|
|
|
a.clear();
|
|
|
|
a.free = SavedRegs | ScratchRegs;
|
2008-12-10 17:19:40 -08:00
|
|
|
if (!config.sse2)
|
2008-06-19 10:47:58 -07:00
|
|
|
a.free &= ~XmmRegs;
|
2009-07-10 12:58:34 -07:00
|
|
|
debug_only( a.managed = a.free; )
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2009-08-27 13:47:39 -07:00
|
|
|
void Assembler::nPatchBranch(NIns* branch, NIns* targ)
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
|
|
|
intptr_t offset = intptr_t(targ) - intptr_t(branch);
|
|
|
|
if (branch[0] == JMP32) {
|
|
|
|
*(int32_t*)&branch[1] = offset - 5;
|
|
|
|
} else if (branch[0] == JCC32) {
|
|
|
|
*(int32_t*)&branch[2] = offset - 6;
|
|
|
|
} else
|
|
|
|
NanoAssertMsg(0, "Unknown branch type in nPatchBranch");
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2010-01-24 14:40:11 -08:00
|
|
|
RegisterMask Assembler::hint(LIns* ins)
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
2010-01-24 14:40:11 -08:00
|
|
|
uint32_t op = ins->opcode();
|
|
|
|
int prefer = 0;
|
|
|
|
|
2009-08-27 17:52:46 -07:00
|
|
|
if (op == LIR_icall) {
|
2010-01-24 14:40:11 -08:00
|
|
|
prefer = rmask(retRegs[0]);
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
2009-06-19 02:20:26 -07:00
|
|
|
else if (op == LIR_fcall) {
|
2010-01-24 14:40:11 -08:00
|
|
|
prefer = rmask(FST0);
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
2009-08-27 17:52:46 -07:00
|
|
|
else if (op == LIR_param) {
|
2010-01-24 14:40:11 -08:00
|
|
|
uint8_t arg = ins->paramArg();
|
|
|
|
if (ins->paramKind() == 0) {
|
2009-09-02 21:09:34 -07:00
|
|
|
uint32_t max_regs = max_abi_regs[_thisfrag->lirbuf->abi];
|
2010-01-24 14:40:11 -08:00
|
|
|
if (arg < max_regs)
|
|
|
|
prefer = rmask(argRegs[arg]);
|
2009-09-02 21:09:34 -07:00
|
|
|
} else {
|
2010-01-24 14:40:11 -08:00
|
|
|
if (arg < NumSavedRegs)
|
|
|
|
prefer = rmask(savedRegs[arg]);
|
2009-09-02 21:09:34 -07:00
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
2010-01-24 14:40:11 -08:00
|
|
|
else if (op == LIR_callh || (op == LIR_rsh && ins->oprnd1()->opcode()==LIR_callh)) {
|
|
|
|
prefer = rmask(retRegs[1]);
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
2010-01-24 14:40:11 -08:00
|
|
|
else if (ins->isCmp()) {
|
|
|
|
prefer = AllowableFlagRegs;
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
2010-01-24 14:40:11 -08:00
|
|
|
else if (ins->isconst()) {
|
|
|
|
prefer = ScratchRegs;
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
2010-01-24 14:40:11 -08:00
|
|
|
|
|
|
|
return prefer;
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
void Assembler::asm_qjoin(LIns *ins)
|
|
|
|
{
|
2009-07-10 12:58:34 -07:00
|
|
|
int d = findMemFor(ins);
|
|
|
|
AvmAssert(d);
|
|
|
|
LIns* lo = ins->oprnd1();
|
|
|
|
LIns* hi = ins->oprnd2();
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2009-09-09 18:00:18 -07:00
|
|
|
Register rr = ins->getReg();
|
|
|
|
if (isKnownReg(rr) && (rmask(rr) & FpRegs))
|
2009-12-17 17:39:16 -08:00
|
|
|
evict(ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
if (hi->isconst())
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
|
|
|
STi(FP, d+4, hi->imm32());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Register r = findRegFor(hi, GpRegs);
|
|
|
|
ST(FP, d+4, r);
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
if (lo->isconst())
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
|
|
|
STi(FP, d, lo->imm32());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// okay if r gets recycled.
|
|
|
|
Register r = findRegFor(lo, GpRegs);
|
|
|
|
ST(FP, d, r);
|
|
|
|
}
|
|
|
|
|
2009-07-23 17:02:22 -07:00
|
|
|
freeRsrcOf(ins, false); // if we had a reg in use, emit a ST to flush it to mem
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
|
2009-11-17 16:15:20 -08:00
|
|
|
// WARNING: the code generated by this function must not affect the
|
|
|
|
// condition codes. See asm_cmp().
|
2009-12-17 17:39:16 -08:00
|
|
|
void Assembler::asm_restore(LInsp ins, Register r)
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
2009-12-17 17:39:16 -08:00
|
|
|
NanoAssert(ins->getReg() == r);
|
|
|
|
|
2009-09-13 15:29:29 -07:00
|
|
|
uint32_t arg;
|
|
|
|
uint32_t abi_regcount;
|
2009-12-17 17:39:16 -08:00
|
|
|
if (ins->isop(LIR_alloc)) {
|
|
|
|
// The value of a LIR_alloc instruction is the address of the
|
|
|
|
// stack allocation. We can rematerialize that from the record we
|
|
|
|
// have of where the allocation lies in the stack.
|
|
|
|
NanoAssert(ins->getArIndex()); // must have stack slots allocated
|
|
|
|
LEA(r, disp(ins), FP);
|
|
|
|
|
|
|
|
} else if (ins->isconst()) {
|
|
|
|
asm_int(r, ins->imm32(), /*canClobberCCs*/false);
|
|
|
|
if (!ins->getArIndex()) {
|
|
|
|
ins->markAsClear();
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
2009-12-17 17:39:16 -08:00
|
|
|
|
2010-01-05 15:28:40 -08:00
|
|
|
} else if (ins->isconstq()) {
|
|
|
|
asm_quad(r, ins->imm64(), ins->imm64f(), /*canClobberCCs*/false);
|
|
|
|
if (!ins->getArIndex()) {
|
|
|
|
ins->markAsClear();
|
|
|
|
}
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
} else if (ins->isop(LIR_param) && ins->paramKind() == 0 &&
|
|
|
|
(arg = ins->paramArg()) >= (abi_regcount = max_abi_regs[_thisfrag->lirbuf->abi])) {
|
|
|
|
// Incoming arg is on stack, can restore it from there instead of spilling.
|
|
|
|
|
|
|
|
// Compute position of argument relative to ebp. Higher argument
|
|
|
|
// numbers are at higher positive offsets. The first abi_regcount
|
2009-09-18 13:31:09 -07:00
|
|
|
// arguments are in registers, rest on stack. +8 accomodates the
|
2009-12-17 17:39:16 -08:00
|
|
|
// return address and saved ebp value. Assuming abi_regcount == 0:
|
|
|
|
//
|
2009-09-18 13:31:09 -07:00
|
|
|
// low-addr ebp
|
|
|
|
// [frame...][saved-ebp][return-addr][arg0][arg1]...
|
2009-12-17 17:39:16 -08:00
|
|
|
//
|
2009-09-13 15:29:29 -07:00
|
|
|
int d = (arg - abi_regcount) * sizeof(intptr_t) + 8;
|
|
|
|
LD(r, d, FP);
|
2009-12-17 17:39:16 -08:00
|
|
|
if (!ins->getArIndex()) {
|
|
|
|
ins->markAsClear();
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
int d = findMemFor(ins);
|
|
|
|
if (rmask(r) & GpRegs) {
|
|
|
|
LD(r, d, FP);
|
|
|
|
} else if (rmask(r) & XmmRegs) {
|
|
|
|
SSE_LDQ(r, d, FP);
|
|
|
|
} else {
|
|
|
|
NanoAssert(rmask(r) & x87Regs);
|
|
|
|
FLDQ(d, FP);
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2009-12-01 16:50:03 -08:00
|
|
|
void Assembler::asm_store32(LOpcode op, LIns* value, int dr, LIns* base)
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
2009-12-17 17:39:16 -08:00
|
|
|
if (value->isconst()) {
|
2009-12-17 13:24:39 -08:00
|
|
|
Register rb = getBaseReg(base, dr, GpRegs);
|
2009-05-09 11:38:34 -07:00
|
|
|
int c = value->imm32();
|
2009-12-17 17:39:16 -08:00
|
|
|
switch (op) {
|
2009-12-01 16:50:03 -08:00
|
|
|
case LIR_stb:
|
|
|
|
ST8i(rb, dr, c);
|
|
|
|
break;
|
|
|
|
case LIR_sts:
|
|
|
|
ST16i(rb, dr, c);
|
|
|
|
break;
|
|
|
|
case LIR_sti:
|
|
|
|
STi(rb, dr, c);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NanoAssertMsg(0, "asm_store32 should never receive this LIR opcode");
|
|
|
|
break;
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-12-17 17:39:16 -08:00
|
|
|
// Quirk of x86-32: reg must be a/b/c/d for single-byte stores.
|
2009-12-01 16:50:03 -08:00
|
|
|
const RegisterMask SrcRegs = (op == LIR_stb) ?
|
|
|
|
(1<<EAX | 1<<ECX | 1<<EDX | 1<<EBX) :
|
|
|
|
GpRegs;
|
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
Register ra, rb;
|
2010-01-14 20:07:32 -08:00
|
|
|
if (base->isconst()) {
|
2008-10-13 13:29:18 -07:00
|
|
|
// absolute address
|
2009-12-17 17:39:16 -08:00
|
|
|
rb = UnknownReg;
|
2009-05-09 11:38:34 -07:00
|
|
|
dr += base->imm32();
|
2009-12-01 16:50:03 -08:00
|
|
|
ra = findRegFor(value, SrcRegs);
|
2008-10-13 13:29:18 -07:00
|
|
|
} else {
|
2010-01-14 20:07:32 -08:00
|
|
|
getBaseReg2(SrcRegs, value, ra, GpRegs, base, rb, dr);
|
2009-12-01 16:50:03 -08:00
|
|
|
}
|
2009-12-17 17:39:16 -08:00
|
|
|
switch (op) {
|
2009-12-01 16:50:03 -08:00
|
|
|
case LIR_stb:
|
|
|
|
ST8(rb, dr, ra);
|
|
|
|
break;
|
|
|
|
case LIR_sts:
|
|
|
|
ST16(rb, dr, ra);
|
|
|
|
break;
|
|
|
|
case LIR_sti:
|
|
|
|
ST(rb, dr, ra);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NanoAssertMsg(0, "asm_store32 should never receive this LIR opcode");
|
|
|
|
break;
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
void Assembler::asm_spill(Register rr, int d, bool pop, bool quad)
|
|
|
|
{
|
|
|
|
(void)quad;
|
|
|
|
if (d)
|
|
|
|
{
|
|
|
|
// save to spill location
|
2008-06-19 10:47:58 -07:00
|
|
|
if (rmask(rr) & FpRegs)
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
2008-06-19 10:47:58 -07:00
|
|
|
if (rmask(rr) & XmmRegs) {
|
2008-07-31 13:28:12 -07:00
|
|
|
SSE_STQ(d, FP, rr);
|
2008-06-19 10:47:58 -07:00
|
|
|
} else {
|
2009-07-10 12:58:34 -07:00
|
|
|
FSTQ((pop?1:0), d, FP);
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ST(FP, d, rr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (pop && (rmask(rr) & x87Regs))
|
|
|
|
{
|
|
|
|
// pop the fpu result since it isn't used
|
|
|
|
FSTP(FST0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::asm_load64(LInsp ins)
|
|
|
|
{
|
2010-01-04 19:03:49 -08:00
|
|
|
NanoAssert(!ins->isop(LIR_ldq) && !ins->isop(LIR_ldqc));
|
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
LIns* base = ins->oprnd1();
|
|
|
|
int db = ins->disp();
|
2009-09-09 18:00:18 -07:00
|
|
|
Register rr = ins->getReg();
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2009-09-09 18:00:18 -07:00
|
|
|
if (isKnownReg(rr) && rmask(rr) & XmmRegs)
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
|
|
|
freeRsrcOf(ins, false);
|
2009-12-17 13:24:39 -08:00
|
|
|
Register rb = getBaseReg(base, db, GpRegs);
|
2009-12-01 16:50:03 -08:00
|
|
|
switch (ins->opcode()) {
|
2010-01-04 19:03:49 -08:00
|
|
|
case LIR_ldf:
|
|
|
|
case LIR_ldfc:
|
2009-12-01 16:50:03 -08:00
|
|
|
SSE_LDQ(rr, db, rb);
|
|
|
|
break;
|
|
|
|
case LIR_ld32f:
|
|
|
|
case LIR_ldc32f:
|
|
|
|
SSE_CVTSS2SD(rr, rr);
|
|
|
|
SSE_LDSS(rr, db, rb);
|
2009-12-21 12:05:48 -08:00
|
|
|
SSE_XORPDr(rr,rr);
|
2009-12-01 16:50:03 -08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NanoAssertMsg(0, "asm_load64 should never receive this LIR opcode");
|
|
|
|
break;
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-09-09 18:00:18 -07:00
|
|
|
int dr = disp(ins);
|
2009-07-10 12:58:34 -07:00
|
|
|
Register rb;
|
2009-08-27 13:22:30 -07:00
|
|
|
if (base->isop(LIR_alloc)) {
|
2008-10-13 13:29:18 -07:00
|
|
|
rb = FP;
|
|
|
|
db += findMemFor(base);
|
|
|
|
} else {
|
|
|
|
rb = findRegFor(base, GpRegs);
|
|
|
|
}
|
2009-09-09 18:00:18 -07:00
|
|
|
ins->setReg(UnknownReg);
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2009-12-01 16:50:03 -08:00
|
|
|
switch (ins->opcode()) {
|
2010-01-04 19:03:49 -08:00
|
|
|
case LIR_ldf:
|
|
|
|
case LIR_ldfc:
|
2009-12-01 16:50:03 -08:00
|
|
|
// don't use an fpu reg to simply load & store the value.
|
|
|
|
if (dr)
|
|
|
|
asm_mmq(FP, dr, rb, db);
|
|
|
|
freeRsrcOf(ins, false);
|
|
|
|
if (isKnownReg(rr))
|
|
|
|
{
|
|
|
|
NanoAssert(rmask(rr)&x87Regs);
|
|
|
|
_allocator.retire(rr);
|
|
|
|
FLDQ(db, rb);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case LIR_ld32f:
|
|
|
|
case LIR_ldc32f:
|
|
|
|
freeRsrcOf(ins, false);
|
|
|
|
if (isKnownReg(rr))
|
|
|
|
{
|
|
|
|
NanoAssert(rmask(rr)&x87Regs);
|
|
|
|
_allocator.retire(rr);
|
2009-12-14 10:58:24 -08:00
|
|
|
// Be sure to shadow the value onto our local area if there's space for it,
|
|
|
|
// but don't pop the FP stack, we expect the register to stay valid.
|
|
|
|
if (dr)
|
|
|
|
FSTQ(0,dr, FP);
|
2009-12-01 16:50:03 -08:00
|
|
|
FLD32(db, rb);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-12-14 10:58:24 -08:00
|
|
|
// We need to use fpu to expand 32->64, can't use asm_mmq...
|
2009-12-21 12:05:48 -08:00
|
|
|
// just load-and-store-with-pop.
|
2009-12-01 16:50:03 -08:00
|
|
|
NanoAssert(dr != 0);
|
|
|
|
FSTPQ(dr, FP);
|
|
|
|
FLD32(db, rb);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
NanoAssertMsg(0, "asm_load64 should never receive this LIR opcode");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2009-12-01 16:50:03 -08:00
|
|
|
void Assembler::asm_store64(LOpcode op, LInsp value, int dr, LInsp base)
|
|
|
|
{
|
2010-01-04 19:03:49 -08:00
|
|
|
NanoAssert(op != LIR_stqi);
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
Register rb = getBaseReg(base, dr, GpRegs);
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
if (op == LIR_st32f) {
|
2009-12-01 16:50:03 -08:00
|
|
|
bool pop = value->isUnusedOrHasUnknownReg();
|
2009-12-17 17:39:16 -08:00
|
|
|
Register rv = ( pop
|
|
|
|
? findRegFor(value, config.sse2 ? XmmRegs : FpRegs)
|
|
|
|
: value->getReg() );
|
|
|
|
|
|
|
|
if (rmask(rv) & XmmRegs) {
|
2009-12-01 16:50:03 -08:00
|
|
|
// need a scratch reg
|
2009-12-17 17:39:16 -08:00
|
|
|
Register rt = registerAllocTmp(XmmRegs);
|
2009-12-01 16:50:03 -08:00
|
|
|
|
|
|
|
// cvt to single-precision and store
|
2009-12-17 17:39:16 -08:00
|
|
|
SSE_STSS(dr, rb, rt);
|
|
|
|
SSE_CVTSD2SS(rt, rv);
|
|
|
|
SSE_XORPDr(rt, rt); // zero dest to ensure no dependency stalls
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
} else {
|
2009-12-17 17:39:16 -08:00
|
|
|
FST32(pop?1:0, dr, rb);
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
2009-12-17 17:39:16 -08:00
|
|
|
|
|
|
|
} else if (value->isconstq()) {
|
2009-05-19 20:26:31 -07:00
|
|
|
STi(rb, dr+4, value->imm64_1());
|
|
|
|
STi(rb, dr, value->imm64_0());
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2010-01-04 19:03:49 -08:00
|
|
|
} else if (value->isop(LIR_ldf) || value->isop(LIR_ldfc) || value->isop(LIR_qjoin)) {
|
2009-07-10 12:58:34 -07:00
|
|
|
// value is 64bit struct or int64_t, or maybe a double.
|
2009-12-17 17:39:16 -08:00
|
|
|
// It may be live in an FPU reg. Either way, don't put it in an
|
|
|
|
// FPU reg just to load & store it.
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
// a) If we know it's not a double, this is right.
|
|
|
|
// b) If we guarded that it's a double, this store could be on the
|
|
|
|
// side exit, copying a non-double.
|
|
|
|
// c) Maybe it's a double just being stored. Oh well.
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
if (config.sse2) {
|
2008-06-19 10:47:58 -07:00
|
|
|
Register rv = findRegFor(value, XmmRegs);
|
2008-07-31 13:28:12 -07:00
|
|
|
SSE_STQ(dr, rb, rv);
|
2009-07-10 12:58:34 -07:00
|
|
|
} else {
|
2009-12-17 17:39:16 -08:00
|
|
|
int da = findMemFor(value);
|
|
|
|
asm_mmq(rb, dr, FP, da);
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
2010-01-04 19:03:49 -08:00
|
|
|
NanoAssert(!value->isop(LIR_ldq) && !value->isop(LIR_ldqc));
|
2009-12-17 17:39:16 -08:00
|
|
|
bool pop = value->isUnusedOrHasUnknownReg();
|
|
|
|
Register rv = ( pop
|
|
|
|
? findRegFor(value, config.sse2 ? XmmRegs : FpRegs)
|
|
|
|
: value->getReg() );
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
if (rmask(rv) & XmmRegs) {
|
|
|
|
SSE_STQ(dr, rb, rv);
|
|
|
|
} else {
|
|
|
|
FSTQ(pop?1:0, dr, rb);
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2009-12-21 12:05:48 -08:00
|
|
|
// Copy 64 bits: (rd+dd) <- (rs+ds).
|
2009-12-17 17:39:16 -08:00
|
|
|
//
|
2008-06-19 10:47:58 -07:00
|
|
|
void Assembler::asm_mmq(Register rd, int dd, Register rs, int ds)
|
|
|
|
{
|
2009-12-17 17:39:16 -08:00
|
|
|
// Value is either a 64-bit struct or maybe a float that isn't live in
|
|
|
|
// an FPU reg. Either way, avoid allocating an FPU reg just to load
|
|
|
|
// and store it.
|
|
|
|
if (config.sse2) {
|
2009-11-17 17:27:57 -08:00
|
|
|
Register t = registerAllocTmp(XmmRegs);
|
2008-07-31 13:28:12 -07:00
|
|
|
SSE_STQ(dd, rd, t);
|
|
|
|
SSE_LDQ(t, ds, rs);
|
2009-12-17 17:39:16 -08:00
|
|
|
} else {
|
|
|
|
// We avoid copying via the FP stack because it's slow and likely
|
|
|
|
// to cause spills.
|
2009-11-17 17:27:57 -08:00
|
|
|
Register t = registerAllocTmp(GpRegs & ~(rmask(rd)|rmask(rs)));
|
2008-06-19 10:47:58 -07:00
|
|
|
ST(rd, dd+4, t);
|
|
|
|
LD(t, ds+4, rs);
|
|
|
|
ST(rd, dd, t);
|
|
|
|
LD(t, ds, rs);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-23 22:41:32 -07:00
|
|
|
NIns* Assembler::asm_branch(bool branchOnFalse, LInsp cond, NIns* targ)
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
2008-10-20 10:15:07 -07:00
|
|
|
LOpcode condop = cond->opcode();
|
2009-07-10 12:58:34 -07:00
|
|
|
NanoAssert(cond->isCond());
|
2009-11-17 16:15:20 -08:00
|
|
|
|
|
|
|
// Handle float conditions separately.
|
|
|
|
if (condop >= LIR_feq && condop <= LIR_fge) {
|
|
|
|
return asm_fbranch(branchOnFalse, cond, targ);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (branchOnFalse) {
|
|
|
|
// op == LIR_xf
|
|
|
|
switch (condop) {
|
|
|
|
case LIR_ov: JNO(targ); break;
|
|
|
|
case LIR_eq: JNE(targ); break;
|
|
|
|
case LIR_lt: JNL(targ); break;
|
|
|
|
case LIR_le: JNLE(targ); break;
|
|
|
|
case LIR_gt: JNG(targ); break;
|
|
|
|
case LIR_ge: JNGE(targ); break;
|
|
|
|
case LIR_ult: JNB(targ); break;
|
|
|
|
case LIR_ule: JNBE(targ); break;
|
|
|
|
case LIR_ugt: JNA(targ); break;
|
|
|
|
case LIR_uge: JNAE(targ); break;
|
|
|
|
default: NanoAssert(0); break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// op == LIR_xt
|
|
|
|
switch (condop) {
|
|
|
|
case LIR_ov: JO(targ); break;
|
|
|
|
case LIR_eq: JE(targ); break;
|
|
|
|
case LIR_lt: JL(targ); break;
|
|
|
|
case LIR_le: JLE(targ); break;
|
|
|
|
case LIR_gt: JG(targ); break;
|
|
|
|
case LIR_ge: JGE(targ); break;
|
|
|
|
case LIR_ult: JB(targ); break;
|
|
|
|
case LIR_ule: JBE(targ); break;
|
|
|
|
case LIR_ugt: JA(targ); break;
|
|
|
|
case LIR_uge: JAE(targ); break;
|
|
|
|
default: NanoAssert(0); break;
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
2009-11-17 16:15:20 -08:00
|
|
|
NIns* at = _nIns;
|
2009-07-10 12:58:34 -07:00
|
|
|
asm_cmp(cond);
|
|
|
|
return at;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::asm_switch(LIns* ins, NIns* exit)
|
|
|
|
{
|
|
|
|
LIns* diff = ins->oprnd1();
|
2009-08-27 18:46:45 -07:00
|
|
|
findSpecificRegFor(diff, EDX);
|
2009-07-10 12:58:34 -07:00
|
|
|
JMP(exit);
|
2009-07-23 17:02:22 -07:00
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2009-11-11 11:38:12 -08:00
|
|
|
void Assembler::asm_jtbl(LIns* ins, NIns** table)
|
|
|
|
{
|
|
|
|
Register indexreg = findRegFor(ins->oprnd1(), GpRegs);
|
|
|
|
JMP_indexed(indexreg, 2, table);
|
|
|
|
}
|
|
|
|
|
2009-09-16 17:39:03 -07:00
|
|
|
// This generates a 'test' or 'cmp' instruction for a condition, which
|
|
|
|
// causes the condition codes to be set appropriately. It's used with
|
|
|
|
// conditional branches, conditional moves, and when generating
|
|
|
|
// conditional values. For example:
|
|
|
|
//
|
|
|
|
// LIR: eq1 = eq a, 0
|
|
|
|
// LIR: xf1: xf eq1 -> ...
|
|
|
|
// asm: test edx, edx # generated by this function
|
|
|
|
// asm: je ...
|
|
|
|
//
|
|
|
|
// If this is the only use of eq1, then on entry 'cond' is *not* marked as
|
|
|
|
// used, and we do not allocate a register for it. That's because its
|
|
|
|
// result ends up in the condition codes rather than a normal register.
|
|
|
|
// This doesn't get recorded in the regstate and so the asm code that
|
|
|
|
// consumes the result (eg. a conditional branch like 'je') must follow
|
|
|
|
// shortly after.
|
|
|
|
//
|
|
|
|
// If eq1 is instead used again later, we will also generate code
|
|
|
|
// (eg. in asm_cond()) to compute it into a normal register, something
|
|
|
|
// like this:
|
|
|
|
//
|
|
|
|
// LIR: eq1 = eq a, 0
|
|
|
|
// LIR: test edx, edx
|
|
|
|
// asm: sete ebx
|
2009-12-21 12:05:48 -08:00
|
|
|
// asm: movzx ebx, ebx
|
2009-09-16 17:39:03 -07:00
|
|
|
//
|
|
|
|
// In this case we end up computing the condition twice, but that's ok, as
|
|
|
|
// it's just as short as testing eq1's value in the code generated for the
|
|
|
|
// guard.
|
|
|
|
//
|
2009-11-17 16:15:20 -08:00
|
|
|
// WARNING: Because the condition code update is not recorded in the
|
|
|
|
// regstate, this function cannot generate any code that will affect the
|
|
|
|
// condition codes prior to the generation of the test/cmp, because any
|
|
|
|
// such code will be run after the test/cmp but before the instruction
|
|
|
|
// that consumes the condition code. And because this function calls
|
|
|
|
// findRegFor() before the test/cmp is generated, and findRegFor() calls
|
|
|
|
// asm_restore(), that means that asm_restore() cannot generate code which
|
|
|
|
// affects the condition codes.
|
|
|
|
//
|
2009-07-10 12:58:34 -07:00
|
|
|
void Assembler::asm_cmp(LIns *cond)
|
|
|
|
{
|
|
|
|
LOpcode condop = cond->opcode();
|
|
|
|
|
2009-07-01 19:21:28 -07:00
|
|
|
// LIR_ov recycles the flags set by arithmetic ops
|
2009-07-23 17:02:22 -07:00
|
|
|
if (condop == LIR_ov)
|
2008-10-20 10:15:07 -07:00
|
|
|
return;
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2008-10-20 10:15:07 -07:00
|
|
|
LInsp lhs = cond->oprnd1();
|
2009-07-10 12:58:34 -07:00
|
|
|
LInsp rhs = cond->oprnd2();
|
|
|
|
|
2010-01-24 13:25:04 -08:00
|
|
|
NanoAssert(lhs->isI32() && rhs->isI32());
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
// Ready to issue the compare.
|
|
|
|
if (rhs->isconst()) {
|
2009-07-10 12:58:34 -07:00
|
|
|
int c = rhs->imm32();
|
2009-12-17 17:39:16 -08:00
|
|
|
// findRegFor() can call asm_restore() -- asm_restore() better not
|
|
|
|
// disturb the CCs!
|
2009-12-15 15:16:47 -08:00
|
|
|
Register r = findRegFor(lhs, GpRegs);
|
2009-07-10 12:58:34 -07:00
|
|
|
if (c == 0 && cond->isop(LIR_eq)) {
|
2009-12-15 15:16:47 -08:00
|
|
|
TEST(r, r);
|
|
|
|
} else {
|
2009-07-10 12:58:34 -07:00
|
|
|
CMPi(r, c);
|
|
|
|
}
|
2009-12-17 17:39:16 -08:00
|
|
|
|
|
|
|
} else {
|
2009-09-09 18:00:18 -07:00
|
|
|
Register ra, rb;
|
2010-01-14 20:07:32 -08:00
|
|
|
findRegFor2(GpRegs, lhs, ra, GpRegs, rhs, rb);
|
2009-07-10 12:58:34 -07:00
|
|
|
CMP(ra, rb);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::asm_fcond(LInsp ins)
|
|
|
|
{
|
2009-11-17 16:15:20 -08:00
|
|
|
LOpcode opcode = ins->opcode();
|
2009-12-17 17:39:16 -08:00
|
|
|
Register r = prepareResultReg(ins, AllowableFlagRegs);
|
2009-11-17 16:15:20 -08:00
|
|
|
|
|
|
|
// SETcc only sets low 8 bits, so extend
|
|
|
|
MOVZX8(r,r);
|
|
|
|
|
|
|
|
if (config.sse2) {
|
|
|
|
// LIR_flt and LIR_fgt are handled by the same case because
|
|
|
|
// asm_fcmp() converts LIR_flt(a,b) to LIR_fgt(b,a). Likewise
|
|
|
|
// for LIR_fle/LIR_fge.
|
|
|
|
switch (opcode) {
|
|
|
|
case LIR_feq: SETNP(r); break;
|
|
|
|
case LIR_flt:
|
|
|
|
case LIR_fgt: SETA(r); break;
|
|
|
|
case LIR_fle:
|
|
|
|
case LIR_fge: SETAE(r); break;
|
|
|
|
default: NanoAssert(0); break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
SETNP(r);
|
|
|
|
}
|
2009-12-17 17:39:16 -08:00
|
|
|
|
|
|
|
freeResourcesOf(ins);
|
|
|
|
|
2009-11-17 16:15:20 -08:00
|
|
|
asm_fcmp(ins);
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::asm_cond(LInsp ins)
|
|
|
|
{
|
|
|
|
LOpcode op = ins->opcode();
|
2009-12-17 17:39:16 -08:00
|
|
|
|
|
|
|
Register r = prepareResultReg(ins, AllowableFlagRegs);
|
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
// SETcc only sets low 8 bits, so extend
|
|
|
|
MOVZX8(r,r);
|
2009-11-17 16:15:20 -08:00
|
|
|
switch (op) {
|
|
|
|
case LIR_ov: SETO(r); break;
|
|
|
|
case LIR_eq: SETE(r); break;
|
|
|
|
case LIR_lt: SETL(r); break;
|
|
|
|
case LIR_le: SETLE(r); break;
|
|
|
|
case LIR_gt: SETG(r); break;
|
|
|
|
case LIR_ge: SETGE(r); break;
|
|
|
|
case LIR_ult: SETB(r); break;
|
|
|
|
case LIR_ule: SETBE(r); break;
|
|
|
|
case LIR_ugt: SETA(r); break;
|
|
|
|
case LIR_uge: SETAE(r); break;
|
|
|
|
default: NanoAssert(0); break;
|
|
|
|
}
|
2009-12-17 17:39:16 -08:00
|
|
|
|
2010-01-04 19:03:49 -08:00
|
|
|
freeResourcesOf(ins);
|
2009-12-17 17:39:16 -08:00
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
asm_cmp(ins);
|
|
|
|
}
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
// Two example cases for "ins = add lhs, rhs". '*' lines are those
|
|
|
|
// generated in this function.
|
|
|
|
//
|
|
|
|
// asm: define lhs into rr
|
|
|
|
// asm: define rhs into rb
|
|
|
|
// ...
|
|
|
|
// * asm: add rr, rb
|
|
|
|
// * asm: spill rr if necessary
|
|
|
|
// ... no more uses of lhs in rr...
|
|
|
|
//
|
|
|
|
// asm: define lhs into ra
|
|
|
|
// asm: define rhs into rb
|
|
|
|
// ...
|
|
|
|
// * asm: mov rr, ra
|
|
|
|
// * asm: add rr, rb
|
|
|
|
// * asm: spill rr if necessary
|
|
|
|
// ... some uses of lhs in ra...
|
|
|
|
//
|
2009-07-10 12:58:34 -07:00
|
|
|
void Assembler::asm_arith(LInsp ins)
|
|
|
|
{
|
|
|
|
LOpcode op = ins->opcode();
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
// First special case.
|
2009-07-10 12:58:34 -07:00
|
|
|
if (op == LIR_mod) {
|
2009-09-16 18:10:26 -07:00
|
|
|
asm_div_mod(ins);
|
2009-07-10 12:58:34 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
LInsp lhs = ins->oprnd1();
|
2009-07-10 12:58:34 -07:00
|
|
|
LInsp rhs = ins->oprnd2();
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
// Second special case.
|
|
|
|
if ((op == LIR_add || op == LIR_iaddp) && lhs->isop(LIR_alloc) && rhs->isconst()) {
|
|
|
|
// LIR_add(LIR_alloc, LIR_int) or LIR_addp(LIR_alloc, LIR_int) -- use lea.
|
|
|
|
Register rr = prepareResultReg(ins, GpRegs);
|
|
|
|
int d = findMemFor(lhs) + rhs->imm32();
|
|
|
|
|
|
|
|
LEA(rr, d, FP);
|
|
|
|
|
|
|
|
freeResourcesOf(ins);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isConstRhs;
|
2009-07-10 12:58:34 -07:00
|
|
|
RegisterMask allow = GpRegs;
|
|
|
|
Register rb = UnknownReg;
|
|
|
|
|
|
|
|
switch (op) {
|
|
|
|
case LIR_div:
|
2009-09-16 18:10:26 -07:00
|
|
|
// Nb: if the div feeds into a mod it will be handled by
|
|
|
|
// asm_div_mod() rather than here.
|
2009-12-17 17:39:16 -08:00
|
|
|
isConstRhs = false;
|
|
|
|
rb = findRegFor(rhs, (GpRegs & ~(rmask(EAX)|rmask(EDX))));
|
2009-08-30 22:33:46 -07:00
|
|
|
allow = rmask(EAX);
|
2009-08-30 18:48:21 -07:00
|
|
|
evictIfActive(EDX);
|
2009-07-10 12:58:34 -07:00
|
|
|
break;
|
|
|
|
case LIR_mul:
|
2009-12-17 17:39:16 -08:00
|
|
|
isConstRhs = false;
|
|
|
|
if (lhs != rhs) {
|
|
|
|
rb = findRegFor(rhs, allow);
|
|
|
|
allow &= ~rmask(rb);
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
break;
|
|
|
|
case LIR_lsh:
|
|
|
|
case LIR_rsh:
|
|
|
|
case LIR_ush:
|
2009-12-17 17:39:16 -08:00
|
|
|
isConstRhs = rhs->isconst();
|
|
|
|
if (!isConstRhs) {
|
2009-07-10 12:58:34 -07:00
|
|
|
rb = findSpecificRegFor(rhs, ECX);
|
|
|
|
allow &= ~rmask(rb);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
2009-12-17 17:39:16 -08:00
|
|
|
isConstRhs = rhs->isconst();
|
|
|
|
if (!isConstRhs && lhs != rhs) {
|
|
|
|
rb = findRegFor(rhs, allow);
|
|
|
|
allow &= ~rmask(rb);
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
// Somewhere for the result of 'ins'.
|
|
|
|
Register rr = prepareResultReg(ins, allow);
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
// If 'lhs' isn't in a register, it can be clobbered by 'ins'.
|
|
|
|
Register ra = lhs->isUnusedOrHasUnknownReg() ? rr : lhs->getReg();
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
if (!isConstRhs) {
|
2009-07-10 12:58:34 -07:00
|
|
|
if (lhs == rhs)
|
|
|
|
rb = ra;
|
|
|
|
|
|
|
|
switch (op) {
|
|
|
|
case LIR_add:
|
2009-12-17 17:39:16 -08:00
|
|
|
case LIR_addp: ADD(rr, rb); break;
|
|
|
|
case LIR_sub: SUB(rr, rb); break;
|
|
|
|
case LIR_mul: MUL(rr, rb); break;
|
|
|
|
case LIR_and: AND(rr, rb); break;
|
|
|
|
case LIR_or: OR( rr, rb); break;
|
|
|
|
case LIR_xor: XOR(rr, rb); break;
|
|
|
|
case LIR_lsh: SHL(rr, rb); break;
|
|
|
|
case LIR_rsh: SAR(rr, rb); break;
|
|
|
|
case LIR_ush: SHR(rr, rb); break;
|
2009-07-10 12:58:34 -07:00
|
|
|
case LIR_div:
|
2009-09-16 18:10:26 -07:00
|
|
|
DIV(rb);
|
2009-12-17 17:39:16 -08:00
|
|
|
CDQ(); // sign-extend EAX into EDX:EAX
|
2009-07-10 12:58:34 -07:00
|
|
|
break;
|
2009-12-17 17:39:16 -08:00
|
|
|
default: NanoAssert(0); break;
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
2009-12-17 17:39:16 -08:00
|
|
|
|
|
|
|
} else {
|
2009-07-10 12:58:34 -07:00
|
|
|
int c = rhs->imm32();
|
|
|
|
switch (op) {
|
2009-08-27 17:52:46 -07:00
|
|
|
case LIR_addp:
|
2009-07-10 12:58:34 -07:00
|
|
|
// this doesn't set cc's, only use it when cc's not required.
|
|
|
|
LEA(rr, c, ra);
|
|
|
|
ra = rr; // suppress mov
|
|
|
|
break;
|
2009-12-17 17:39:16 -08:00
|
|
|
case LIR_add: ADDi(rr, c); break;
|
|
|
|
case LIR_sub: SUBi(rr, c); break;
|
|
|
|
case LIR_and: ANDi(rr, c); break;
|
|
|
|
case LIR_or: ORi( rr, c); break;
|
|
|
|
case LIR_xor: XORi(rr, c); break;
|
|
|
|
case LIR_lsh: SHLi(rr, c); break;
|
|
|
|
case LIR_rsh: SARi(rr, c); break;
|
|
|
|
case LIR_ush: SHRi(rr, c); break;
|
|
|
|
default: NanoAssert(0); break;
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
if (rr != ra)
|
|
|
|
MR(rr, ra);
|
|
|
|
|
|
|
|
freeResourcesOf(ins);
|
|
|
|
if (lhs->isUnusedOrHasUnknownReg()) {
|
|
|
|
NanoAssert(ra == rr);
|
|
|
|
findSpecificRegForUnallocated(lhs, ra);
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
// This is called when we have a mod(div(divL, divR)) sequence.
|
2009-09-16 18:10:26 -07:00
|
|
|
void Assembler::asm_div_mod(LInsp mod)
|
|
|
|
{
|
|
|
|
LInsp div = mod->oprnd1();
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
// LIR_mod expects the LIR_div to be near (no interference from the register allocator).
|
2009-09-16 18:10:26 -07:00
|
|
|
NanoAssert(mod->isop(LIR_mod));
|
|
|
|
NanoAssert(div->isop(LIR_div));
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
LInsp divL = div->oprnd1();
|
|
|
|
LInsp divR = div->oprnd2();
|
2009-09-16 18:10:26 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
prepareResultReg(mod, rmask(EDX));
|
|
|
|
prepareResultReg(div, rmask(EAX));
|
2009-09-16 18:10:26 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
Register rDivR = findRegFor(divR, (GpRegs & ~(rmask(EAX)|rmask(EDX))));
|
2009-09-16 18:10:26 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
Register rDivL = divL->isUnusedOrHasUnknownReg() ? EAX : divL->getReg();
|
2009-09-16 18:10:26 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
DIV(rDivR);
|
2009-09-16 18:10:26 -07:00
|
|
|
CDQ(); // sign-extend EAX into EDX:EAX
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
if (EAX != rDivL)
|
|
|
|
MR(EAX, rDivL);
|
|
|
|
|
|
|
|
freeResourcesOf(mod);
|
|
|
|
freeResourcesOf(div);
|
|
|
|
if (divL->isUnusedOrHasUnknownReg()) {
|
|
|
|
NanoAssert(rDivL == EAX);
|
|
|
|
findSpecificRegForUnallocated(divL, EAX);
|
|
|
|
}
|
2009-09-16 18:10:26 -07:00
|
|
|
}
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
// Two example cases for "ins = neg lhs". Lines marked with '*' are
|
|
|
|
// generated in this function.
|
|
|
|
//
|
|
|
|
// asm: define lhs into rr
|
|
|
|
// ...
|
|
|
|
// * asm: neg rr
|
|
|
|
// * asm: spill rr if necessary
|
|
|
|
// ... no more uses of lhs in rr...
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// asm: define lhs into ra
|
|
|
|
// ...
|
|
|
|
// * asm: mov rr, ra
|
|
|
|
// * asm: neg rr
|
|
|
|
// * asm: spill rr if necessary
|
|
|
|
// ... more uses of lhs in ra...
|
|
|
|
//
|
2009-07-10 12:58:34 -07:00
|
|
|
void Assembler::asm_neg_not(LInsp ins)
|
|
|
|
{
|
|
|
|
LOpcode op = ins->opcode();
|
|
|
|
LIns* lhs = ins->oprnd1();
|
2009-12-17 17:39:16 -08:00
|
|
|
|
|
|
|
Register rr = prepareResultReg(ins, GpRegs);
|
|
|
|
|
|
|
|
// If 'lhs' isn't in a register, it can be clobbered by 'ins'.
|
|
|
|
Register ra = lhs->isUnusedOrHasUnknownReg() ? rr : lhs->getReg();
|
2009-07-10 12:58:34 -07:00
|
|
|
|
|
|
|
if (op == LIR_not)
|
|
|
|
NOT(rr);
|
|
|
|
else
|
|
|
|
NEG(rr);
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
if (rr != ra)
|
|
|
|
MR(rr, ra);
|
|
|
|
|
|
|
|
freeResourcesOf(ins);
|
|
|
|
if (lhs->isUnusedOrHasUnknownReg()) {
|
|
|
|
NanoAssert(ra == rr);
|
|
|
|
findSpecificRegForUnallocated(lhs, ra);
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
|
2009-12-01 16:50:03 -08:00
|
|
|
void Assembler::asm_load32(LInsp ins)
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
|
|
|
LOpcode op = ins->opcode();
|
|
|
|
LIns* base = ins->oprnd1();
|
|
|
|
int32_t d = ins->disp();
|
|
|
|
Register rr = prepResultReg(ins, GpRegs);
|
|
|
|
|
|
|
|
if (base->isconst()) {
|
|
|
|
intptr_t addr = base->imm32();
|
|
|
|
addr += d;
|
2009-12-01 16:50:03 -08:00
|
|
|
switch(op) {
|
|
|
|
case LIR_ldzb:
|
|
|
|
case LIR_ldcb:
|
|
|
|
LD8Zdm(rr, addr);
|
|
|
|
return;
|
|
|
|
case LIR_ldsb:
|
|
|
|
case LIR_ldcsb:
|
|
|
|
LD8Sdm(rr, addr);
|
|
|
|
return;
|
|
|
|
case LIR_ldzs:
|
|
|
|
case LIR_ldcs:
|
|
|
|
LD16Zdm(rr, addr);
|
|
|
|
return;
|
|
|
|
case LIR_ldss:
|
|
|
|
case LIR_ldcss:
|
|
|
|
LD16Sdm(rr, addr);
|
|
|
|
return;
|
|
|
|
case LIR_ld:
|
|
|
|
case LIR_ldc:
|
|
|
|
LDdm(rr, addr);
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode");
|
|
|
|
return;
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Search for add(X,Y) */
|
|
|
|
if (base->opcode() == LIR_piadd) {
|
|
|
|
int scale = 0;
|
|
|
|
LIns *lhs = base->oprnd1();
|
|
|
|
LIns *rhs = base->oprnd2();
|
|
|
|
|
|
|
|
/* See if we can bypass any SHLs, by searching for
|
|
|
|
* add(X, shl(Y,Z)) -> mov r, [X+Y*Z]
|
|
|
|
*/
|
|
|
|
if (rhs->opcode() == LIR_pilsh && rhs->oprnd2()->isconst()) {
|
|
|
|
scale = rhs->oprnd2()->imm32();
|
|
|
|
if (scale >= 1 && scale <= 3)
|
|
|
|
rhs = rhs->oprnd1();
|
|
|
|
else
|
|
|
|
scale = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Does LHS have a register yet? If not, re-use the result reg.
|
|
|
|
* @todo -- If LHS is const, we could eliminate a register use.
|
|
|
|
*/
|
2009-09-09 18:00:18 -07:00
|
|
|
Register rleft = ( lhs->isUnusedOrHasUnknownReg()
|
2009-11-03 19:45:29 -08:00
|
|
|
? findSpecificRegForUnallocated(lhs, rr)
|
2009-09-09 18:00:18 -07:00
|
|
|
: lhs->getReg() );
|
2009-07-10 12:58:34 -07:00
|
|
|
|
|
|
|
/* Does RHS have a register yet? If not, try to re-use the result reg. */
|
2009-09-09 18:00:18 -07:00
|
|
|
Register rright = ( rr != rleft && rhs->isUnusedOrHasUnknownReg()
|
2009-11-03 19:45:29 -08:00
|
|
|
? findSpecificRegForUnallocated(rhs, rr)
|
2009-09-09 18:00:18 -07:00
|
|
|
: findRegFor(rhs, GpRegs & ~(rmask(rleft))) );
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2009-12-01 16:50:03 -08:00
|
|
|
switch(op) {
|
|
|
|
case LIR_ldzb:
|
|
|
|
case LIR_ldcb:
|
|
|
|
LD8Zsib(rr, d, rleft, rright, scale);
|
|
|
|
return;
|
|
|
|
case LIR_ldsb:
|
|
|
|
case LIR_ldcsb:
|
|
|
|
LD8Ssib(rr, d, rleft, rright, scale);
|
|
|
|
return;
|
|
|
|
case LIR_ldzs:
|
|
|
|
case LIR_ldcs:
|
|
|
|
LD16Zsib(rr, d, rleft, rright, scale);
|
|
|
|
return;
|
|
|
|
case LIR_ldss:
|
|
|
|
case LIR_ldcss:
|
|
|
|
LD16Ssib(rr, d, rleft, rright, scale);
|
|
|
|
return;
|
|
|
|
case LIR_ld:
|
|
|
|
case LIR_ldc:
|
|
|
|
LDsib(rr, d, rleft, rright, scale);
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode");
|
|
|
|
return;
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
|
2009-12-17 13:24:39 -08:00
|
|
|
Register ra = getBaseReg(base, d, GpRegs);
|
2009-12-01 16:50:03 -08:00
|
|
|
switch(op) {
|
|
|
|
case LIR_ldzb:
|
|
|
|
case LIR_ldcb:
|
|
|
|
LD8Z(rr, d, ra);
|
|
|
|
return;
|
|
|
|
case LIR_ldsb:
|
|
|
|
case LIR_ldcsb:
|
|
|
|
LD8S(rr, d, ra);
|
|
|
|
return;
|
|
|
|
case LIR_ldzs:
|
|
|
|
case LIR_ldcs:
|
|
|
|
LD16Z(rr, d, ra);
|
|
|
|
return;
|
|
|
|
case LIR_ldss:
|
|
|
|
case LIR_ldcss:
|
|
|
|
LD16S(rr, d, ra);
|
|
|
|
return;
|
|
|
|
case LIR_ld:
|
|
|
|
case LIR_ldc:
|
|
|
|
LD(rr, d, ra);
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode");
|
|
|
|
return;
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::asm_cmov(LInsp ins)
|
|
|
|
{
|
|
|
|
LOpcode op = ins->opcode();
|
|
|
|
LIns* condval = ins->oprnd1();
|
2009-07-21 17:28:25 -07:00
|
|
|
LIns* iftrue = ins->oprnd2();
|
|
|
|
LIns* iffalse = ins->oprnd3();
|
2009-07-20 14:18:17 -07:00
|
|
|
|
2009-07-21 17:28:25 -07:00
|
|
|
NanoAssert(condval->isCmp());
|
2010-01-24 13:25:04 -08:00
|
|
|
NanoAssert(op == LIR_cmov && iftrue->isI32() && iffalse->isI32());
|
2009-07-10 12:58:34 -07:00
|
|
|
|
|
|
|
const Register rr = prepResultReg(ins, GpRegs);
|
|
|
|
|
|
|
|
// this code assumes that neither LD nor MR nor MRcc set any of the condition flags.
|
|
|
|
// (This is true on Intel, is it true on all architectures?)
|
|
|
|
const Register iffalsereg = findRegFor(iffalse, GpRegs & ~rmask(rr));
|
|
|
|
if (op == LIR_cmov) {
|
|
|
|
switch (condval->opcode())
|
|
|
|
{
|
|
|
|
// note that these are all opposites...
|
2009-07-23 17:02:22 -07:00
|
|
|
case LIR_eq: MRNE(rr, iffalsereg); break;
|
2009-07-10 12:58:34 -07:00
|
|
|
case LIR_ov: MRNO(rr, iffalsereg); break;
|
2009-07-23 17:02:22 -07:00
|
|
|
case LIR_lt: MRGE(rr, iffalsereg); break;
|
2009-07-10 12:58:34 -07:00
|
|
|
case LIR_le: MRG(rr, iffalsereg); break;
|
2009-07-23 17:02:22 -07:00
|
|
|
case LIR_gt: MRLE(rr, iffalsereg); break;
|
2009-07-10 12:58:34 -07:00
|
|
|
case LIR_ge: MRL(rr, iffalsereg); break;
|
2009-07-23 17:02:22 -07:00
|
|
|
case LIR_ult: MRAE(rr, iffalsereg); break;
|
|
|
|
case LIR_ule: MRA(rr, iffalsereg); break;
|
|
|
|
case LIR_ugt: MRBE(rr, iffalsereg); break;
|
|
|
|
case LIR_uge: MRB(rr, iffalsereg); break;
|
2009-07-26 17:34:44 -07:00
|
|
|
default: NanoAssert(0); break;
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
} else if (op == LIR_qcmov) {
|
|
|
|
NanoAssert(0);
|
|
|
|
}
|
|
|
|
/*const Register iftruereg =*/ findSpecificRegFor(iftrue, rr);
|
|
|
|
asm_cmp(condval);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::asm_qhi(LInsp ins)
|
|
|
|
{
|
|
|
|
Register rr = prepResultReg(ins, GpRegs);
|
|
|
|
LIns *q = ins->oprnd1();
|
2010-01-05 15:28:40 -08:00
|
|
|
if (q->isconstq())
|
|
|
|
{
|
|
|
|
// This should only be possible if ExprFilter isn't in use,
|
|
|
|
// as it will fold qhi(qconst()) properly... still, if it's
|
|
|
|
// disabled, we need this for proper behavior
|
|
|
|
LDi(rr, q->imm64_1());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int d = findMemFor(q);
|
|
|
|
LD(rr, d+4, FP);
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::asm_param(LInsp ins)
|
|
|
|
{
|
|
|
|
uint32_t a = ins->paramArg();
|
|
|
|
uint32_t kind = ins->paramKind();
|
|
|
|
if (kind == 0) {
|
|
|
|
// ordinary param
|
|
|
|
AbiKind abi = _thisfrag->lirbuf->abi;
|
|
|
|
uint32_t abi_regcount = max_abi_regs[abi];
|
|
|
|
if (a < abi_regcount) {
|
2009-12-17 17:39:16 -08:00
|
|
|
// Incoming arg in register.
|
2009-07-10 12:58:34 -07:00
|
|
|
prepResultReg(ins, rmask(argRegs[a]));
|
|
|
|
} else {
|
2009-12-17 17:39:16 -08:00
|
|
|
// Incoming arg is on stack, and EBP points nearby (see genPrologue()).
|
2009-07-10 12:58:34 -07:00
|
|
|
Register r = prepResultReg(ins, GpRegs);
|
|
|
|
int d = (a - abi_regcount) * sizeof(intptr_t) + 8;
|
|
|
|
LD(r, d, FP);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// saved param
|
|
|
|
prepResultReg(ins, rmask(savedRegs[a]));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::asm_int(LInsp ins)
|
|
|
|
{
|
2009-12-17 17:39:16 -08:00
|
|
|
Register rr = prepareResultReg(ins, GpRegs);
|
|
|
|
|
2009-11-29 22:26:15 -08:00
|
|
|
asm_int(rr, ins->imm32(), /*canClobberCCs*/true);
|
2009-12-17 17:39:16 -08:00
|
|
|
|
|
|
|
freeResourcesOf(ins);
|
2009-11-29 22:26:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::asm_int(Register r, int32_t val, bool canClobberCCs)
|
|
|
|
{
|
|
|
|
if (val == 0 && canClobberCCs)
|
|
|
|
XOR(r, r);
|
2009-07-10 12:58:34 -07:00
|
|
|
else
|
2009-11-29 22:26:15 -08:00
|
|
|
LDi(r, val);
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
|
2010-01-05 15:28:40 -08:00
|
|
|
void Assembler::asm_quad(Register r, uint64_t q, double d, bool canClobberCCs)
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
2010-01-05 15:28:40 -08:00
|
|
|
// Quads require non-standard handling. There is no load-64-bit-immediate
|
|
|
|
// instruction on i386, so in the general case, we must load it from memory.
|
2010-01-07 10:05:33 -08:00
|
|
|
// This is unlike most other LIR operations which can be computed directly
|
2010-01-05 15:28:40 -08:00
|
|
|
// in a register. We can special-case 0.0 and various other small ints
|
|
|
|
// (1.0 on x87, any int32_t value on SSE2), but for all other values, we
|
|
|
|
// allocate an 8-byte chunk via dataAlloc and load from there. Note that
|
|
|
|
// this implies that quads never require spill area, since they will always
|
|
|
|
// be rematerialized from const data (or inline instructions in the special cases).
|
2009-12-17 17:39:16 -08:00
|
|
|
|
2010-01-05 15:28:40 -08:00
|
|
|
if (rmask(r) & XmmRegs) {
|
|
|
|
if (q == 0) {
|
|
|
|
// test (int64)0 since -0.0 == 0.0
|
|
|
|
SSE_XORPDr(r, r);
|
|
|
|
} else if (d && d == (int)d && canClobberCCs) {
|
|
|
|
// can fit in 32bits? then use cvt which is faster
|
|
|
|
Register tr = registerAllocTmp(GpRegs);
|
|
|
|
SSE_CVTSI2SD(r, tr);
|
|
|
|
SSE_XORPDr(r, r); // zero r to ensure no dependency stalls
|
|
|
|
asm_int(tr, (int)d, canClobberCCs);
|
2009-07-10 12:58:34 -07:00
|
|
|
} else {
|
2010-01-05 15:28:40 -08:00
|
|
|
const uint64_t* p = findQuadConstant(q);
|
|
|
|
LDSDm(r, (const double*)p);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
NanoAssert(r == FST0);
|
|
|
|
if (q == 0) {
|
|
|
|
// test (int64)0 since -0.0 == 0.0
|
|
|
|
FLDZ();
|
|
|
|
} else if (d == 1.0) {
|
|
|
|
FLD1();
|
|
|
|
} else {
|
|
|
|
const uint64_t* p = findQuadConstant(q);
|
|
|
|
FLDQdm((const double*)p);
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
}
|
2010-01-05 15:28:40 -08:00
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2010-01-05 15:28:40 -08:00
|
|
|
void Assembler::asm_quad(LInsp ins)
|
|
|
|
{
|
|
|
|
Register rr = ins->getReg();
|
|
|
|
|
|
|
|
if (isKnownReg(rr)) {
|
|
|
|
NanoAssert((rmask(rr) & FpRegs) != 0);
|
|
|
|
asm_quad(rr, ins->imm64(), ins->imm64f(), /*canClobberCCs*/true);
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
2009-12-17 17:39:16 -08:00
|
|
|
|
|
|
|
freeResourcesOf(ins);
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::asm_qlo(LInsp ins)
|
|
|
|
{
|
|
|
|
LIns *q = ins->oprnd1();
|
|
|
|
|
|
|
|
if (!config.sse2)
|
|
|
|
{
|
|
|
|
Register rr = prepResultReg(ins, GpRegs);
|
2010-01-05 15:28:40 -08:00
|
|
|
if (q->isconstq())
|
|
|
|
{
|
|
|
|
// This should only be possible if ExprFilter isn't in use,
|
|
|
|
// as it will fold qlo(qconst()) properly... still, if it's
|
|
|
|
// disabled, we need this for proper behavior
|
|
|
|
LDi(rr, q->imm64_0());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int d = findMemFor(q);
|
|
|
|
LD(rr, d, FP);
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-09-09 18:00:18 -07:00
|
|
|
Register rr = ins->getReg();
|
|
|
|
if (!isKnownReg(rr)) {
|
2009-07-10 12:58:34 -07:00
|
|
|
// store quad in spill loc
|
2009-09-09 18:00:18 -07:00
|
|
|
int d = disp(ins);
|
2009-07-10 12:58:34 -07:00
|
|
|
freeRsrcOf(ins, false);
|
|
|
|
Register qr = findRegFor(q, XmmRegs);
|
|
|
|
SSE_MOVDm(d, FP, qr);
|
|
|
|
} else {
|
|
|
|
freeRsrcOf(ins, false);
|
|
|
|
Register qr = findRegFor(q, XmmRegs);
|
|
|
|
SSE_MOVD(rr,qr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-21 12:05:48 -08:00
|
|
|
// negateMask is used by asm_fneg.
|
2009-09-29 18:25:39 -07:00
|
|
|
#if defined __SUNPRO_CC
|
|
|
|
// From Sun Studio C++ Readme: #pragma align inside namespace requires mangled names.
|
|
|
|
// Initialize here to avoid multithreading contention issues during initialization.
|
|
|
|
static uint32_t negateMask_temp[] = {0, 0, 0, 0, 0, 0, 0};
|
|
|
|
|
|
|
|
static uint32_t* negateMaskInit()
|
|
|
|
{
|
|
|
|
uint32_t* negateMask = (uint32_t*)alignUp(negateMask_temp, 16);
|
|
|
|
negateMask[1] = 0x80000000;
|
|
|
|
return negateMask;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t *negateMask = negateMaskInit();
|
|
|
|
#else
|
|
|
|
static const AVMPLUS_ALIGN16(uint32_t) negateMask[] = {0,0x80000000,0,0};
|
|
|
|
#endif
|
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
void Assembler::asm_fneg(LInsp ins)
|
|
|
|
{
|
2009-12-17 17:39:16 -08:00
|
|
|
LIns *lhs = ins->oprnd1();
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
if (config.sse2) {
|
|
|
|
Register rr = prepareResultReg(ins, XmmRegs);
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
// If 'lhs' isn't in a register, it can be clobbered by 'ins'.
|
|
|
|
Register ra;
|
2009-09-09 18:00:18 -07:00
|
|
|
if (lhs->isUnusedOrHasUnknownReg()) {
|
2009-12-17 17:39:16 -08:00
|
|
|
ra = rr;
|
|
|
|
} else if (!(rmask(lhs->getReg()) & XmmRegs)) {
|
|
|
|
// We need to evict lhs from x87Regs, which then puts us in
|
|
|
|
// the same situation as the isUnusedOrHasUnknownReg() case.
|
|
|
|
evict(lhs);
|
|
|
|
ra = rr;
|
2009-09-09 18:00:18 -07:00
|
|
|
} else {
|
|
|
|
ra = lhs->getReg();
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
2008-07-01 14:46:10 -07:00
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
SSE_XORPD(rr, negateMask);
|
2008-07-01 14:46:10 -07:00
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
if (rr != ra)
|
|
|
|
SSE_MOVSD(rr, ra);
|
2008-07-01 14:46:10 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
freeResourcesOf(ins);
|
|
|
|
if (lhs->isUnusedOrHasUnknownReg()) {
|
|
|
|
NanoAssert(ra == rr);
|
|
|
|
findSpecificRegForUnallocated(lhs, ra);
|
|
|
|
}
|
2008-07-01 14:46:10 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
} else {
|
|
|
|
verbose_only( Register rr = ) prepareResultReg(ins, x87Regs);
|
|
|
|
NanoAssert(FST0 == rr);
|
2009-09-09 18:00:18 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
NanoAssert(lhs->isUnusedOrHasUnknownReg() || FST0 == lhs->getReg());
|
2008-07-01 14:46:10 -07:00
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
FCHS();
|
2008-07-01 14:46:10 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
freeResourcesOf(ins);
|
|
|
|
if (lhs->isUnusedOrHasUnknownReg())
|
|
|
|
findSpecificRegForUnallocated(lhs, FST0);
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
}
|
2008-07-01 14:46:10 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
void Assembler::asm_arg(ArgSize sz, LInsp ins, Register r, int32_t& stkd)
|
2008-10-13 13:29:18 -07:00
|
|
|
{
|
2009-12-17 17:39:16 -08:00
|
|
|
// If 'r' is known, then that's the register we have to put 'ins'
|
|
|
|
// into.
|
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
if (sz == ARGSIZE_Q)
|
2008-10-13 13:29:18 -07:00
|
|
|
{
|
2009-07-10 12:58:34 -07:00
|
|
|
// ref arg - use lea
|
2009-09-09 18:00:18 -07:00
|
|
|
if (isKnownReg(r))
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
2009-12-17 17:39:16 -08:00
|
|
|
NanoAssert(rmask(r) & FpRegs);
|
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
// arg in specific reg
|
2010-01-05 15:28:40 -08:00
|
|
|
if (ins->isconstq())
|
|
|
|
{
|
|
|
|
const uint64_t* p = findQuadConstant(ins->imm64());
|
|
|
|
LDi(r, uint32_t(p));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int da = findMemFor(ins);
|
2009-12-17 17:39:16 -08:00
|
|
|
|
2010-01-05 15:28:40 -08:00
|
|
|
LEA(r, da, FP);
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
NanoAssert(0); // not supported
|
|
|
|
}
|
|
|
|
}
|
2009-07-29 17:44:34 -07:00
|
|
|
else if (sz == ARGSIZE_I || sz == ARGSIZE_U)
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
2009-09-09 18:00:18 -07:00
|
|
|
if (isKnownReg(r)) {
|
2009-12-17 17:39:16 -08:00
|
|
|
if (ins->isconst()) {
|
|
|
|
// Rematerialize the constant.
|
|
|
|
asm_int(r, ins->imm32(), /*canClobberCCs*/true);
|
|
|
|
} else if (ins->isUsed()) {
|
|
|
|
if (!ins->hasKnownReg()) {
|
|
|
|
int d = disp(ins);
|
|
|
|
NanoAssert(d != 0);
|
|
|
|
if (ins->isop(LIR_alloc)) {
|
|
|
|
LEA(r, d, FP);
|
2008-10-13 13:29:18 -07:00
|
|
|
} else {
|
2009-12-17 17:39:16 -08:00
|
|
|
LD(r, d, FP);
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
2009-12-17 17:39:16 -08:00
|
|
|
} else {
|
|
|
|
if (r != ins->getReg())
|
|
|
|
MR(r, ins->getReg());
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
2009-12-17 17:39:16 -08:00
|
|
|
|
|
|
|
} else {
|
|
|
|
// This is the last use, so fine to assign it
|
|
|
|
// to the scratch reg, it's dead after this point.
|
|
|
|
findSpecificRegForUnallocated(ins, r);
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
else {
|
2009-10-23 13:46:09 -07:00
|
|
|
if (config.fixed_esp)
|
2009-12-17 17:39:16 -08:00
|
|
|
asm_stkarg(ins, stkd);
|
2009-10-23 13:46:09 -07:00
|
|
|
else
|
2009-12-17 17:39:16 -08:00
|
|
|
asm_pusharg(ins);
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
else
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
NanoAssert(sz == ARGSIZE_F);
|
2009-12-17 17:39:16 -08:00
|
|
|
asm_farg(ins, stkd);
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
void Assembler::asm_pusharg(LInsp ins)
|
2009-10-23 13:46:09 -07:00
|
|
|
{
|
|
|
|
// arg goes on stack
|
2009-12-17 17:39:16 -08:00
|
|
|
if (!ins->isUsed() && ins->isconst())
|
2009-10-23 13:46:09 -07:00
|
|
|
{
|
2009-12-17 17:39:16 -08:00
|
|
|
PUSHi(ins->imm32()); // small const we push directly
|
2009-10-23 13:46:09 -07:00
|
|
|
}
|
2009-12-17 17:39:16 -08:00
|
|
|
else if (!ins->isUsed() || ins->isop(LIR_alloc))
|
2009-10-23 13:46:09 -07:00
|
|
|
{
|
2009-12-17 17:39:16 -08:00
|
|
|
Register ra = findRegFor(ins, GpRegs);
|
2009-10-23 13:46:09 -07:00
|
|
|
PUSHr(ra);
|
|
|
|
}
|
2009-12-17 17:39:16 -08:00
|
|
|
else if (!ins->hasKnownReg())
|
2009-10-23 13:46:09 -07:00
|
|
|
{
|
2009-12-17 17:39:16 -08:00
|
|
|
PUSHm(disp(ins), FP);
|
2009-10-23 13:46:09 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2009-12-17 17:39:16 -08:00
|
|
|
PUSHr(ins->getReg());
|
2009-10-23 13:46:09 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
void Assembler::asm_stkarg(LInsp ins, int32_t& stkd)
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
|
|
|
// arg goes on stack
|
2009-12-17 17:39:16 -08:00
|
|
|
if (!ins->isUsed() && ins->isconst())
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
|
|
|
// small const we push directly
|
2009-12-17 17:39:16 -08:00
|
|
|
STi(SP, stkd, ins->imm32());
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
2009-10-07 14:25:29 -07:00
|
|
|
else {
|
|
|
|
Register ra;
|
2009-12-17 17:39:16 -08:00
|
|
|
if (ins->isUnusedOrHasUnknownReg() || ins->isop(LIR_alloc))
|
|
|
|
ra = findRegFor(ins, GpRegs & (~SavedRegs));
|
2009-10-07 14:25:29 -07:00
|
|
|
else
|
2009-12-17 17:39:16 -08:00
|
|
|
ra = ins->getReg();
|
2009-10-07 14:25:29 -07:00
|
|
|
ST(SP, stkd, ra);
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
2009-10-07 14:25:29 -07:00
|
|
|
|
|
|
|
stkd += sizeof(int32_t);
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
void Assembler::asm_farg(LInsp ins, int32_t& stkd)
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
2010-01-24 13:25:04 -08:00
|
|
|
NanoAssert(ins->isF64());
|
2009-12-17 17:39:16 -08:00
|
|
|
Register r = findRegFor(ins, FpRegs);
|
2009-07-10 12:58:34 -07:00
|
|
|
if (rmask(r) & XmmRegs) {
|
2009-10-07 14:25:29 -07:00
|
|
|
SSE_STQ(stkd, SP, r);
|
2009-07-10 12:58:34 -07:00
|
|
|
} else {
|
2009-10-07 14:25:29 -07:00
|
|
|
FSTPQ(stkd, SP);
|
2009-12-21 12:05:48 -08:00
|
|
|
|
2009-10-29 12:29:28 -07:00
|
|
|
//
|
|
|
|
// 22Jul09 rickr - Enabling the evict causes a 10% slowdown on primes
|
|
|
|
//
|
|
|
|
// evict() triggers a very expensive fstpq/fldq pair around the store.
|
|
|
|
// We need to resolve the bug some other way.
|
|
|
|
//
|
|
|
|
// see https://bugzilla.mozilla.org/show_bug.cgi?id=491084
|
2009-12-21 12:05:48 -08:00
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
/* It's possible that the same LIns* with r=FST0 will appear in the argument list more
|
|
|
|
* than once. In this case FST0 will not have been evicted and the multiple pop
|
|
|
|
* actions will unbalance the FPU stack. A quick fix is to always evict FST0 manually.
|
|
|
|
*/
|
2009-08-30 18:48:21 -07:00
|
|
|
evictIfActive(FST0);
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
2009-10-23 13:46:09 -07:00
|
|
|
if (!config.fixed_esp)
|
2009-12-17 17:39:16 -08:00
|
|
|
SUBi(ESP, 8);
|
2009-10-07 14:25:29 -07:00
|
|
|
|
|
|
|
stkd += sizeof(double);
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::asm_fop(LInsp ins)
|
|
|
|
{
|
|
|
|
LOpcode op = ins->opcode();
|
|
|
|
if (config.sse2)
|
|
|
|
{
|
|
|
|
LIns *lhs = ins->oprnd1();
|
|
|
|
LIns *rhs = ins->oprnd2();
|
|
|
|
|
|
|
|
RegisterMask allow = XmmRegs;
|
|
|
|
Register rb = UnknownReg;
|
|
|
|
if (lhs != rhs) {
|
|
|
|
rb = findRegFor(rhs,allow);
|
|
|
|
allow &= ~rmask(rb);
|
|
|
|
}
|
|
|
|
|
|
|
|
Register rr = prepResultReg(ins, allow);
|
|
|
|
Register ra;
|
|
|
|
|
|
|
|
// if this is last use of lhs in reg, we can re-use result reg
|
2009-09-09 18:00:18 -07:00
|
|
|
if (lhs->isUnusedOrHasUnknownReg()) {
|
2009-11-03 19:45:29 -08:00
|
|
|
ra = findSpecificRegForUnallocated(lhs, rr);
|
2009-09-09 18:00:18 -07:00
|
|
|
} else if ((rmask(lhs->getReg()) & XmmRegs) == 0) {
|
2009-09-18 13:31:09 -07:00
|
|
|
// We need this case on AMD64, because it's possible that
|
|
|
|
// an earlier instruction has done a quadword load and reserved a
|
|
|
|
// GPR. If so, ask for a new register.
|
2009-07-10 12:58:34 -07:00
|
|
|
ra = findRegFor(lhs, XmmRegs);
|
2009-09-09 18:00:18 -07:00
|
|
|
} else {
|
|
|
|
// lhs already has a register assigned but maybe not from the allow set
|
2008-10-13 13:29:18 -07:00
|
|
|
ra = findRegFor(lhs, allow);
|
|
|
|
}
|
2008-07-01 14:46:10 -07:00
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
if (lhs == rhs)
|
|
|
|
rb = ra;
|
|
|
|
|
|
|
|
if (op == LIR_fadd)
|
|
|
|
SSE_ADDSD(rr, rb);
|
|
|
|
else if (op == LIR_fsub)
|
|
|
|
SSE_SUBSD(rr, rb);
|
|
|
|
else if (op == LIR_fmul)
|
|
|
|
SSE_MULSD(rr, rb);
|
|
|
|
else //if (op == LIR_fdiv)
|
|
|
|
SSE_DIVSD(rr, rb);
|
|
|
|
|
|
|
|
if (rr != ra)
|
|
|
|
SSE_MOVSD(rr, ra);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// we swap lhs/rhs on purpose here, works out better
|
|
|
|
// if you only have one fpu reg. use divr/subr.
|
|
|
|
LIns* rhs = ins->oprnd1();
|
|
|
|
LIns* lhs = ins->oprnd2();
|
|
|
|
Register rr = prepResultReg(ins, rmask(FST0));
|
|
|
|
|
2010-01-05 15:28:40 -08:00
|
|
|
if (rhs->isconstq())
|
|
|
|
{
|
|
|
|
const uint64_t* p = findQuadConstant(rhs->imm64());
|
|
|
|
|
|
|
|
// lhs into reg, prefer same reg as result
|
|
|
|
|
|
|
|
// last use of lhs in reg, can reuse rr
|
|
|
|
// else, lhs already has a different reg assigned
|
|
|
|
if (lhs->isUnusedOrHasUnknownReg())
|
|
|
|
findSpecificRegForUnallocated(lhs, rr);
|
|
|
|
|
|
|
|
NanoAssert(lhs->getReg()==FST0);
|
|
|
|
// assume that the lhs is in ST(0) and rhs is on stack
|
|
|
|
if (op == LIR_fadd)
|
|
|
|
{ FADDdm((const double*)p); }
|
|
|
|
else if (op == LIR_fsub)
|
|
|
|
{ FSUBRdm((const double*)p); }
|
|
|
|
else if (op == LIR_fmul)
|
|
|
|
{ FMULdm((const double*)p); }
|
|
|
|
else if (op == LIR_fdiv)
|
|
|
|
{ FDIVRdm((const double*)p); }
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// make sure rhs is in memory
|
|
|
|
int db = findMemFor(rhs);
|
|
|
|
|
|
|
|
// lhs into reg, prefer same reg as result
|
|
|
|
|
|
|
|
// last use of lhs in reg, can reuse rr
|
|
|
|
// else, lhs already has a different reg assigned
|
|
|
|
if (lhs->isUnusedOrHasUnknownReg())
|
|
|
|
findSpecificRegForUnallocated(lhs, rr);
|
|
|
|
|
|
|
|
NanoAssert(lhs->getReg()==FST0);
|
|
|
|
// assume that the lhs is in ST(0) and rhs is on stack
|
|
|
|
if (op == LIR_fadd)
|
|
|
|
{ FADD(db, FP); }
|
|
|
|
else if (op == LIR_fsub)
|
|
|
|
{ FSUBR(db, FP); }
|
|
|
|
else if (op == LIR_fmul)
|
|
|
|
{ FMUL(db, FP); }
|
|
|
|
else if (op == LIR_fdiv)
|
|
|
|
{ FDIVR(db, FP); }
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::asm_i2f(LInsp ins)
|
|
|
|
{
|
2009-12-17 17:39:16 -08:00
|
|
|
LIns* lhs = ins->oprnd1();
|
|
|
|
|
|
|
|
Register rr = prepareResultReg(ins, FpRegs);
|
|
|
|
if (rmask(rr) & XmmRegs) {
|
2009-07-10 12:58:34 -07:00
|
|
|
// todo support int value in memory
|
2009-12-17 17:39:16 -08:00
|
|
|
Register ra = findRegFor(lhs, GpRegs);
|
|
|
|
SSE_CVTSI2SD(rr, ra);
|
|
|
|
SSE_XORPDr(rr, rr); // zero rr to ensure no dependency stalls
|
|
|
|
} else {
|
|
|
|
int d = findMemFor(lhs);
|
2009-07-10 12:58:34 -07:00
|
|
|
FILD(d, FP);
|
|
|
|
}
|
2009-12-17 17:39:16 -08:00
|
|
|
|
|
|
|
freeResourcesOf(ins);
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::asm_u2f(LInsp ins)
|
|
|
|
{
|
2009-12-17 17:39:16 -08:00
|
|
|
LIns* lhs = ins->oprnd1();
|
|
|
|
|
|
|
|
Register rr = prepareResultReg(ins, FpRegs);
|
|
|
|
if (rmask(rr) & XmmRegs) {
|
|
|
|
Register rt = registerAllocTmp(GpRegs);
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
// Technique inspired by gcc disassembly. Edwin explains it:
|
2009-07-10 12:58:34 -07:00
|
|
|
//
|
2009-12-17 17:39:16 -08:00
|
|
|
// rt is 0..2^32-1
|
2009-07-10 12:58:34 -07:00
|
|
|
//
|
2009-12-17 17:39:16 -08:00
|
|
|
// sub rt,0x80000000
|
2009-07-10 12:58:34 -07:00
|
|
|
//
|
2009-12-17 17:39:16 -08:00
|
|
|
// Now rt is -2^31..2^31-1, i.e. the range of int, but not the same value
|
|
|
|
// as before.
|
2009-07-10 12:58:34 -07:00
|
|
|
//
|
2009-12-17 17:39:16 -08:00
|
|
|
// cvtsi2sd rr,rt
|
2009-07-10 12:58:34 -07:00
|
|
|
//
|
2009-12-17 17:39:16 -08:00
|
|
|
// rr is now a double with the int value range.
|
2009-07-10 12:58:34 -07:00
|
|
|
//
|
|
|
|
// addsd rr, 2147483648.0
|
|
|
|
//
|
2009-12-17 17:39:16 -08:00
|
|
|
// Adding back double(0x80000000) makes the range 0..2^32-1.
|
2009-07-10 12:58:34 -07:00
|
|
|
|
|
|
|
static const double k_NEGONE = 2147483648.0;
|
|
|
|
SSE_ADDSDm(rr, &k_NEGONE);
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
SSE_CVTSI2SD(rr, rt);
|
2009-09-21 17:03:07 -07:00
|
|
|
SSE_XORPDr(rr,rr); // zero rr to ensure no dependency stalls
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
Register ra;
|
|
|
|
if (lhs->isUsed() && (ra = lhs->getReg(), isKnownReg(ra)) && (rmask(ra) & GpRegs)) {
|
|
|
|
LEA(rt, 0x80000000, ra);
|
|
|
|
|
|
|
|
} else {
|
|
|
|
const int d = findMemFor(lhs);
|
|
|
|
SUBi(rt, 0x80000000);
|
|
|
|
LD(rt, d, FP);
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
2009-12-17 17:39:16 -08:00
|
|
|
|
|
|
|
} else {
|
2008-07-31 13:28:12 -07:00
|
|
|
const int disp = -8;
|
|
|
|
const Register base = SP;
|
2009-12-17 17:39:16 -08:00
|
|
|
Register ra = findRegFor(lhs, GpRegs);
|
2009-07-10 12:58:34 -07:00
|
|
|
NanoAssert(rr == FST0);
|
|
|
|
FILDQ(disp, base);
|
2009-07-23 17:02:22 -07:00
|
|
|
STi(base, disp+4, 0); // high 32 bits = 0
|
2009-12-17 17:39:16 -08:00
|
|
|
ST(base, disp, ra); // low 32 bits = unsigned value
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
2009-12-17 17:39:16 -08:00
|
|
|
|
|
|
|
freeResourcesOf(ins);
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
|
2010-01-13 14:40:35 -08:00
|
|
|
void Assembler::asm_f2i(LInsp ins)
|
|
|
|
{
|
|
|
|
LIns *lhs = ins->oprnd1();
|
|
|
|
|
|
|
|
if (config.sse2) {
|
|
|
|
Register rr = prepareResultReg(ins, GpRegs);
|
|
|
|
Register ra = findRegFor(lhs, XmmRegs);
|
|
|
|
SSE_CVTSD2SI(rr, ra);
|
|
|
|
} else {
|
|
|
|
int pop = lhs->isUnusedOrHasUnknownReg();
|
|
|
|
findSpecificRegFor(lhs, FST0);
|
|
|
|
if (ins->hasKnownReg())
|
|
|
|
evict(ins);
|
|
|
|
int d = findMemFor(ins);
|
|
|
|
FIST((pop?1:0), d, FP);
|
|
|
|
}
|
|
|
|
|
|
|
|
freeResourcesOf(ins);
|
|
|
|
}
|
|
|
|
|
2009-12-17 17:39:16 -08:00
|
|
|
void Assembler::asm_nongp_copy(Register rd, Register rs)
|
2009-07-10 12:58:34 -07:00
|
|
|
{
|
2009-12-17 17:39:16 -08:00
|
|
|
if ((rmask(rd) & XmmRegs) && (rmask(rs) & XmmRegs)) {
|
|
|
|
// xmm -> xmm
|
|
|
|
SSE_MOVSD(rd, rs);
|
|
|
|
} else if ((rmask(rd) & GpRegs) && (rmask(rs) & XmmRegs)) {
|
|
|
|
// xmm -> gp
|
|
|
|
SSE_MOVD(rd, rs);
|
2009-07-10 12:58:34 -07:00
|
|
|
} else {
|
2009-12-17 17:39:16 -08:00
|
|
|
NanoAssertMsgf(false, "bad asm_nongp_copy(%s, %s)", gpn(rd), gpn(rs));
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
}
|
2008-07-01 14:46:10 -07:00
|
|
|
|
2009-11-17 16:15:20 -08:00
|
|
|
NIns* Assembler::asm_fbranch(bool branchOnFalse, LIns *cond, NIns *targ)
|
2008-10-13 13:29:18 -07:00
|
|
|
{
|
2009-11-17 16:15:20 -08:00
|
|
|
NIns* at;
|
|
|
|
LOpcode opcode = cond->opcode();
|
|
|
|
|
|
|
|
if (config.sse2) {
|
|
|
|
// LIR_flt and LIR_fgt are handled by the same case because
|
|
|
|
// asm_fcmp() converts LIR_flt(a,b) to LIR_fgt(b,a). Likewise
|
|
|
|
// for LIR_fle/LIR_fge.
|
|
|
|
if (branchOnFalse) {
|
|
|
|
// op == LIR_xf
|
|
|
|
switch (opcode) {
|
|
|
|
case LIR_feq: JP(targ); break;
|
|
|
|
case LIR_flt:
|
|
|
|
case LIR_fgt: JNA(targ); break;
|
|
|
|
case LIR_fle:
|
|
|
|
case LIR_fge: JNAE(targ); break;
|
|
|
|
default: NanoAssert(0); break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// op == LIR_xt
|
|
|
|
switch (opcode) {
|
|
|
|
case LIR_feq: JNP(targ); break;
|
|
|
|
case LIR_flt:
|
|
|
|
case LIR_fgt: JA(targ); break;
|
|
|
|
case LIR_fle:
|
|
|
|
case LIR_fge: JAE(targ); break;
|
|
|
|
default: NanoAssert(0); break;
|
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
2009-11-17 16:15:20 -08:00
|
|
|
} else {
|
|
|
|
if (branchOnFalse)
|
|
|
|
JP(targ);
|
|
|
|
else
|
|
|
|
JNP(targ);
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
|
|
|
|
2009-11-17 16:15:20 -08:00
|
|
|
at = _nIns;
|
2009-07-10 12:58:34 -07:00
|
|
|
asm_fcmp(cond);
|
2008-10-13 13:29:18 -07:00
|
|
|
|
2009-11-17 16:15:20 -08:00
|
|
|
return at;
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
|
|
|
|
2009-11-17 16:15:20 -08:00
|
|
|
// WARNING: This function cannot generate any code that will affect the
|
|
|
|
// condition codes prior to the generation of the
|
|
|
|
// ucomisd/fcompp/fcmop/fcom. See asm_cmp() for more details.
|
2009-07-10 12:58:34 -07:00
|
|
|
void Assembler::asm_fcmp(LIns *cond)
|
|
|
|
{
|
|
|
|
LOpcode condop = cond->opcode();
|
|
|
|
NanoAssert(condop >= LIR_feq && condop <= LIR_fge);
|
|
|
|
LIns* lhs = cond->oprnd1();
|
|
|
|
LIns* rhs = cond->oprnd2();
|
2010-01-24 13:25:04 -08:00
|
|
|
NanoAssert(lhs->isF64() && rhs->isF64());
|
2009-11-17 16:15:20 -08:00
|
|
|
|
|
|
|
if (config.sse2) {
|
|
|
|
// First, we convert (a < b) into (b > a), and (a <= b) into (b >= a).
|
|
|
|
if (condop == LIR_flt) {
|
|
|
|
condop = LIR_fgt;
|
|
|
|
LIns* t = lhs; lhs = rhs; rhs = t;
|
|
|
|
} else if (condop == LIR_fle) {
|
|
|
|
condop = LIR_fge;
|
|
|
|
LIns* t = lhs; lhs = rhs; rhs = t;
|
|
|
|
}
|
2008-07-01 14:46:10 -07:00
|
|
|
|
2009-11-17 16:15:20 -08:00
|
|
|
if (condop == LIR_feq) {
|
|
|
|
if (lhs == rhs) {
|
|
|
|
// We can generate better code for LIR_feq when lhs==rhs (NaN test).
|
|
|
|
|
|
|
|
// ucomisd ZPC outcome (SETNP/JNP succeeds if P==0)
|
|
|
|
// ------- --- -------
|
|
|
|
// UNORDERED 111 SETNP/JNP fails
|
|
|
|
// EQUAL 100 SETNP/JNP succeeds
|
|
|
|
|
|
|
|
Register r = findRegFor(lhs, XmmRegs);
|
|
|
|
SSE_UCOMISD(r, r);
|
|
|
|
} else {
|
|
|
|
// LAHF puts the flags into AH like so: SF:ZF:0:AF:0:PF:1:CF (aka. SZ0A_0P1C).
|
|
|
|
// We then mask out the bits as follows.
|
|
|
|
// - LIR_feq: mask == 0x44 == 0100_0100b, which extracts 0Z00_0P00 from AH.
|
|
|
|
int mask = 0x44;
|
|
|
|
|
|
|
|
// ucomisd ZPC lahf/test(0x44) SZP outcome
|
|
|
|
// ------- --- --------- --- -------
|
|
|
|
// UNORDERED 111 0100_0100 001 SETNP/JNP fails
|
|
|
|
// EQUAL 100 0100_0000 000 SETNP/JNP succeeds
|
2009-12-21 12:05:48 -08:00
|
|
|
// GREATER_THAN 000 0000_0000 011 SETNP/JNP fails
|
2009-11-17 16:15:20 -08:00
|
|
|
// LESS_THAN 001 0000_0000 011 SETNP/JNP fails
|
2009-12-17 17:39:16 -08:00
|
|
|
|
2009-11-17 16:15:20 -08:00
|
|
|
evictIfActive(EAX);
|
|
|
|
Register ra, rb;
|
2010-01-14 20:07:32 -08:00
|
|
|
findRegFor2(XmmRegs, lhs, ra, XmmRegs, rhs, rb);
|
2009-11-17 16:15:20 -08:00
|
|
|
|
|
|
|
TEST_AH(mask);
|
|
|
|
LAHF();
|
|
|
|
SSE_UCOMISD(ra, rb);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// LIR_fgt:
|
|
|
|
// ucomisd ZPC outcome (SETA/JA succeeds if CZ==00)
|
|
|
|
// ------- --- -------
|
|
|
|
// UNORDERED 111 SETA/JA fails
|
|
|
|
// EQUAL 100 SETA/JA fails
|
2009-12-21 12:05:48 -08:00
|
|
|
// GREATER_THAN 000 SETA/JA succeeds
|
|
|
|
// LESS_THAN 001 SETA/JA fails
|
2009-11-17 16:15:20 -08:00
|
|
|
//
|
|
|
|
// LIR_fge:
|
|
|
|
// ucomisd ZPC outcome (SETAE/JAE succeeds if C==0)
|
|
|
|
// ------- --- -------
|
|
|
|
// UNORDERED 111 SETAE/JAE fails
|
|
|
|
// EQUAL 100 SETAE/JAE succeeds
|
|
|
|
// GREATER_THAN 000 SETAE/JAE succeeds
|
|
|
|
// LESS_THAN 001 SETAE/JAE fails
|
2008-07-01 14:46:10 -07:00
|
|
|
|
2009-09-09 18:00:18 -07:00
|
|
|
Register ra, rb;
|
2010-01-14 20:07:32 -08:00
|
|
|
findRegFor2(XmmRegs, lhs, ra, XmmRegs, rhs, rb);
|
2009-09-09 18:00:18 -07:00
|
|
|
SSE_UCOMISD(ra, rb);
|
2008-07-01 14:46:10 -07:00
|
|
|
}
|
2009-11-17 16:15:20 -08:00
|
|
|
|
|
|
|
} else {
|
|
|
|
// First, we convert (a > b) into (b < a), and (a >= b) into (b <= a).
|
|
|
|
// Note that this is the opposite of the sse2 conversion above.
|
|
|
|
if (condop == LIR_fgt) {
|
|
|
|
condop = LIR_flt;
|
|
|
|
LIns* t = lhs; lhs = rhs; rhs = t;
|
|
|
|
} else if (condop == LIR_fge) {
|
|
|
|
condop = LIR_fle;
|
|
|
|
LIns* t = lhs; lhs = rhs; rhs = t;
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
2009-11-17 16:15:20 -08:00
|
|
|
|
|
|
|
// FNSTSW_AX puts the flags into AH like so: B:C3:TOP3:TOP2:TOP1:C2:C1:C0.
|
|
|
|
// Furthermore, fcom/fcomp/fcompp sets C3:C2:C0 the same values
|
|
|
|
// that Z:P:C are set by ucomisd, and the relative positions in AH
|
|
|
|
// line up. (Someone at Intel has a sense of humour.) Therefore
|
|
|
|
// we can use the same lahf/test(mask) technique as used in the
|
|
|
|
// sse2 case above. We could use fcomi/fcomip/fcomipp which set
|
|
|
|
// ZPC directly and then use LAHF instead of FNSTSW_AX and make
|
|
|
|
// this code generally more like the sse2 code, but we don't
|
|
|
|
// because fcomi/fcomip/fcomipp/lahf aren't available on earlier
|
|
|
|
// x86 machines.
|
|
|
|
//
|
|
|
|
// The masks are as follows:
|
|
|
|
// - LIR_feq: mask == 0x44 == 0100_0100b, which extracts 0Z00_0P00 from AH.
|
|
|
|
// - LIR_flt: mask == 0x05 == 0000_0101b, which extracts 0000_0P0C from AH.
|
|
|
|
// - LIR_fle: mask == 0x41 == 0100_0001b, which extracts 0Z00_000C from AH.
|
|
|
|
//
|
|
|
|
// LIR_feq (very similar to the sse2 case above):
|
|
|
|
// ucomisd C3:C2:C0 lahf/test(0x44) SZP outcome
|
|
|
|
// ------- -------- --------- --- -------
|
|
|
|
// UNORDERED 111 0100_0100 001 SETNP fails
|
|
|
|
// EQUAL 100 0100_0000 000 SETNP succeeds
|
2009-12-21 12:05:48 -08:00
|
|
|
// GREATER_THAN 000 0000_0000 011 SETNP fails
|
2009-11-17 16:15:20 -08:00
|
|
|
// LESS_THAN 001 0000_0000 011 SETNP fails
|
|
|
|
//
|
|
|
|
// LIR_flt:
|
|
|
|
// fcom C3:C2:C0 lahf/test(0x05) SZP outcome
|
|
|
|
// ------- -------- --------- --- -------
|
|
|
|
// UNORDERED 111 0000_0101 001 SETNP fails
|
|
|
|
// EQUAL 100 0000_0000 011 SETNP fails
|
2009-12-21 12:05:48 -08:00
|
|
|
// GREATER_THAN 000 0000_0000 011 SETNP fails
|
2009-11-17 16:15:20 -08:00
|
|
|
// LESS_THAN 001 0000_0001 000 SETNP succeeds
|
|
|
|
//
|
|
|
|
// LIR_fle:
|
|
|
|
// fcom C3:C2:C0 lahf/test(0x41) SZP outcome
|
|
|
|
// ------- --- --------- --- -------
|
|
|
|
// UNORDERED 111 0100_0001 001 SETNP fails
|
|
|
|
// EQUAL 100 0100_0000 000 SETNP succeeds
|
2009-12-21 12:05:48 -08:00
|
|
|
// GREATER_THAN 000 0000_0000 011 SETNP fails
|
2009-11-17 16:15:20 -08:00
|
|
|
// LESS_THAN 001 0000_0001 010 SETNP succeeds
|
|
|
|
|
2009-11-17 16:35:39 -08:00
|
|
|
int mask = 0; // init to avoid MSVC compile warnings
|
2009-11-17 16:15:20 -08:00
|
|
|
switch (condop) {
|
|
|
|
case LIR_feq: mask = 0x44; break;
|
|
|
|
case LIR_flt: mask = 0x05; break;
|
|
|
|
case LIR_fle: mask = 0x41; break;
|
|
|
|
default: NanoAssert(0); break;
|
|
|
|
}
|
|
|
|
|
|
|
|
evictIfActive(EAX);
|
|
|
|
int pop = lhs->isUnusedOrHasUnknownReg();
|
|
|
|
findSpecificRegFor(lhs, FST0);
|
|
|
|
|
|
|
|
if (lhs == rhs) {
|
|
|
|
// NaN test.
|
|
|
|
TEST_AH(mask);
|
2009-12-17 17:39:16 -08:00
|
|
|
FNSTSW_AX(); // requires EAX to be free
|
2009-07-10 12:58:34 -07:00
|
|
|
if (pop)
|
|
|
|
FCOMPP();
|
|
|
|
else
|
|
|
|
FCOMP();
|
|
|
|
FLDr(FST0); // DUP
|
2009-11-17 16:15:20 -08:00
|
|
|
} else {
|
|
|
|
TEST_AH(mask);
|
2009-12-17 17:39:16 -08:00
|
|
|
FNSTSW_AX(); // requires EAX to be free
|
2010-01-05 15:28:40 -08:00
|
|
|
if (rhs->isconstq())
|
|
|
|
{
|
|
|
|
const uint64_t* p = findQuadConstant(rhs->imm64());
|
|
|
|
FCOMdm((pop?1:0), (const double*)p);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int d = findMemFor(rhs);
|
|
|
|
FCOM((pop?1:0), d, FP);
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-09-15 15:05:53 -07:00
|
|
|
// Increment the 32-bit profiling counter at pCtr, without
|
|
|
|
// changing any registers.
|
|
|
|
verbose_only(
|
|
|
|
void Assembler::asm_inc_m32(uint32_t* pCtr)
|
|
|
|
{
|
|
|
|
INCLi(pCtr);
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
void Assembler::nativePageReset()
|
2009-09-18 13:31:09 -07:00
|
|
|
{}
|
2009-07-10 12:58:34 -07:00
|
|
|
|
|
|
|
void Assembler::nativePageSetup()
|
|
|
|
{
|
2009-11-23 19:56:33 -08:00
|
|
|
NanoAssert(!_inExit);
|
2009-09-15 15:05:53 -07:00
|
|
|
if (!_nIns)
|
|
|
|
codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes));
|
2009-07-10 12:58:34 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// enough room for n bytes
|
2008-10-13 13:29:18 -07:00
|
|
|
void Assembler::underrunProtect(int n)
|
|
|
|
{
|
2009-07-15 16:50:01 -07:00
|
|
|
NIns *eip = _nIns;
|
2009-07-10 12:58:34 -07:00
|
|
|
NanoAssertMsg(n<=LARGEST_UNDERRUN_PROT, "constant LARGEST_UNDERRUN_PROT is too small");
|
2009-11-23 19:56:33 -08:00
|
|
|
// This may be in a normal code chunk or an exit code chunk.
|
|
|
|
if (eip - n < codeStart) {
|
|
|
|
codeAlloc(codeStart, codeEnd, _nIns verbose_only(, codeBytes));
|
2008-10-13 13:29:18 -07:00
|
|
|
JMP(eip);
|
|
|
|
}
|
|
|
|
}
|
2009-07-10 12:58:34 -07:00
|
|
|
|
2009-08-24 16:57:25 -07:00
|
|
|
void Assembler::asm_ret(LInsp ins)
|
|
|
|
{
|
2009-08-31 16:35:50 -07:00
|
|
|
genEpilogue();
|
|
|
|
|
|
|
|
// Restore ESP from EBP, undoing SUBi(SP,amt) in the prologue
|
|
|
|
MR(SP,FP);
|
|
|
|
|
2009-12-22 02:37:49 -08:00
|
|
|
releaseRegisters();
|
2009-08-24 16:57:25 -07:00
|
|
|
assignSavedRegs();
|
2009-12-17 17:39:16 -08:00
|
|
|
|
2009-08-24 16:57:25 -07:00
|
|
|
LIns *val = ins->oprnd1();
|
|
|
|
if (ins->isop(LIR_ret)) {
|
|
|
|
findSpecificRegFor(val, retRegs[0]);
|
|
|
|
} else {
|
|
|
|
findSpecificRegFor(val, FST0);
|
|
|
|
fpu_pop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::asm_promote(LIns *) {
|
|
|
|
// i2q or u2q
|
|
|
|
TODO(asm_promote);
|
|
|
|
}
|
|
|
|
|
2009-11-23 19:56:33 -08:00
|
|
|
void Assembler::swapCodeChunks() {
|
2010-01-12 05:58:14 -08:00
|
|
|
if (!_nExitIns)
|
|
|
|
codeAlloc(exitStart, exitEnd, _nExitIns verbose_only(, exitBytes));
|
2009-11-23 19:56:33 -08:00
|
|
|
SWAP(NIns*, _nIns, _nExitIns);
|
|
|
|
SWAP(NIns*, codeStart, exitStart);
|
|
|
|
SWAP(NIns*, codeEnd, exitEnd);
|
|
|
|
verbose_only( SWAP(size_t, codeBytes, exitBytes); )
|
|
|
|
}
|
2009-12-21 12:05:48 -08:00
|
|
|
|
2009-07-10 12:58:34 -07:00
|
|
|
#endif /* FEATURE_NANOJIT */
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|