2008-06-30 15:33:41 -07:00
|
|
|
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 4 -*- */
|
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
|
|
|
|
*
|
|
|
|
* 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 ***** */
|
|
|
|
|
|
|
|
#include "nanojit.h"
|
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
#ifdef FEATURE_NANOJIT
|
|
|
|
|
2008-07-15 13:06:05 -07:00
|
|
|
#ifdef AVMPLUS_PORTING_API
|
|
|
|
#include "portapi_nanojit.h"
|
|
|
|
#endif
|
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
#if defined(AVMPLUS_UNIX) && defined(AVMPLUS_ARM)
|
2008-07-08 17:09:53 -07:00
|
|
|
#include <asm/unistd.h>
|
2008-09-02 22:29:23 -07:00
|
|
|
extern "C" void __clear_cache(char *BEG, char *END);
|
2008-07-08 17:09:53 -07:00
|
|
|
#endif
|
|
|
|
|
2009-02-15 18:10:03 -08:00
|
|
|
#ifdef AVMPLUS_SPARC
|
|
|
|
extern "C" void sync_instruction_memory(caddr_t v, u_int len);
|
|
|
|
#endif
|
|
|
|
|
2008-06-19 10:47:58 -07:00
|
|
|
namespace nanojit
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
class DeadCodeFilter: public LirFilter
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
const CallInfo *functions;
|
|
|
|
|
|
|
|
bool ignoreInstruction(LInsp ins)
|
|
|
|
{
|
|
|
|
LOpcode op = ins->opcode();
|
|
|
|
if (ins->isStore() ||
|
|
|
|
op == LIR_loop ||
|
|
|
|
op == LIR_label ||
|
|
|
|
op == LIR_live ||
|
|
|
|
isRet(op)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return ins->resv() == 0;
|
|
|
|
}
|
|
|
|
|
2008-12-10 17:25:46 -08:00
|
|
|
public:
|
2008-10-13 13:29:18 -07:00
|
|
|
DeadCodeFilter(LirFilter *in, const CallInfo *f) : LirFilter(in), functions(f) {}
|
2008-06-19 10:47:58 -07:00
|
|
|
LInsp read() {
|
|
|
|
for (;;) {
|
|
|
|
LInsp i = in->read();
|
2008-10-13 13:29:18 -07:00
|
|
|
if (!i || i->isGuard() || i->isBranch()
|
|
|
|
|| i->isCall() && !i->isCse(functions)
|
|
|
|
|| !ignoreInstruction(i))
|
2008-06-19 10:47:58 -07:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#ifdef NJ_VERBOSE
|
|
|
|
class VerboseBlockReader: public LirFilter
|
|
|
|
{
|
|
|
|
Assembler *assm;
|
|
|
|
LirNameMap *names;
|
2008-11-14 12:46:35 -08:00
|
|
|
InsList block;
|
2008-10-13 13:29:18 -07:00
|
|
|
bool flushnext;
|
2008-06-19 10:47:58 -07:00
|
|
|
public:
|
|
|
|
VerboseBlockReader(LirFilter *in, Assembler *a, LirNameMap *n)
|
2008-10-13 13:29:18 -07:00
|
|
|
: LirFilter(in), assm(a), names(n), block(a->_gc), flushnext(false)
|
|
|
|
{}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
void flush() {
|
2008-10-13 13:29:18 -07:00
|
|
|
flushnext = false;
|
|
|
|
if (!block.isEmpty()) {
|
|
|
|
for (int j=0,n=block.size(); j < n; j++) {
|
|
|
|
LIns *i = block[j];
|
2008-10-20 15:52:11 -07:00
|
|
|
assm->outputf(" %s", names->formatIns(i));
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
|
|
|
block.clear();
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
void flush_add(LInsp i) {
|
|
|
|
flush();
|
|
|
|
block.add(i);
|
|
|
|
}
|
|
|
|
|
2008-06-19 10:47:58 -07:00
|
|
|
LInsp read() {
|
|
|
|
LInsp i = in->read();
|
|
|
|
if (!i) {
|
|
|
|
flush();
|
|
|
|
return i;
|
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
if (i->isGuard()) {
|
|
|
|
flush_add(i);
|
2008-06-19 10:47:58 -07:00
|
|
|
if (i->oprnd1())
|
|
|
|
block.add(i->oprnd1());
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
|
|
|
else if (isRet(i->opcode()) || i->isBranch()) {
|
|
|
|
flush_add(i);
|
|
|
|
}
|
2008-07-16 14:21:31 -07:00
|
|
|
else {
|
2008-10-13 13:29:18 -07:00
|
|
|
if (flushnext)
|
|
|
|
flush();
|
2008-12-11 13:50:55 -08:00
|
|
|
block.add(i);//flush_add(i);
|
2008-10-13 13:29:18 -07:00
|
|
|
if (i->isop(LIR_label))
|
|
|
|
flushnext = true;
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Need the following:
|
|
|
|
*
|
|
|
|
* - merging paths ( build a graph? ), possibly use external rep to drive codegen
|
|
|
|
*/
|
|
|
|
Assembler::Assembler(Fragmento* frago)
|
2008-10-13 13:29:18 -07:00
|
|
|
: hasLoop(0)
|
2008-12-10 17:25:46 -08:00
|
|
|
, _frago(frago)
|
2008-06-19 10:47:58 -07:00
|
|
|
, _gc(frago->core()->gc)
|
2008-10-13 13:29:18 -07:00
|
|
|
, _labels(_gc)
|
|
|
|
, _patches(_gc)
|
|
|
|
, pending_lives(_gc)
|
2008-12-10 17:19:40 -08:00
|
|
|
, config(frago->core()->config)
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
|
|
|
AvmCore *core = frago->core();
|
|
|
|
nInit(core);
|
|
|
|
verbose_only( _verbose = !core->quiet_opt() && core->verbose() );
|
|
|
|
verbose_only( _outputCache = 0);
|
2008-10-20 15:52:11 -07:00
|
|
|
verbose_only( outlineEOL[0] = '\0');
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
internalReset();
|
2008-11-14 12:46:35 -08:00
|
|
|
pageReset();
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::arReset()
|
|
|
|
{
|
|
|
|
_activation.highwatermark = 0;
|
|
|
|
_activation.lowwatermark = 0;
|
|
|
|
_activation.tos = 0;
|
|
|
|
|
|
|
|
for(uint32_t i=0; i<NJ_MAX_STACK_ENTRY; i++)
|
|
|
|
_activation.entry[i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::registerResetAll()
|
|
|
|
{
|
|
|
|
nRegisterResetAll(_allocator);
|
|
|
|
|
|
|
|
// keep a tally of the registers to check that our allocator works correctly
|
|
|
|
debug_only(_allocator.count = _allocator.countFree(); )
|
|
|
|
debug_only(_allocator.checkCount(); )
|
|
|
|
debug_only(_fpuStkDepth = 0; )
|
|
|
|
}
|
|
|
|
|
|
|
|
Register Assembler::registerAlloc(RegisterMask allow)
|
|
|
|
{
|
|
|
|
RegAlloc ®s = _allocator;
|
|
|
|
// RegisterMask prefer = livePastCall(_ins) ? saved : scratch;
|
|
|
|
RegisterMask prefer = SavedRegs & allow;
|
|
|
|
RegisterMask free = regs.free & allow;
|
|
|
|
|
|
|
|
RegisterMask set = prefer;
|
|
|
|
if (set == 0) set = allow;
|
|
|
|
|
|
|
|
if (free)
|
|
|
|
{
|
|
|
|
// at least one is free
|
|
|
|
set &= free;
|
|
|
|
|
|
|
|
// ok we have at least 1 free register so let's try to pick
|
|
|
|
// the best one given the profile of the instruction
|
|
|
|
if (!set)
|
|
|
|
{
|
|
|
|
// desired register class is not free so pick first of any class
|
|
|
|
set = free;
|
|
|
|
}
|
|
|
|
NanoAssert((set & allow) != 0);
|
|
|
|
Register r = nRegisterAllocFromSet(set);
|
|
|
|
regs.used |= rmask(r);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
counter_increment(steals);
|
|
|
|
|
|
|
|
// nothing free, steal one
|
|
|
|
// LSRA says pick the one with the furthest use
|
2008-10-20 10:15:07 -07:00
|
|
|
LIns* vic = findVictim(regs, allow);
|
2008-09-02 22:29:23 -07:00
|
|
|
NanoAssert(vic != NULL);
|
|
|
|
|
2008-06-19 10:47:58 -07:00
|
|
|
Reservation* resv = getresv(vic);
|
|
|
|
|
|
|
|
// restore vic
|
|
|
|
Register r = resv->reg;
|
|
|
|
regs.removeActive(r);
|
|
|
|
resv->reg = UnknownReg;
|
|
|
|
|
|
|
|
asm_restore(vic, resv, r);
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::reserveReset()
|
|
|
|
{
|
|
|
|
_resvTable[0].arIndex = 0;
|
|
|
|
int i;
|
2008-10-13 13:29:18 -07:00
|
|
|
for(i=1; i<NJ_MAX_STACK_ENTRY; i++) {
|
2008-06-19 10:47:58 -07:00
|
|
|
_resvTable[i].arIndex = i-1;
|
2008-10-13 13:29:18 -07:00
|
|
|
_resvTable[i].used = 0;
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
_resvFree= i-1;
|
|
|
|
}
|
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
/**
|
|
|
|
* these instructions don't have to be saved & reloaded to spill,
|
|
|
|
* they can just be recalculated w/out any inputs.
|
|
|
|
*/
|
|
|
|
bool Assembler::canRemat(LIns *i) {
|
|
|
|
return i->isconst() || i->isconstq() || i->isop(LIR_alloc);
|
|
|
|
}
|
|
|
|
|
2008-06-19 10:47:58 -07:00
|
|
|
Reservation* Assembler::reserveAlloc(LInsp i)
|
|
|
|
{
|
|
|
|
uint32_t item = _resvFree;
|
2008-12-04 11:41:43 -08:00
|
|
|
/* If there are no free reservations, mark the table as full and re-use an index.
|
|
|
|
* This will clobber that reservation, but the error will be caught as soon as
|
|
|
|
* the current LIR instruction returns back to gen().
|
|
|
|
*/
|
|
|
|
if (!item) {
|
|
|
|
setError(ResvFull);
|
|
|
|
item = 1;
|
|
|
|
}
|
2008-12-10 17:25:46 -08:00
|
|
|
Reservation *r = &_resvTable[item];
|
2008-06-19 10:47:58 -07:00
|
|
|
_resvFree = r->arIndex;
|
|
|
|
r->reg = UnknownReg;
|
|
|
|
r->arIndex = 0;
|
2008-12-10 17:25:46 -08:00
|
|
|
r->used = 1;
|
|
|
|
i->setresv(item);
|
2008-06-19 10:47:58 -07:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::reserveFree(LInsp i)
|
|
|
|
{
|
|
|
|
Reservation *rs = getresv(i);
|
|
|
|
NanoAssert(rs == &_resvTable[i->resv()]);
|
|
|
|
rs->arIndex = _resvFree;
|
2008-10-13 13:29:18 -07:00
|
|
|
rs->used = 0;
|
2008-06-19 10:47:58 -07:00
|
|
|
_resvFree = i->resv();
|
|
|
|
i->setresv(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::internalReset()
|
|
|
|
{
|
|
|
|
// readies for a brand spanking new code generation pass.
|
|
|
|
registerResetAll();
|
|
|
|
reserveReset();
|
|
|
|
arReset();
|
2008-10-13 13:29:18 -07:00
|
|
|
pending_lives.clear();
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NIns* Assembler::pageAlloc(bool exitPage)
|
|
|
|
{
|
|
|
|
Page*& list = (exitPage) ? _nativeExitPages : _nativePages;
|
|
|
|
Page* page = _frago->pageAlloc();
|
|
|
|
if (page)
|
|
|
|
{
|
|
|
|
page->next = list;
|
|
|
|
list = page;
|
2008-10-21 18:34:10 -07:00
|
|
|
nMarkExecute(page, PAGE_READ|PAGE_WRITE|PAGE_EXEC);
|
2008-10-13 13:29:18 -07:00
|
|
|
_stats.pages++;
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-11-14 12:46:35 -08:00
|
|
|
// return a location that is 'safe' to write to while we are out of mem
|
2008-06-19 10:47:58 -07:00
|
|
|
setError(OutOMem);
|
2008-11-14 12:46:35 -08:00
|
|
|
return _startingIns;
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
return &page->code[sizeof(page->code)/sizeof(NIns)]; // just past the end
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::pageReset()
|
|
|
|
{
|
|
|
|
pagesFree(_nativePages);
|
|
|
|
pagesFree(_nativeExitPages);
|
|
|
|
|
|
|
|
_nIns = 0;
|
|
|
|
_nExitIns = 0;
|
2008-11-14 12:46:35 -08:00
|
|
|
_startingIns = 0;
|
2008-10-13 13:29:18 -07:00
|
|
|
_stats.pages = 0;
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
nativePageReset();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::pagesFree(Page*& page)
|
|
|
|
{
|
|
|
|
while(page)
|
|
|
|
{
|
|
|
|
Page *next = page->next; // pull next ptr prior to free
|
|
|
|
_frago->pageFree(page);
|
|
|
|
page = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
#define bytesFromTop(x) ( (size_t)(x) - (size_t)pageTop(x) )
|
|
|
|
#define bytesToBottom(x) ( (size_t)pageBottom(x) - (size_t)(x) )
|
|
|
|
#define bytesBetween(x,y) ( (size_t)(x) - (size_t)(y) )
|
|
|
|
|
|
|
|
int32_t Assembler::codeBytes()
|
|
|
|
{
|
|
|
|
// start and end on same page?
|
|
|
|
size_t exit = 0;
|
|
|
|
int32_t pages = _stats.pages;
|
|
|
|
if (_nExitIns-1 == _stats.codeExitStart)
|
|
|
|
;
|
|
|
|
else if (samepage(_nExitIns,_stats.codeExitStart))
|
|
|
|
exit = bytesBetween(_stats.codeExitStart, _nExitIns);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pages--;
|
|
|
|
exit = ((intptr_t)_stats.codeExitStart & (NJ_PAGE_SIZE-1)) ? bytesFromTop(_stats.codeExitStart)+1 : 0;
|
|
|
|
exit += bytesToBottom(_nExitIns)+1;
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t main = 0;
|
|
|
|
if (_nIns-1 == _stats.codeStart)
|
|
|
|
;
|
|
|
|
else if (samepage(_nIns,_stats.codeStart))
|
|
|
|
main = bytesBetween(_stats.codeStart, _nIns);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pages--;
|
|
|
|
main = ((intptr_t)_stats.codeStart & (NJ_PAGE_SIZE-1)) ? bytesFromTop(_stats.codeStart)+1 : 0;
|
|
|
|
main += bytesToBottom(_nIns)+1;
|
|
|
|
}
|
|
|
|
//fprintf(stderr,"size %d, exit is %d, main is %d, page count %d, sizeof %d\n", (int)((pages) * NJ_PAGE_SIZE + main + exit),(int)exit, (int)main, (int)_stats.pages, (int)sizeof(Page));
|
|
|
|
return (pages) * NJ_PAGE_SIZE + main + exit;
|
|
|
|
}
|
|
|
|
|
|
|
|
#undef bytesFromTop
|
|
|
|
#undef bytesToBottom
|
|
|
|
#undef byteBetween
|
|
|
|
|
2008-06-19 10:47:58 -07:00
|
|
|
Page* Assembler::handoverPages(bool exitPages)
|
|
|
|
{
|
|
|
|
Page*& list = (exitPages) ? _nativeExitPages : _nativePages;
|
|
|
|
NIns*& ins = (exitPages) ? _nExitIns : _nIns;
|
|
|
|
Page* start = list;
|
|
|
|
list = 0;
|
|
|
|
ins = 0;
|
|
|
|
return start;
|
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
|
2008-06-19 10:47:58 -07:00
|
|
|
#ifdef _DEBUG
|
|
|
|
bool Assembler::onPage(NIns* where, bool exitPages)
|
|
|
|
{
|
|
|
|
Page* page = (exitPages) ? _nativeExitPages : _nativePages;
|
|
|
|
bool on = false;
|
|
|
|
while(page)
|
|
|
|
{
|
|
|
|
if (samepage(where-1,page))
|
|
|
|
on = true;
|
|
|
|
page = page->next;
|
|
|
|
}
|
|
|
|
return on;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::pageValidate()
|
|
|
|
{
|
|
|
|
if (error()) return;
|
|
|
|
// _nIns and _nExitIns need to be at least on
|
|
|
|
// one of these pages
|
|
|
|
NanoAssertMsg( onPage(_nIns)&& onPage(_nExitIns,true), "Native instruction pointer overstep paging bounds; check overrideProtect for last instruction");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
|
|
|
|
|
void Assembler::resourceConsistencyCheck()
|
|
|
|
{
|
|
|
|
if (error()) return;
|
|
|
|
|
|
|
|
#ifdef NANOJIT_IA32
|
|
|
|
NanoAssert(_allocator.active[FST0] && _fpuStkDepth == -1 ||
|
|
|
|
!_allocator.active[FST0] && _fpuStkDepth == 0);
|
|
|
|
#endif
|
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
AR &ar = _activation;
|
2008-06-19 10:47:58 -07:00
|
|
|
// check AR entries
|
2008-10-13 13:29:18 -07:00
|
|
|
NanoAssert(ar.highwatermark < NJ_MAX_STACK_ENTRY);
|
2008-06-19 10:47:58 -07:00
|
|
|
LIns* ins = 0;
|
|
|
|
RegAlloc* regs = &_allocator;
|
2008-10-13 13:29:18 -07:00
|
|
|
for(uint32_t i = ar.lowwatermark; i < ar.tos; i++)
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
ins = ar.entry[i];
|
2008-06-19 10:47:58 -07:00
|
|
|
if ( !ins )
|
|
|
|
continue;
|
|
|
|
Reservation *r = getresv(ins);
|
2008-10-13 13:29:18 -07:00
|
|
|
NanoAssert(r != 0);
|
2008-06-19 10:47:58 -07:00
|
|
|
int32_t idx = r - _resvTable;
|
|
|
|
NanoAssertMsg(idx, "MUST have a resource for the instruction for it to have a stack location assigned to it");
|
2008-10-13 13:29:18 -07:00
|
|
|
if (r->arIndex) {
|
|
|
|
if (ins->isop(LIR_alloc)) {
|
|
|
|
int j=i+1;
|
|
|
|
for (int n = i + (ins->size()>>2); j < n; j++) {
|
|
|
|
NanoAssert(ar.entry[j]==ins);
|
|
|
|
}
|
|
|
|
NanoAssert(r->arIndex == (uint32_t)j-1);
|
|
|
|
i = j-1;
|
|
|
|
}
|
|
|
|
else if (ins->isQuad()) {
|
|
|
|
NanoAssert(ar.entry[i - stack_direction(1)]==ins);
|
|
|
|
i += 1; // skip high word
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
NanoAssertMsg(r->arIndex == i, "Stack record index mismatch");
|
|
|
|
}
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
NanoAssertMsg( r->reg==UnknownReg || regs->isConsistent(r->reg,ins), "Register record mismatch");
|
|
|
|
}
|
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
registerConsistencyCheck();
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
// check resv table
|
|
|
|
int32_t inuseCount = 0;
|
|
|
|
int32_t notInuseCount = 0;
|
2008-10-13 13:29:18 -07:00
|
|
|
for(uint32_t i=1; i < sizeof(_resvTable)/sizeof(_resvTable[0]); i++) {
|
|
|
|
_resvTable[i].used ? inuseCount++ : notInuseCount++;
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
int32_t freeCount = 0;
|
|
|
|
uint32_t free = _resvFree;
|
2008-10-13 13:29:18 -07:00
|
|
|
while(free) {
|
2008-06-19 10:47:58 -07:00
|
|
|
free = _resvTable[free].arIndex;
|
|
|
|
freeCount++;
|
|
|
|
}
|
|
|
|
NanoAssert( ( freeCount==notInuseCount && inuseCount+notInuseCount==(NJ_MAX_STACK_ENTRY-1) ) );
|
|
|
|
}
|
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
void Assembler::registerConsistencyCheck()
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
|
|
|
// check registers
|
|
|
|
RegAlloc *regs = &_allocator;
|
|
|
|
uint32_t managed = regs->managed;
|
|
|
|
Register r = FirstReg;
|
|
|
|
while(managed)
|
|
|
|
{
|
|
|
|
if (managed&1)
|
|
|
|
{
|
|
|
|
if (regs->isFree(r))
|
|
|
|
{
|
|
|
|
NanoAssert(regs->getActive(r)==0);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LIns* ins = regs->getActive(r);
|
|
|
|
// @todo we should be able to check across RegAlloc's somehow (to include savedGP...)
|
|
|
|
Reservation *v = getresv(ins);
|
2008-10-13 13:29:18 -07:00
|
|
|
NanoAssert(v != 0);
|
2008-06-19 10:47:58 -07:00
|
|
|
int32_t idx = v - _resvTable;
|
|
|
|
NanoAssert(idx >= 0 && idx < NJ_MAX_STACK_ENTRY);
|
|
|
|
NanoAssertMsg(idx, "MUST have a resource for the instruction for it to have a register assigned to it");
|
|
|
|
NanoAssertMsg( regs->getActive(v->reg)==ins, "Register record mismatch");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// next register in bitfield
|
|
|
|
r = nextreg(r);
|
|
|
|
managed >>= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif /* _DEBUG */
|
|
|
|
|
|
|
|
void Assembler::findRegFor2(RegisterMask allow, LIns* ia, Reservation* &resva, LIns* ib, Reservation* &resvb)
|
|
|
|
{
|
|
|
|
if (ia == ib)
|
|
|
|
{
|
|
|
|
findRegFor(ia, allow);
|
|
|
|
resva = resvb = getresv(ia);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Register rb = UnknownReg;
|
2008-10-13 13:29:18 -07:00
|
|
|
resvb = getresv(ib);
|
|
|
|
if (resvb && (rb = resvb->reg) != UnknownReg) {
|
|
|
|
if (allow & rmask(rb)) {
|
|
|
|
// ib already assigned to an allowable reg, keep that one
|
|
|
|
allow &= ~rmask(rb);
|
|
|
|
} else {
|
|
|
|
// ib assigned to unusable reg, pick a different one below.
|
|
|
|
rb = UnknownReg;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Register ra = findRegFor(ia, allow);
|
|
|
|
resva = getresv(ia);
|
|
|
|
NanoAssert(error() || (resva != 0 && ra != UnknownReg));
|
|
|
|
if (rb == UnknownReg)
|
|
|
|
{
|
|
|
|
allow &= ~rmask(ra);
|
|
|
|
findRegFor(ib, allow);
|
|
|
|
resvb = getresv(ib);
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Register Assembler::findSpecificRegFor(LIns* i, Register w)
|
|
|
|
{
|
|
|
|
return findRegFor(i, rmask(w));
|
|
|
|
}
|
2008-10-20 10:15:07 -07:00
|
|
|
|
|
|
|
Register Assembler::getBaseReg(LIns *i, int &d, RegisterMask allow)
|
|
|
|
{
|
|
|
|
if (i->isop(LIR_alloc)) {
|
|
|
|
d += findMemFor(i);
|
|
|
|
return FP;
|
|
|
|
} else {
|
|
|
|
return findRegFor(i, allow);
|
|
|
|
}
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
Register Assembler::findRegFor(LIns* i, RegisterMask allow)
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
if (i->isop(LIR_alloc)) {
|
|
|
|
// never allocate a reg for this w/out stack space too
|
|
|
|
findMemFor(i);
|
|
|
|
}
|
|
|
|
|
|
|
|
Reservation* resv = getresv(i);
|
2008-06-19 10:47:58 -07:00
|
|
|
Register r;
|
|
|
|
|
2008-09-02 22:29:23 -07:00
|
|
|
// if we have an existing reservation and it has a non-unknown
|
|
|
|
// register allocated, and that register is in our allowed mask,
|
|
|
|
// return it.
|
2008-06-19 10:47:58 -07:00
|
|
|
if (resv && (r=resv->reg) != UnknownReg && (rmask(r) & allow)) {
|
2008-10-13 13:29:18 -07:00
|
|
|
_allocator.useActive(r);
|
2008-06-19 10:47:58 -07:00
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2008-09-02 22:29:23 -07:00
|
|
|
// figure out what registers are preferred for this instruction
|
2008-06-19 10:47:58 -07:00
|
|
|
RegisterMask prefer = hint(i, allow);
|
2008-09-02 22:29:23 -07:00
|
|
|
|
|
|
|
// if we didn't have a reservation, allocate one now
|
2008-10-13 13:29:18 -07:00
|
|
|
if (!resv)
|
2008-06-19 10:47:58 -07:00
|
|
|
resv = reserveAlloc(i);
|
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
r = resv->reg;
|
2008-10-20 10:15:07 -07:00
|
|
|
|
|
|
|
#ifdef AVMPLUS_IA32
|
2008-10-13 13:29:18 -07:00
|
|
|
if (r != UnknownReg &&
|
|
|
|
((rmask(r)&XmmRegs) && !(allow&XmmRegs) ||
|
|
|
|
(rmask(r)&x87Regs) && !(allow&x87Regs)))
|
|
|
|
{
|
|
|
|
// x87 <-> xmm copy required
|
|
|
|
//_nvprof("fpu-evict",1);
|
|
|
|
evict(r);
|
|
|
|
r = UnknownReg;
|
|
|
|
}
|
2008-10-20 10:15:07 -07:00
|
|
|
#endif
|
2008-10-13 13:29:18 -07:00
|
|
|
|
|
|
|
if (r == UnknownReg)
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
|
|
|
r = resv->reg = registerAlloc(prefer);
|
|
|
|
_allocator.addActive(r, i);
|
|
|
|
return r;
|
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
else
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
2008-09-02 22:29:23 -07:00
|
|
|
// the already-allocated register isn't in the allowed mask;
|
|
|
|
// we need to grab a new one and then copy over the old
|
|
|
|
// contents to the new.
|
2008-06-19 10:47:58 -07:00
|
|
|
resv->reg = UnknownReg;
|
|
|
|
_allocator.retire(r);
|
|
|
|
Register s = resv->reg = registerAlloc(prefer);
|
|
|
|
_allocator.addActive(s, i);
|
2008-08-18 12:32:14 -07:00
|
|
|
if ((rmask(r) & GpRegs) && (rmask(s) & GpRegs)) {
|
2008-06-19 10:47:58 -07:00
|
|
|
MR(r, s);
|
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
else {
|
2008-07-01 14:46:10 -07:00
|
|
|
asm_nongp_copy(r, s);
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
return s;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int Assembler::findMemFor(LIns *i)
|
|
|
|
{
|
|
|
|
Reservation* resv = getresv(i);
|
|
|
|
if (!resv)
|
|
|
|
resv = reserveAlloc(i);
|
2008-10-13 13:29:18 -07:00
|
|
|
if (!resv->arIndex) {
|
2008-06-19 10:47:58 -07:00
|
|
|
resv->arIndex = arReserve(i);
|
2008-10-13 13:29:18 -07:00
|
|
|
NanoAssert(resv->arIndex <= _activation.highwatermark);
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
return disp(resv);
|
|
|
|
}
|
|
|
|
|
|
|
|
Register Assembler::prepResultReg(LIns *i, RegisterMask allow)
|
|
|
|
{
|
|
|
|
Reservation* resv = getresv(i);
|
|
|
|
const bool pop = !resv || resv->reg == UnknownReg;
|
|
|
|
Register rr = findRegFor(i, allow);
|
|
|
|
freeRsrcOf(i, pop);
|
|
|
|
return rr;
|
|
|
|
}
|
|
|
|
|
2008-10-20 10:15:07 -07:00
|
|
|
void Assembler::asm_spilli(LInsp i, Reservation *resv, bool pop)
|
|
|
|
{
|
|
|
|
int d = disp(resv);
|
|
|
|
Register rr = resv->reg;
|
|
|
|
bool quad = i->opcode() == LIR_param || i->isQuad();
|
2008-10-20 15:52:11 -07:00
|
|
|
verbose_only( if (d && _verbose) { outputForEOL(" <= spill %s", _thisfrag->lirbuf->names->formatRef(i)); } )
|
2008-10-20 10:15:07 -07:00
|
|
|
asm_spill(rr, d, pop, quad);
|
|
|
|
}
|
|
|
|
|
2008-06-19 10:47:58 -07:00
|
|
|
void Assembler::freeRsrcOf(LIns *i, bool pop)
|
|
|
|
{
|
|
|
|
Reservation* resv = getresv(i);
|
|
|
|
int index = resv->arIndex;
|
|
|
|
Register rr = resv->reg;
|
|
|
|
|
|
|
|
if (rr != UnknownReg)
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
asm_spilli(i, resv, pop);
|
2008-06-19 10:47:58 -07:00
|
|
|
_allocator.retire(rr); // free any register associated with entry
|
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
if (index)
|
|
|
|
arFree(index); // free any stack stack space associated with entry
|
2008-06-19 10:47:58 -07:00
|
|
|
reserveFree(i); // clear fields of entry and add it to free list
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::evict(Register r)
|
|
|
|
{
|
|
|
|
registerAlloc(rmask(r));
|
|
|
|
_allocator.addFree(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::patch(GuardRecord *lr)
|
|
|
|
{
|
2008-10-21 17:50:32 -07:00
|
|
|
Fragment *frag = lr->exit->target;
|
2008-10-13 13:29:18 -07:00
|
|
|
NanoAssert(frag->fragEntry != 0);
|
2008-12-10 17:31:17 -08:00
|
|
|
NIns* was = nPatchBranch((NIns*)lr->jmp, frag->fragEntry);
|
2008-06-24 15:57:33 -07:00
|
|
|
verbose_only(verbose_outputf("patching jump at %p to target %p (was %p)\n",
|
2008-12-10 17:31:17 -08:00
|
|
|
lr->jmp, frag->fragEntry, was);)
|
2008-11-04 14:18:17 -08:00
|
|
|
(void)was;
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
|
2008-10-21 17:50:32 -07:00
|
|
|
void Assembler::patch(SideExit *exit)
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
2008-10-21 17:50:32 -07:00
|
|
|
GuardRecord *rec = exit->guards;
|
|
|
|
AvmAssert(rec);
|
|
|
|
while (rec) {
|
|
|
|
patch(rec);
|
2008-10-22 16:00:08 -07:00
|
|
|
rec = rec->next;
|
2008-10-21 17:50:32 -07:00
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
2008-11-04 14:20:19 -08:00
|
|
|
|
2009-02-11 17:40:27 -08:00
|
|
|
#ifdef NANOJIT_IA32
|
|
|
|
void Assembler::patch(SideExit* exit, SwitchInfo* si)
|
|
|
|
{
|
|
|
|
for (GuardRecord* lr = exit->guards; lr; lr = lr->next) {
|
|
|
|
Fragment *frag = lr->exit->target;
|
|
|
|
NanoAssert(frag->fragEntry != 0);
|
|
|
|
si->table[si->index] = frag->fragEntry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-06-24 15:57:33 -07:00
|
|
|
NIns* Assembler::asm_exit(LInsp guard)
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
2008-10-21 17:50:32 -07:00
|
|
|
SideExit *exit = guard->record()->exit;
|
2008-06-19 10:47:58 -07:00
|
|
|
NIns* at = 0;
|
|
|
|
if (!_branchStateMap->get(exit))
|
|
|
|
{
|
2008-06-24 15:57:33 -07:00
|
|
|
at = asm_leave_trace(guard);
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
RegAlloc* captured = _branchStateMap->get(exit);
|
2008-10-13 13:29:18 -07:00
|
|
|
intersectRegisterState(*captured);
|
2008-06-24 15:57:33 -07:00
|
|
|
verbose_only(
|
|
|
|
verbose_outputf(" merging trunk with %s",
|
|
|
|
_frago->labels->format(exit->target));
|
|
|
|
verbose_outputf(" %p:",_nIns);
|
|
|
|
)
|
2008-06-19 10:47:58 -07:00
|
|
|
at = exit->target->fragEntry;
|
2008-10-13 13:29:18 -07:00
|
|
|
NanoAssert(at != 0);
|
2008-06-24 15:57:33 -07:00
|
|
|
_branchStateMap->remove(exit);
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
return at;
|
|
|
|
}
|
|
|
|
|
2008-06-24 15:57:33 -07:00
|
|
|
NIns* Assembler::asm_leave_trace(LInsp guard)
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
|
|
|
verbose_only(bool priorVerbose = _verbose; )
|
|
|
|
verbose_only( _verbose = verbose_enabled() && _frago->core()->config.verbose_exits; )
|
|
|
|
verbose_only( int32_t nativeSave = _stats.native );
|
2008-10-21 17:50:32 -07:00
|
|
|
verbose_only(verbose_outputf("--------------------------------------- end exit block %p", guard);)
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
RegAlloc capture = _allocator;
|
|
|
|
|
|
|
|
// this point is unreachable. so free all the registers.
|
|
|
|
// if an instruction has a stack entry we will leave it alone,
|
2008-10-13 13:29:18 -07:00
|
|
|
// otherwise we free it entirely. intersectRegisterState will restore.
|
2008-06-19 10:47:58 -07:00
|
|
|
releaseRegisters();
|
|
|
|
|
|
|
|
swapptrs();
|
|
|
|
_inExit = true;
|
|
|
|
|
|
|
|
//verbose_only( verbose_outputf(" LIR_xend swapptrs, _nIns is now %08X(%08X), _nExitIns is now %08X(%08X)",_nIns, *_nIns,_nExitIns,*_nExitIns) );
|
|
|
|
debug_only( _sv_fpuStkDepth = _fpuStkDepth; _fpuStkDepth = 0; )
|
|
|
|
|
2008-07-08 17:09:53 -07:00
|
|
|
nFragExit(guard);
|
|
|
|
|
2008-10-31 16:48:14 -07:00
|
|
|
// restore the callee-saved register and parameters
|
|
|
|
assignSavedRegs();
|
|
|
|
assignParamRegs();
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
intersectRegisterState(capture);
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
// this can be useful for breaking whenever an exit is taken
|
|
|
|
//INT3();
|
|
|
|
//NOP();
|
|
|
|
|
|
|
|
// we are done producing the exit logic for the guard so demark where our exit block code begins
|
2008-12-10 17:31:17 -08:00
|
|
|
NIns* jmpTarget = _nIns; // target in exit path for our mainline conditional jump
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
// swap back pointers, effectively storing the last location used in the exit path
|
|
|
|
swapptrs();
|
|
|
|
_inExit = false;
|
|
|
|
|
|
|
|
//verbose_only( verbose_outputf(" LIR_xt/xf swapptrs, _nIns is now %08X(%08X), _nExitIns is now %08X(%08X)",_nIns, *_nIns,_nExitIns,*_nExitIns) );
|
2008-12-10 17:31:17 -08:00
|
|
|
verbose_only( verbose_outputf(" %p:",jmpTarget);)
|
2008-06-19 10:47:58 -07:00
|
|
|
verbose_only( verbose_outputf("--------------------------------------- exit block (LIR_xt|LIR_xf)") );
|
|
|
|
|
|
|
|
#ifdef NANOJIT_IA32
|
2008-11-12 18:02:34 -08:00
|
|
|
NanoAssertMsgf(_fpuStkDepth == _sv_fpuStkDepth, "LIR_xtf, _fpuStkDepth=%d, expect %d",_fpuStkDepth, _sv_fpuStkDepth);
|
2008-06-19 10:47:58 -07:00
|
|
|
debug_only( _fpuStkDepth = _sv_fpuStkDepth; _sv_fpuStkDepth = 9999; )
|
|
|
|
#endif
|
|
|
|
|
|
|
|
verbose_only( _verbose = priorVerbose; )
|
|
|
|
verbose_only(_stats.exitnative += (_stats.native-nativeSave));
|
|
|
|
|
2008-12-10 17:31:17 -08:00
|
|
|
return jmpTarget;
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
void Assembler::beginAssembly(Fragment *frag, RegAllocMap* branchStateMap)
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
2009-02-06 11:59:54 -08:00
|
|
|
internalReset();
|
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
_thisfrag = frag;
|
2008-06-19 10:47:58 -07:00
|
|
|
_activation.lowwatermark = 1;
|
|
|
|
_activation.tos = _activation.lowwatermark;
|
|
|
|
_activation.highwatermark = _activation.tos;
|
|
|
|
|
|
|
|
counter_reset(native);
|
|
|
|
counter_reset(exitnative);
|
|
|
|
counter_reset(steals);
|
|
|
|
counter_reset(spills);
|
|
|
|
counter_reset(remats);
|
|
|
|
|
|
|
|
setError(None);
|
|
|
|
|
|
|
|
// native code gen buffer setup
|
|
|
|
nativePageSetup();
|
|
|
|
|
2008-11-14 12:46:35 -08:00
|
|
|
// When outOMem, nIns is set to startingIns and we overwrite the region until the error is handled
|
|
|
|
underrunProtect(LARGEST_UNDERRUN_PROT); // the largest value passed to underrunProtect()
|
|
|
|
_startingIns = _nIns;
|
|
|
|
|
2008-07-15 13:06:05 -07:00
|
|
|
#ifdef AVMPLUS_PORTING_API
|
|
|
|
_endJit2Addr = _nExitIns;
|
|
|
|
#endif
|
|
|
|
|
2008-06-19 10:47:58 -07:00
|
|
|
// make sure we got memory at least one page
|
2008-06-24 15:57:33 -07:00
|
|
|
if (error()) return;
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
#ifdef PERFM
|
|
|
|
_stats.pages = 0;
|
|
|
|
_stats.codeStart = _nIns-1;
|
|
|
|
_stats.codeExitStart = _nExitIns-1;
|
|
|
|
//fprintf(stderr,"pageReset %d start %x exit start %x\n", _stats.pages, (int)_stats.codeStart, (int)_stats.codeExitStart);
|
|
|
|
#endif /* PERFM */
|
|
|
|
|
|
|
|
_epilogue = genEpilogue();
|
2008-06-19 10:47:58 -07:00
|
|
|
_branchStateMap = branchStateMap;
|
2008-10-13 13:29:18 -07:00
|
|
|
_labels.clear();
|
|
|
|
_patches.clear();
|
|
|
|
|
2008-10-20 15:52:11 -07:00
|
|
|
verbose_only( outputAddr=true; )
|
|
|
|
verbose_only( asm_output("[epilogue]"); )
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
|
2008-06-24 15:57:33 -07:00
|
|
|
void Assembler::assemble(Fragment* frag, NInsList& loopJumps)
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
2008-06-24 15:57:33 -07:00
|
|
|
if (error()) return;
|
2008-06-19 10:47:58 -07:00
|
|
|
AvmCore *core = _frago->core();
|
|
|
|
_thisfrag = frag;
|
|
|
|
|
2008-07-08 17:09:53 -07:00
|
|
|
// set up backwards pipeline: assembler -> StackFilter -> LirReader
|
2008-06-19 10:47:58 -07:00
|
|
|
LirReader bufreader(frag->lastIns);
|
2008-11-07 15:52:51 -08:00
|
|
|
avmplus::GC *gc = core->gc;
|
2008-10-13 13:29:18 -07:00
|
|
|
StackFilter storefilter1(&bufreader, gc, frag->lirbuf, frag->lirbuf->sp);
|
|
|
|
StackFilter storefilter2(&storefilter1, gc, frag->lirbuf, frag->lirbuf->rp);
|
|
|
|
DeadCodeFilter deadfilter(&storefilter2, frag->lirbuf->_functions);
|
2008-06-19 10:47:58 -07:00
|
|
|
LirFilter* rdr = &deadfilter;
|
|
|
|
verbose_only(
|
|
|
|
VerboseBlockReader vbr(rdr, this, frag->lirbuf->names);
|
|
|
|
if (verbose_enabled())
|
|
|
|
rdr = &vbr;
|
|
|
|
)
|
|
|
|
|
|
|
|
verbose_only(_thisfrag->compileNbr++; )
|
|
|
|
verbose_only(_frago->_stats.compiles++; )
|
2008-06-24 15:57:33 -07:00
|
|
|
verbose_only(_frago->_stats.totalCompiles++; )
|
2008-10-13 13:29:18 -07:00
|
|
|
_inExit = false;
|
|
|
|
gen(rdr, loopJumps);
|
2008-11-04 14:20:19 -08:00
|
|
|
frag->loopEntry = _nIns;
|
2008-10-21 17:50:32 -07:00
|
|
|
//frag->outbound = core->config.tree_opt? _latestGuard : 0;
|
2008-06-19 10:47:58 -07:00
|
|
|
//fprintf(stderr, "assemble frag %X entry %X\n", (int)frag, (int)frag->fragEntry);
|
2008-10-13 13:29:18 -07:00
|
|
|
|
|
|
|
if (!error()) {
|
|
|
|
// patch all branches
|
|
|
|
while(!_patches.isEmpty())
|
|
|
|
{
|
|
|
|
NIns* where = _patches.lastKey();
|
|
|
|
LInsp targ = _patches.removeLast();
|
|
|
|
LabelState *label = _labels.get(targ);
|
|
|
|
NIns* ntarg = label->addr;
|
|
|
|
if (ntarg) {
|
|
|
|
nPatchBranch(where,ntarg);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
_err = UnknownBranch;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-11-14 12:46:35 -08:00
|
|
|
else {
|
|
|
|
_nIns = _startingIns; // in case of failure reset nIns ready for the next assembly run
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
|
2008-06-24 15:57:33 -07:00
|
|
|
void Assembler::endAssembly(Fragment* frag, NInsList& loopJumps)
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
2009-01-28 15:12:31 -08:00
|
|
|
// don't try to patch code if we are in an error state since we might have partially
|
|
|
|
// overwritten the code cache already
|
|
|
|
if (error())
|
|
|
|
return;
|
|
|
|
|
2008-10-22 16:00:08 -07:00
|
|
|
NIns* SOT = 0;
|
|
|
|
if (frag->isRoot()) {
|
2008-11-04 14:20:19 -08:00
|
|
|
SOT = frag->loopEntry;
|
2008-10-22 16:00:08 -07:00
|
|
|
verbose_only( verbose_outputf(" %p:",_nIns); )
|
|
|
|
} else {
|
|
|
|
SOT = frag->root->fragEntry;
|
|
|
|
}
|
|
|
|
AvmAssert(SOT);
|
2008-06-19 10:47:58 -07:00
|
|
|
while(!loopJumps.isEmpty())
|
|
|
|
{
|
|
|
|
NIns* loopJump = (NIns*)loopJumps.removeLast();
|
2008-10-22 16:00:08 -07:00
|
|
|
verbose_only( verbose_outputf("patching %p to %p", loopJump, SOT); )
|
|
|
|
nPatchBranch(loopJump, SOT);
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
|
2008-11-04 14:20:19 -08:00
|
|
|
NIns* fragEntry = 0;
|
|
|
|
|
2008-06-24 15:57:33 -07:00
|
|
|
if (!error())
|
|
|
|
{
|
2008-11-04 14:20:19 -08:00
|
|
|
fragEntry = genPrologue();
|
2008-10-20 15:52:11 -07:00
|
|
|
verbose_only( outputAddr=true; )
|
|
|
|
verbose_only( asm_output("[prologue]"); )
|
2008-06-24 15:57:33 -07:00
|
|
|
}
|
|
|
|
|
2008-06-19 10:47:58 -07:00
|
|
|
// something bad happened?
|
|
|
|
if (!error())
|
|
|
|
{
|
|
|
|
// check for resource leaks
|
|
|
|
debug_only(
|
|
|
|
for(uint32_t i=_activation.lowwatermark;i<_activation.highwatermark; i++) {
|
2008-11-12 18:02:34 -08:00
|
|
|
NanoAssertMsgf(_activation.entry[i] == 0, "frame entry %d wasn't freed",-4*i);
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2008-11-04 14:20:19 -08:00
|
|
|
frag->fragEntry = fragEntry;
|
2008-06-24 15:57:33 -07:00
|
|
|
NIns* code = _nIns;
|
2008-10-13 13:29:18 -07:00
|
|
|
#ifdef PERFM
|
|
|
|
_nvprof("code", codeBytes()); // requires that all pages are released between begin/endAssembly()otherwise we double count
|
|
|
|
#endif
|
2008-06-24 15:57:33 -07:00
|
|
|
// let the fragment manage the pages if we're using trees and there are branches
|
2008-10-13 13:29:18 -07:00
|
|
|
Page* manage = (_frago->core()->config.tree_opt) ? handoverPages() : 0;
|
2008-06-24 15:57:33 -07:00
|
|
|
frag->setCode(code, manage); // root of tree should manage all pages
|
2008-06-19 10:47:58 -07:00
|
|
|
//fprintf(stderr, "endAssembly frag %X entry %X\n", (int)frag, (int)frag->fragEntry);
|
|
|
|
}
|
2008-11-14 12:46:35 -08:00
|
|
|
else
|
|
|
|
{
|
|
|
|
_nIns = _startingIns; // in case of failure reset nIns ready for the next assembly run
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2008-11-12 18:02:34 -08:00
|
|
|
NanoAssertMsgf(error() || _fpuStkDepth == 0,"_fpuStkDepth %d",_fpuStkDepth);
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
internalReset(); // clear the reservation tables and regalloc
|
2008-12-10 17:25:46 -08:00
|
|
|
NanoAssert( !_branchStateMap || _branchStateMap->isEmpty());
|
2008-06-19 10:47:58 -07:00
|
|
|
_branchStateMap = 0;
|
2008-09-02 22:29:23 -07:00
|
|
|
|
|
|
|
#ifdef AVMPLUS_ARM
|
2008-06-19 10:47:58 -07:00
|
|
|
// If we've modified the code, we need to flush so we don't end up trying
|
|
|
|
// to execute junk
|
2008-09-02 22:29:23 -07:00
|
|
|
# if defined(UNDER_CE)
|
2008-06-19 10:47:58 -07:00
|
|
|
FlushInstructionCache(GetCurrentProcess(), NULL, NULL);
|
2009-02-15 18:10:03 -08:00
|
|
|
#elif defined AVMPLUS_SPARC
|
|
|
|
// Clear Instruction Cache
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
Page *p = (i == 0) ? _nativePages : _nativeExitPages;
|
|
|
|
|
|
|
|
Page *first = p;
|
|
|
|
while (p) {
|
|
|
|
if (!p->next || p->next != p+1) {
|
|
|
|
sync_instruction_memory((char *)first, NJ_PAGE_SIZE);
|
|
|
|
first = p->next;
|
|
|
|
}
|
|
|
|
p = p->next;
|
|
|
|
}
|
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
# elif defined(AVMPLUS_UNIX)
|
2008-09-02 22:29:23 -07:00
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
Page *p = (i == 0) ? _nativePages : _nativeExitPages;
|
|
|
|
|
2008-09-02 22:29:23 -07:00
|
|
|
Page *first = p;
|
2008-09-02 22:29:23 -07:00
|
|
|
while (p) {
|
2008-09-02 22:29:23 -07:00
|
|
|
if (!p->next || p->next != p+1) {
|
|
|
|
__clear_cache((char*)first, (char*)(p+1));
|
|
|
|
first = p->next;
|
|
|
|
}
|
2008-09-02 22:29:23 -07:00
|
|
|
p = p->next;
|
2008-07-08 17:09:53 -07:00
|
|
|
}
|
|
|
|
}
|
2008-09-02 22:29:23 -07:00
|
|
|
# endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
# ifdef AVMPLUS_PORTING_API
|
2008-11-14 12:46:35 -08:00
|
|
|
NanoJIT_PortAPI_FlushInstructionCache(_nIns, _startingIns);
|
2008-07-15 13:06:05 -07:00
|
|
|
NanoJIT_PortAPI_FlushInstructionCache(_nExitIns, _endJit2Addr);
|
2008-09-02 22:29:23 -07:00
|
|
|
# endif
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::copyRegisters(RegAlloc* copyTo)
|
|
|
|
{
|
|
|
|
*copyTo = _allocator;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::releaseRegisters()
|
|
|
|
{
|
|
|
|
for (Register r = FirstReg; r <= LastReg; r = nextreg(r))
|
|
|
|
{
|
|
|
|
LIns *i = _allocator.getActive(r);
|
|
|
|
if (i)
|
|
|
|
{
|
|
|
|
// clear reg allocation, preserve stack allocation.
|
|
|
|
Reservation* resv = getresv(i);
|
|
|
|
NanoAssert(resv != 0);
|
|
|
|
_allocator.retire(r);
|
|
|
|
if (r == resv->reg)
|
|
|
|
resv->reg = UnknownReg;
|
|
|
|
|
|
|
|
if (!resv->arIndex && resv->reg == UnknownReg)
|
|
|
|
{
|
|
|
|
reserveFree(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
|
|
|
|
#ifdef PERFM
|
|
|
|
#define countlir_live() _nvprof("lir-live",1)
|
|
|
|
#define countlir_ret() _nvprof("lir-ret",1)
|
|
|
|
#define countlir_alloc() _nvprof("lir-alloc",1)
|
|
|
|
#define countlir_var() _nvprof("lir-var",1)
|
|
|
|
#define countlir_use() _nvprof("lir-use",1)
|
|
|
|
#define countlir_def() _nvprof("lir-def",1)
|
|
|
|
#define countlir_imm() _nvprof("lir-imm",1)
|
|
|
|
#define countlir_param() _nvprof("lir-param",1)
|
|
|
|
#define countlir_cmov() _nvprof("lir-cmov",1)
|
|
|
|
#define countlir_ld() _nvprof("lir-ld",1)
|
|
|
|
#define countlir_ldq() _nvprof("lir-ldq",1)
|
|
|
|
#define countlir_alu() _nvprof("lir-alu",1)
|
|
|
|
#define countlir_qjoin() _nvprof("lir-qjoin",1)
|
|
|
|
#define countlir_qlo() _nvprof("lir-qlo",1)
|
|
|
|
#define countlir_qhi() _nvprof("lir-qhi",1)
|
|
|
|
#define countlir_fpu() _nvprof("lir-fpu",1)
|
|
|
|
#define countlir_st() _nvprof("lir-st",1)
|
|
|
|
#define countlir_stq() _nvprof("lir-stq",1)
|
|
|
|
#define countlir_jmp() _nvprof("lir-jmp",1)
|
|
|
|
#define countlir_jcc() _nvprof("lir-jcc",1)
|
|
|
|
#define countlir_label() _nvprof("lir-label",1)
|
|
|
|
#define countlir_xcc() _nvprof("lir-xcc",1)
|
|
|
|
#define countlir_x() _nvprof("lir-x",1)
|
|
|
|
#define countlir_loop() _nvprof("lir-loop",1)
|
|
|
|
#define countlir_call() _nvprof("lir-call",1)
|
|
|
|
#else
|
|
|
|
#define countlir_live()
|
|
|
|
#define countlir_ret()
|
|
|
|
#define countlir_alloc()
|
|
|
|
#define countlir_var()
|
|
|
|
#define countlir_use()
|
|
|
|
#define countlir_def()
|
|
|
|
#define countlir_imm()
|
|
|
|
#define countlir_param()
|
|
|
|
#define countlir_cmov()
|
|
|
|
#define countlir_ld()
|
|
|
|
#define countlir_ldq()
|
|
|
|
#define countlir_alu()
|
|
|
|
#define countlir_qjoin()
|
|
|
|
#define countlir_qlo()
|
|
|
|
#define countlir_qhi()
|
|
|
|
#define countlir_fpu()
|
|
|
|
#define countlir_st()
|
|
|
|
#define countlir_stq()
|
|
|
|
#define countlir_jmp()
|
|
|
|
#define countlir_jcc()
|
|
|
|
#define countlir_label()
|
|
|
|
#define countlir_xcc()
|
|
|
|
#define countlir_x()
|
|
|
|
#define countlir_loop()
|
|
|
|
#define countlir_call()
|
|
|
|
#endif
|
|
|
|
|
2008-06-24 15:57:33 -07:00
|
|
|
void Assembler::gen(LirFilter* reader, NInsList& loopJumps)
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
2009-02-11 17:40:27 -08:00
|
|
|
// trace must end with LIR_x, LIR_loop, LIR_ret, or LIR_xtbl
|
2009-02-10 11:23:27 -08:00
|
|
|
NanoAssert(reader->pos()->isop(LIR_x) ||
|
|
|
|
reader->pos()->isop(LIR_loop) ||
|
2009-02-11 17:40:27 -08:00
|
|
|
reader->pos()->isop(LIR_ret) ||
|
|
|
|
reader->pos()->isop(LIR_xtbl));
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
for (LInsp ins = reader->read(); ins != 0 && !error(); ins = reader->read())
|
|
|
|
{
|
|
|
|
LOpcode op = ins->opcode();
|
|
|
|
switch(op)
|
|
|
|
{
|
|
|
|
default:
|
2008-11-12 18:02:34 -08:00
|
|
|
NanoAssertMsgf(false, "unsupported LIR instruction: %d (~0x40: %d)", op, op&~LIR64);
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
2009-01-23 13:21:55 -08:00
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
case LIR_live: {
|
|
|
|
countlir_live();
|
|
|
|
pending_lives.add(ins->oprnd1());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case LIR_ret: {
|
|
|
|
countlir_ret();
|
2008-12-11 13:50:55 -08:00
|
|
|
if (_nIns != _epilogue) {
|
|
|
|
JMP(_epilogue);
|
|
|
|
}
|
|
|
|
assignSavedRegs();
|
|
|
|
#ifdef NANOJIT_ARM
|
|
|
|
// the epilogue moves R2 to R0; we may want to do this
|
|
|
|
// after assignSavedRegs
|
|
|
|
findSpecificRegFor(ins->oprnd1(), R2);
|
|
|
|
#else
|
|
|
|
findSpecificRegFor(ins->oprnd1(), retRegs[0]);
|
|
|
|
#endif
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case LIR_fret: {
|
|
|
|
countlir_ret();
|
|
|
|
if (_nIns != _epilogue) {
|
|
|
|
JMP(_epilogue);
|
|
|
|
}
|
|
|
|
assignSavedRegs();
|
|
|
|
#ifdef NANOJIT_IA32
|
|
|
|
findSpecificRegFor(ins->oprnd1(), FST0);
|
|
|
|
#else
|
|
|
|
NanoAssert(false);
|
|
|
|
#endif
|
|
|
|
fpu_pop();
|
2008-10-13 13:29:18 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// allocate some stack space. the value of this instruction
|
|
|
|
// is the address of the stack space.
|
|
|
|
case LIR_alloc: {
|
|
|
|
countlir_alloc();
|
|
|
|
Reservation *resv = getresv(ins);
|
|
|
|
NanoAssert(resv->arIndex != 0);
|
|
|
|
Register r = resv->reg;
|
|
|
|
if (r != UnknownReg) {
|
|
|
|
_allocator.retire(r);
|
|
|
|
resv->reg = UnknownReg;
|
|
|
|
asm_restore(ins, resv, r);
|
|
|
|
}
|
|
|
|
freeRsrcOf(ins, 0);
|
|
|
|
break;
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
case LIR_short:
|
2008-10-20 10:15:07 -07:00
|
|
|
{
|
|
|
|
countlir_imm();
|
|
|
|
asm_short(ins);
|
|
|
|
break;
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
case LIR_int:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_imm();
|
2008-10-20 10:15:07 -07:00
|
|
|
asm_int(ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LIR_quad:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_imm();
|
2008-07-01 14:46:10 -07:00
|
|
|
asm_quad(ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
|
|
|
}
|
2008-08-19 17:19:19 -07:00
|
|
|
#if !defined NANOJIT_64BIT
|
2008-06-19 10:47:58 -07:00
|
|
|
case LIR_callh:
|
|
|
|
{
|
|
|
|
// return result of quad-call in register
|
|
|
|
prepResultReg(ins, rmask(retRegs[1]));
|
|
|
|
// if hi half was used, we must use the call to ensure it happens
|
2008-11-13 09:52:26 -08:00
|
|
|
findSpecificRegFor(ins->oprnd1(), retRegs[0]);
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
|
|
|
}
|
2008-08-19 17:19:19 -07:00
|
|
|
#endif
|
2008-06-19 10:47:58 -07:00
|
|
|
case LIR_param:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_param();
|
2008-10-20 10:15:07 -07:00
|
|
|
asm_param(ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LIR_qlo:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_qlo();
|
2008-10-20 10:15:07 -07:00
|
|
|
asm_qlo(ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
2008-10-20 10:15:07 -07:00
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
case LIR_qhi:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_qhi();
|
2008-10-20 10:15:07 -07:00
|
|
|
asm_qhi(ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
|
|
|
}
|
2008-08-19 17:19:19 -07:00
|
|
|
case LIR_qcmov:
|
2008-06-19 10:47:58 -07:00
|
|
|
case LIR_cmov:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_cmov();
|
2008-10-20 10:15:07 -07:00
|
|
|
asm_cmov(ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
2008-10-20 10:15:07 -07:00
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
case LIR_ld:
|
|
|
|
case LIR_ldc:
|
|
|
|
case LIR_ldcb:
|
2008-10-30 14:17:42 -07:00
|
|
|
case LIR_ldcs:
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_ld();
|
2008-10-20 10:15:07 -07:00
|
|
|
asm_ld(ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LIR_ldq:
|
2008-10-13 13:29:18 -07:00
|
|
|
case LIR_ldqc:
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_ldq();
|
2008-06-19 10:47:58 -07:00
|
|
|
asm_load64(ins);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LIR_neg:
|
|
|
|
case LIR_not:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_alu();
|
2008-10-20 10:15:07 -07:00
|
|
|
asm_neg_not(ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LIR_qjoin:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_qjoin();
|
2008-06-19 10:47:58 -07:00
|
|
|
asm_qjoin(ins);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-08-18 12:32:14 -07:00
|
|
|
#if defined NANOJIT_64BIT
|
|
|
|
case LIR_qiadd:
|
|
|
|
case LIR_qiand:
|
|
|
|
case LIR_qilsh:
|
2008-08-27 16:08:59 -07:00
|
|
|
case LIR_qior:
|
2008-08-18 12:32:14 -07:00
|
|
|
{
|
|
|
|
asm_qbinop(ins);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2008-06-19 10:47:58 -07:00
|
|
|
case LIR_add:
|
2008-10-13 13:29:18 -07:00
|
|
|
case LIR_addp:
|
2008-06-19 10:47:58 -07:00
|
|
|
case LIR_sub:
|
|
|
|
case LIR_mul:
|
|
|
|
case LIR_and:
|
|
|
|
case LIR_or:
|
|
|
|
case LIR_xor:
|
|
|
|
case LIR_lsh:
|
|
|
|
case LIR_rsh:
|
|
|
|
case LIR_ush:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_alu();
|
2008-10-20 10:15:07 -07:00
|
|
|
asm_arith(ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#ifndef NJ_SOFTFLOAT
|
|
|
|
case LIR_fneg:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_fpu();
|
2008-07-01 14:46:10 -07:00
|
|
|
asm_fneg(ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LIR_fadd:
|
|
|
|
case LIR_fsub:
|
|
|
|
case LIR_fmul:
|
|
|
|
case LIR_fdiv:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_fpu();
|
2008-07-01 14:46:10 -07:00
|
|
|
asm_fop(ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LIR_i2f:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_fpu();
|
2008-07-01 14:46:10 -07:00
|
|
|
asm_i2f(ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LIR_u2f:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_fpu();
|
2008-07-01 14:46:10 -07:00
|
|
|
asm_u2f(ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif // NJ_SOFTFLOAT
|
|
|
|
case LIR_st:
|
|
|
|
case LIR_sti:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_st();
|
2008-06-24 15:57:33 -07:00
|
|
|
asm_store32(ins->oprnd1(), ins->immdisp(), ins->oprnd2());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LIR_stq:
|
|
|
|
case LIR_stqi:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_stq();
|
2008-06-19 10:47:58 -07:00
|
|
|
LIns* value = ins->oprnd1();
|
|
|
|
LIns* base = ins->oprnd2();
|
|
|
|
int dr = ins->immdisp();
|
2008-10-13 13:29:18 -07:00
|
|
|
if (value->isop(LIR_qjoin))
|
|
|
|
{
|
2008-06-24 15:57:33 -07:00
|
|
|
// this is correct for little-endian only
|
|
|
|
asm_store32(value->oprnd1(), dr, base);
|
|
|
|
asm_store32(value->oprnd2(), dr+4, base);
|
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
else
|
|
|
|
{
|
2008-06-19 10:47:58 -07:00
|
|
|
asm_store64(value, dr, base);
|
2008-06-24 15:57:33 -07:00
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
|
|
|
|
case LIR_j:
|
|
|
|
{
|
|
|
|
countlir_jmp();
|
|
|
|
LInsp to = ins->getTarget();
|
|
|
|
LabelState *label = _labels.get(to);
|
|
|
|
// the jump is always taken so whatever register state we
|
|
|
|
// have from downstream code, is irrelevant to code before
|
|
|
|
// this jump. so clear it out. we will pick up register
|
|
|
|
// state from the jump target, if we have seen that label.
|
|
|
|
releaseRegisters();
|
|
|
|
if (label && label->addr) {
|
|
|
|
// forward jump - pick up register state from target.
|
|
|
|
unionRegisterState(label->regs);
|
|
|
|
JMP(label->addr);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// backwards jump
|
|
|
|
hasLoop = true;
|
|
|
|
handleLoopCarriedExprs();
|
|
|
|
if (!label) {
|
|
|
|
// save empty register state at loop header
|
|
|
|
_labels.add(to, 0, _allocator);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
intersectRegisterState(label->regs);
|
|
|
|
}
|
|
|
|
JMP(0);
|
|
|
|
_patches.put(_nIns, to);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case LIR_jt:
|
|
|
|
case LIR_jf:
|
|
|
|
{
|
|
|
|
countlir_jcc();
|
|
|
|
LInsp to = ins->getTarget();
|
|
|
|
LIns* cond = ins->oprnd1();
|
|
|
|
LabelState *label = _labels.get(to);
|
|
|
|
if (label && label->addr) {
|
|
|
|
// forward jump to known label. need to merge with label's register state.
|
|
|
|
unionRegisterState(label->regs);
|
2008-11-04 14:18:17 -08:00
|
|
|
asm_branch(op == LIR_jf, cond, label->addr, false);
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// back edge.
|
|
|
|
hasLoop = true;
|
|
|
|
handleLoopCarriedExprs();
|
|
|
|
if (!label) {
|
|
|
|
// evict all registers, most conservative approach.
|
|
|
|
evictRegs(~_allocator.free);
|
|
|
|
_labels.add(to, 0, _allocator);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// evict all registers, most conservative approach.
|
|
|
|
intersectRegisterState(label->regs);
|
|
|
|
}
|
2008-11-04 14:18:17 -08:00
|
|
|
NIns *branch = asm_branch(op == LIR_jf, cond, 0, false);
|
2008-10-13 13:29:18 -07:00
|
|
|
_patches.put(branch,to);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LIR_label:
|
|
|
|
{
|
|
|
|
countlir_label();
|
|
|
|
LabelState *label = _labels.get(ins);
|
|
|
|
if (!label) {
|
|
|
|
// label seen first, normal target of forward jump, save addr & allocator
|
|
|
|
_labels.add(ins, _nIns, _allocator);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// we're at the top of a loop
|
|
|
|
hasLoop = true;
|
|
|
|
NanoAssert(label->addr == 0 && label->regs.isValid());
|
|
|
|
//evictRegs(~_allocator.free);
|
|
|
|
intersectRegisterState(label->regs);
|
|
|
|
label->addr = _nIns;
|
|
|
|
}
|
2008-10-21 11:53:14 -07:00
|
|
|
verbose_only( if (_verbose) { outputAddr=true; asm_output("[%s]", _thisfrag->lirbuf->names->formatRef(ins)); } )
|
2008-10-13 13:29:18 -07:00
|
|
|
break;
|
|
|
|
}
|
2009-01-23 13:21:55 -08:00
|
|
|
case LIR_xbarrier: {
|
|
|
|
break;
|
|
|
|
}
|
2009-02-11 17:40:27 -08:00
|
|
|
#ifdef NANOJIT_IA32
|
|
|
|
case LIR_xtbl: {
|
|
|
|
NIns* exit = asm_exit(ins); // does intersectRegisterState()
|
|
|
|
asm_switch(ins, exit);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
case LIR_xtbl:
|
|
|
|
NanoAssertMsg(0, "Not supported for this architecture");
|
|
|
|
break;
|
|
|
|
#endif
|
2008-10-13 13:29:18 -07:00
|
|
|
case LIR_xt:
|
2008-06-19 10:47:58 -07:00
|
|
|
case LIR_xf:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_xcc();
|
2008-06-19 10:47:58 -07:00
|
|
|
// we only support cmp with guard right now, also assume it is 'close' and only emit the branch
|
2008-10-13 13:29:18 -07:00
|
|
|
NIns* exit = asm_exit(ins); // does intersectRegisterState()
|
2008-06-19 10:47:58 -07:00
|
|
|
LIns* cond = ins->oprnd1();
|
2008-11-04 14:18:17 -08:00
|
|
|
asm_branch(op == LIR_xf, cond, exit, false);
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LIR_x:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_x();
|
2008-06-19 10:47:58 -07:00
|
|
|
verbose_only(verbose_output(""));
|
|
|
|
// generate the side exit branch on the main trace.
|
2008-06-24 15:57:33 -07:00
|
|
|
NIns *exit = asm_exit(ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
JMP( exit );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case LIR_loop:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_loop();
|
2008-10-20 10:15:07 -07:00
|
|
|
asm_loop(ins, loopJumps);
|
2008-10-31 16:48:14 -07:00
|
|
|
assignSavedRegs();
|
|
|
|
assignParamRegs();
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
|
|
|
}
|
2008-10-20 10:15:07 -07:00
|
|
|
|
2008-06-24 15:57:33 -07:00
|
|
|
#ifndef NJ_SOFTFLOAT
|
|
|
|
case LIR_feq:
|
|
|
|
case LIR_fle:
|
|
|
|
case LIR_flt:
|
|
|
|
case LIR_fgt:
|
|
|
|
case LIR_fge:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_fpu();
|
2008-10-20 10:15:07 -07:00
|
|
|
asm_fcond(ins);
|
2008-06-24 15:57:33 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
2008-06-19 10:47:58 -07:00
|
|
|
case LIR_eq:
|
2008-06-30 15:33:41 -07:00
|
|
|
case LIR_ov:
|
|
|
|
case LIR_cs:
|
2008-06-19 10:47:58 -07:00
|
|
|
case LIR_le:
|
|
|
|
case LIR_lt:
|
|
|
|
case LIR_gt:
|
|
|
|
case LIR_ge:
|
|
|
|
case LIR_ult:
|
|
|
|
case LIR_ule:
|
|
|
|
case LIR_ugt:
|
|
|
|
case LIR_uge:
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_alu();
|
2008-10-20 10:15:07 -07:00
|
|
|
asm_cond(ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
break;
|
|
|
|
}
|
2008-10-20 10:15:07 -07:00
|
|
|
|
2008-06-19 10:47:58 -07:00
|
|
|
#ifndef NJ_SOFTFLOAT
|
|
|
|
case LIR_fcall:
|
2008-10-13 13:29:18 -07:00
|
|
|
case LIR_fcalli:
|
2008-08-19 17:19:19 -07:00
|
|
|
#endif
|
|
|
|
#if defined NANOJIT_64BIT
|
|
|
|
case LIR_callh:
|
2008-06-19 10:47:58 -07:00
|
|
|
#endif
|
|
|
|
case LIR_call:
|
2008-10-13 13:29:18 -07:00
|
|
|
case LIR_calli:
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
countlir_call();
|
2008-06-19 10:47:58 -07:00
|
|
|
Register rr = UnknownReg;
|
|
|
|
#ifndef NJ_SOFTFLOAT
|
2008-10-13 13:29:18 -07:00
|
|
|
if ((op&LIR64))
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
// fcall or fcalli
|
|
|
|
Reservation* rR = getresv(ins);
|
2008-07-01 14:46:10 -07:00
|
|
|
rr = asm_prep_fcall(rR, ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
rr = retRegs[0];
|
|
|
|
prepResultReg(ins, rmask(rr));
|
|
|
|
}
|
|
|
|
|
|
|
|
// do this after we've handled the call result, so we dont
|
|
|
|
// force the call result to be spilled unnecessarily.
|
2008-10-13 13:29:18 -07:00
|
|
|
|
|
|
|
evictScratchRegs();
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2008-07-16 14:21:31 -07:00
|
|
|
asm_call(ins);
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-03 16:10:20 -08:00
|
|
|
if (error())
|
|
|
|
return;
|
|
|
|
|
2008-06-19 10:47:58 -07:00
|
|
|
// check that all is well (don't check in exit paths since its more complicated)
|
|
|
|
debug_only( pageValidate(); )
|
|
|
|
debug_only( resourceConsistencyCheck(); )
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-11 17:40:27 -08:00
|
|
|
/*
|
|
|
|
* Write a jump table for the given SwitchInfo and store the table
|
|
|
|
* address in the SwitchInfo. Every entry will initially point to
|
|
|
|
* target.
|
|
|
|
*/
|
|
|
|
void Assembler::emitJumpTable(SwitchInfo* si, NIns* target)
|
|
|
|
{
|
|
|
|
underrunProtect(si->count * sizeof(NIns*) + 20);
|
|
|
|
// Align for platform. The branch should be optimized away and is
|
|
|
|
// required to select the compatible int type.
|
|
|
|
if (sizeof(NIns*) == 8) {
|
|
|
|
_nIns = (NIns*) (uint64(_nIns) & ~7);
|
|
|
|
} else if (sizeof(NIns*) == 4) {
|
|
|
|
_nIns = (NIns*) (uint32(_nIns) & ~3);
|
|
|
|
}
|
|
|
|
for (uint32_t i = 0; i < si->count; ++i) {
|
|
|
|
_nIns = (NIns*) (((uint8*) _nIns) - sizeof(NIns*));
|
|
|
|
*(NIns**) _nIns = target;
|
|
|
|
}
|
|
|
|
si->table = (NIns**) _nIns;
|
|
|
|
}
|
|
|
|
|
2008-10-31 16:48:14 -07:00
|
|
|
void Assembler::assignSavedRegs()
|
2008-10-13 13:29:18 -07:00
|
|
|
{
|
|
|
|
// restore saved regs
|
|
|
|
releaseRegisters();
|
|
|
|
LirBuffer *b = _thisfrag->lirbuf;
|
|
|
|
for (int i=0, n = NumSavedRegs; i < n; i++) {
|
2008-10-31 16:48:14 -07:00
|
|
|
LIns *p = b->savedRegs[i];
|
2008-10-13 13:29:18 -07:00
|
|
|
if (p)
|
|
|
|
findSpecificRegFor(p, savedRegs[p->imm8()]);
|
|
|
|
}
|
2008-07-16 14:21:31 -07:00
|
|
|
}
|
|
|
|
|
2008-10-31 16:48:14 -07:00
|
|
|
void Assembler::reserveSavedRegs()
|
2008-10-13 13:29:18 -07:00
|
|
|
{
|
|
|
|
LirBuffer *b = _thisfrag->lirbuf;
|
|
|
|
for (int i=0, n = NumSavedRegs; i < n; i++) {
|
2008-10-31 16:48:14 -07:00
|
|
|
LIns *p = b->savedRegs[i];
|
2008-10-13 13:29:18 -07:00
|
|
|
if (p)
|
|
|
|
findMemFor(p);
|
|
|
|
}
|
|
|
|
}
|
2008-09-02 22:29:23 -07:00
|
|
|
|
2008-10-31 16:48:14 -07:00
|
|
|
// restore parameter registers
|
|
|
|
void Assembler::assignParamRegs()
|
|
|
|
{
|
|
|
|
LInsp state = _thisfrag->lirbuf->state;
|
|
|
|
if (state)
|
|
|
|
findSpecificRegFor(state, argRegs[state->imm8()]);
|
|
|
|
LInsp param1 = _thisfrag->lirbuf->param1;
|
|
|
|
if (param1)
|
|
|
|
findSpecificRegFor(param1, argRegs[param1->imm8()]);
|
|
|
|
}
|
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
void Assembler::handleLoopCarriedExprs()
|
|
|
|
{
|
|
|
|
// ensure that exprs spanning the loop are marked live at the end of the loop
|
2008-10-31 16:48:14 -07:00
|
|
|
reserveSavedRegs();
|
2008-10-13 13:29:18 -07:00
|
|
|
for (int i=0, n=pending_lives.size(); i < n; i++) {
|
|
|
|
findMemFor(pending_lives[i]);
|
|
|
|
}
|
|
|
|
}
|
2008-09-02 22:29:23 -07:00
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
void Assembler::arFree(uint32_t idx)
|
|
|
|
{
|
|
|
|
AR &ar = _activation;
|
|
|
|
LIns *i = ar.entry[idx];
|
|
|
|
NanoAssert(i != 0);
|
|
|
|
do {
|
|
|
|
ar.entry[idx] = 0;
|
|
|
|
idx--;
|
|
|
|
} while (ar.entry[idx] == i);
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NJ_VERBOSE
|
|
|
|
void Assembler::printActivationState()
|
|
|
|
{
|
|
|
|
bool verbose_activation = false;
|
|
|
|
if (!verbose_activation)
|
|
|
|
return;
|
|
|
|
|
|
|
|
#ifdef NANOJIT_ARM
|
2008-10-20 10:15:07 -07:00
|
|
|
// @todo Why is there here?!? This routine should be indep. of platform
|
2008-06-19 10:47:58 -07:00
|
|
|
verbose_only(
|
|
|
|
if (_verbose) {
|
|
|
|
char* s = &outline[0];
|
|
|
|
memset(s, ' ', 51); s[51] = '\0';
|
|
|
|
s += strlen(s);
|
|
|
|
sprintf(s, " SP ");
|
|
|
|
s += strlen(s);
|
|
|
|
for(uint32_t i=_activation.lowwatermark; i<_activation.tos;i++) {
|
|
|
|
LInsp ins = _activation.entry[i];
|
|
|
|
if (ins && ins !=_activation.entry[i+1]) {
|
|
|
|
sprintf(s, "%d(%s) ", 4*i, _thisfrag->lirbuf->names->formatRef(ins));
|
|
|
|
s += strlen(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
output(&outline[0]);
|
|
|
|
}
|
|
|
|
)
|
|
|
|
#else
|
|
|
|
verbose_only(
|
|
|
|
char* s = &outline[0];
|
|
|
|
if (_verbose) {
|
|
|
|
memset(s, ' ', 51); s[51] = '\0';
|
|
|
|
s += strlen(s);
|
|
|
|
sprintf(s, " ebp ");
|
|
|
|
s += strlen(s);
|
|
|
|
|
|
|
|
for(uint32_t i=_activation.lowwatermark; i<_activation.tos;i++) {
|
|
|
|
LInsp ins = _activation.entry[i];
|
2009-01-23 00:09:27 -08:00
|
|
|
if (ins) {
|
2008-06-19 10:47:58 -07:00
|
|
|
sprintf(s, "%d(%s) ", -4*i,_thisfrag->lirbuf->names->formatRef(ins));
|
|
|
|
s += strlen(s);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
output(&outline[0]);
|
|
|
|
}
|
|
|
|
)
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif
|
2008-10-13 13:29:18 -07:00
|
|
|
|
|
|
|
bool canfit(int32_t size, int32_t loc, AR &ar) {
|
|
|
|
for (int i=0; i < size; i++) {
|
|
|
|
if (ar.entry[loc+stack_direction(i)])
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
uint32_t Assembler::arReserve(LIns* l)
|
|
|
|
{
|
2008-07-16 14:21:31 -07:00
|
|
|
NanoAssert(!l->isTramp());
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
//verbose_only(printActivationState());
|
2008-10-13 13:29:18 -07:00
|
|
|
int32_t size = l->isop(LIR_alloc) ? (l->size()>>2) : l->isQuad() ? 2 : sizeof(intptr_t)>>2;
|
|
|
|
AR &ar = _activation;
|
|
|
|
const int32_t tos = ar.tos;
|
|
|
|
int32_t start = ar.lowwatermark;
|
2008-06-19 10:47:58 -07:00
|
|
|
int32_t i = 0;
|
|
|
|
NanoAssert(start>0);
|
2008-10-13 13:29:18 -07:00
|
|
|
|
|
|
|
if (size == 1) {
|
|
|
|
// easy most common case -- find a hole, or make the frame bigger
|
|
|
|
for (i=start; i < NJ_MAX_STACK_ENTRY; i++) {
|
|
|
|
if (ar.entry[i] == 0) {
|
|
|
|
// found a hole
|
|
|
|
ar.entry[i] = l;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (size == 2) {
|
|
|
|
if ( (start&1)==1 ) start++; // even 8 boundary
|
|
|
|
for (i=start; i < NJ_MAX_STACK_ENTRY; i+=2) {
|
|
|
|
if ( (ar.entry[i+stack_direction(1)] == 0) && (i==tos || (ar.entry[i] == 0)) ) {
|
|
|
|
// found 2 adjacent aligned slots
|
|
|
|
NanoAssert(_activation.entry[i] == 0);
|
|
|
|
NanoAssert(_activation.entry[i+stack_direction(1)] == 0);
|
|
|
|
ar.entry[i] = l;
|
|
|
|
ar.entry[i+stack_direction(1)] = l;
|
|
|
|
break;
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
else {
|
|
|
|
// alloc larger block on 8byte boundary.
|
|
|
|
if (start < size) start = size;
|
|
|
|
if ((start&1)==1) start++;
|
|
|
|
for (i=start; i < NJ_MAX_STACK_ENTRY; i+=2) {
|
|
|
|
if (canfit(size, i, ar)) {
|
|
|
|
// place the entry in the table and mark the instruction with it
|
|
|
|
for (int32_t j=0; j < size; j++) {
|
|
|
|
NanoAssert(_activation.entry[i+stack_direction(j)] == 0);
|
|
|
|
_activation.entry[i+stack_direction(j)] = l;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i >= (int32_t)ar.tos) {
|
|
|
|
ar.tos = ar.highwatermark = i+1;
|
|
|
|
}
|
|
|
|
if (tos+size >= NJ_MAX_STACK_ENTRY) {
|
|
|
|
setError(StackFull);
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
return i;
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
/**
|
|
|
|
* move regs around so the SavedRegs contains the highest priority regs.
|
|
|
|
*/
|
|
|
|
void Assembler::evictScratchRegs()
|
|
|
|
{
|
|
|
|
// find the top GpRegs that are candidates to put in SavedRegs
|
|
|
|
|
|
|
|
// tosave is a binary heap stored in an array. the root is tosave[0],
|
|
|
|
// left child is at i+1, right child is at i+2.
|
|
|
|
|
|
|
|
Register tosave[LastReg-FirstReg+1];
|
|
|
|
int len=0;
|
|
|
|
RegAlloc *regs = &_allocator;
|
|
|
|
for (Register r = FirstReg; r <= LastReg; r = nextreg(r)) {
|
|
|
|
if (rmask(r) & GpRegs) {
|
|
|
|
LIns *i = regs->getActive(r);
|
|
|
|
if (i) {
|
|
|
|
if (canRemat(i)) {
|
|
|
|
evict(r);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
int32_t pri = regs->getPriority(r);
|
|
|
|
// add to heap by adding to end and bubbling up
|
|
|
|
int j = len++;
|
|
|
|
while (j > 0 && pri > regs->getPriority(tosave[j/2])) {
|
|
|
|
tosave[j] = tosave[j/2];
|
|
|
|
j /= 2;
|
|
|
|
}
|
|
|
|
NanoAssert(size_t(j) < sizeof(tosave)/sizeof(tosave[0]));
|
|
|
|
tosave[j] = r;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
// now primap has the live exprs in priority order.
|
|
|
|
// allocate each of the top priority exprs to a SavedReg
|
|
|
|
|
|
|
|
RegisterMask allow = SavedRegs;
|
|
|
|
while (allow && len > 0) {
|
|
|
|
// get the highest priority var
|
|
|
|
Register hi = tosave[0];
|
2008-10-31 12:56:02 -07:00
|
|
|
if (!(rmask(hi) & SavedRegs)) {
|
|
|
|
LIns *i = regs->getActive(hi);
|
|
|
|
Register r = findRegFor(i, allow);
|
|
|
|
allow &= ~rmask(r);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// hi is already in a saved reg, leave it alone.
|
|
|
|
allow &= ~rmask(hi);
|
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
|
|
|
|
// remove from heap by replacing root with end element and bubbling down.
|
|
|
|
if (allow && --len > 0) {
|
|
|
|
Register last = tosave[len];
|
|
|
|
int j = 0;
|
|
|
|
while (j+1 < len) {
|
|
|
|
int child = j+1;
|
|
|
|
if (j+2 < len && regs->getPriority(tosave[j+2]) > regs->getPriority(tosave[j+1]))
|
|
|
|
child++;
|
|
|
|
if (regs->getPriority(last) > regs->getPriority(tosave[child]))
|
|
|
|
break;
|
|
|
|
tosave[j] = tosave[child];
|
|
|
|
j = child;
|
|
|
|
}
|
|
|
|
tosave[j] = last;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now evict everything else.
|
|
|
|
evictRegs(~SavedRegs);
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
void Assembler::evictRegs(RegisterMask regs)
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
|
|
|
// generate code to restore callee saved registers
|
|
|
|
// @todo speed this up
|
2008-10-13 13:29:18 -07:00
|
|
|
for (Register r = FirstReg; r <= LastReg; r = nextreg(r)) {
|
|
|
|
if ((rmask(r) & regs) && _allocator.getActive(r)) {
|
2008-06-19 10:47:58 -07:00
|
|
|
evict(r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Merge the current state of the registers with a previously stored version
|
2008-10-13 13:29:18 -07:00
|
|
|
* current == saved skip
|
|
|
|
* current & saved evict current, keep saved
|
|
|
|
* current & !saved evict current (unionRegisterState would keep)
|
|
|
|
* !current & saved keep saved
|
2008-06-19 10:47:58 -07:00
|
|
|
*/
|
2008-10-13 13:29:18 -07:00
|
|
|
void Assembler::intersectRegisterState(RegAlloc& saved)
|
2008-06-19 10:47:58 -07:00
|
|
|
{
|
|
|
|
// evictions and pops first
|
|
|
|
RegisterMask skip = 0;
|
2008-10-20 15:52:11 -07:00
|
|
|
verbose_only(bool shouldMention=false; )
|
2008-06-19 10:47:58 -07:00
|
|
|
for (Register r=FirstReg; r <= LastReg; r = nextreg(r))
|
|
|
|
{
|
|
|
|
LIns * curins = _allocator.getActive(r);
|
|
|
|
LIns * savedins = saved.getActive(r);
|
|
|
|
if (curins == savedins)
|
|
|
|
{
|
2008-10-20 15:52:11 -07:00
|
|
|
//verbose_only( if (curins) verbose_outputf(" skip %s", regNames[r]); )
|
2008-06-19 10:47:58 -07:00
|
|
|
skip |= rmask(r);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2008-10-13 13:29:18 -07:00
|
|
|
if (curins) {
|
|
|
|
//_nvprof("intersect-evict",1);
|
2008-10-20 15:52:11 -07:00
|
|
|
verbose_only( shouldMention=true; )
|
2008-06-19 10:47:58 -07:00
|
|
|
evict(r);
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
#ifdef NANOJIT_IA32
|
2008-10-20 15:52:11 -07:00
|
|
|
if (savedins && (rmask(r) & x87Regs)) {
|
|
|
|
verbose_only( shouldMention=true; )
|
2008-06-19 10:47:58 -07:00
|
|
|
FSTP(r);
|
2008-10-20 15:52:11 -07:00
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
assignSaved(saved, skip);
|
2008-10-20 15:52:11 -07:00
|
|
|
verbose_only( if (shouldMention) verbose_outputf(" merging registers (intersect) with existing edge"); )
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
/**
|
|
|
|
* Merge the current state of the registers with a previously stored version.
|
|
|
|
*
|
|
|
|
* current == saved skip
|
|
|
|
* current & saved evict current, keep saved
|
|
|
|
* current & !saved keep current (intersectRegisterState would evict)
|
|
|
|
* !current & saved keep saved
|
|
|
|
*/
|
|
|
|
void Assembler::unionRegisterState(RegAlloc& saved)
|
|
|
|
{
|
|
|
|
// evictions and pops first
|
2008-10-20 15:52:11 -07:00
|
|
|
verbose_only(bool shouldMention=false; )
|
2008-10-13 13:29:18 -07:00
|
|
|
RegisterMask skip = 0;
|
|
|
|
for (Register r=FirstReg; r <= LastReg; r = nextreg(r))
|
|
|
|
{
|
|
|
|
LIns * curins = _allocator.getActive(r);
|
|
|
|
LIns * savedins = saved.getActive(r);
|
|
|
|
if (curins == savedins)
|
|
|
|
{
|
2008-10-20 15:52:11 -07:00
|
|
|
//verbose_only( if (curins) verbose_outputf(" skip %s", regNames[r]); )
|
2008-10-13 13:29:18 -07:00
|
|
|
skip |= rmask(r);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (curins && savedins) {
|
|
|
|
//_nvprof("union-evict",1);
|
2008-10-20 15:52:11 -07:00
|
|
|
verbose_only( shouldMention=true; )
|
2008-10-13 13:29:18 -07:00
|
|
|
evict(r);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NANOJIT_IA32
|
|
|
|
if (rmask(r) & x87Regs) {
|
|
|
|
if (savedins) {
|
|
|
|
FSTP(r);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// saved state did not have fpu reg allocated,
|
|
|
|
// so we must evict here to keep x87 stack balanced.
|
|
|
|
evict(r);
|
|
|
|
}
|
2008-10-20 15:52:11 -07:00
|
|
|
verbose_only( shouldMention=true; )
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
assignSaved(saved, skip);
|
2008-10-20 15:52:11 -07:00
|
|
|
verbose_only( if (shouldMention) verbose_outputf(" merging registers (union) with existing edge"); )
|
2008-10-13 13:29:18 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::assignSaved(RegAlloc &saved, RegisterMask skip)
|
|
|
|
{
|
2008-06-19 10:47:58 -07:00
|
|
|
// now reassign mainline registers
|
|
|
|
for (Register r=FirstReg; r <= LastReg; r = nextreg(r))
|
|
|
|
{
|
|
|
|
LIns *i = saved.getActive(r);
|
|
|
|
if (i && !(skip&rmask(r)))
|
|
|
|
findSpecificRegFor(i, r);
|
|
|
|
}
|
|
|
|
debug_only(saved.used = 0); // marker that we are no longer in exit path
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::setCallTable(const CallInfo* functions)
|
|
|
|
{
|
|
|
|
_functions = functions;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NJ_VERBOSE
|
|
|
|
char Assembler::outline[8192];
|
2008-10-20 15:52:11 -07:00
|
|
|
char Assembler::outlineEOL[512];
|
|
|
|
|
|
|
|
void Assembler::outputForEOL(const char* format, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
|
|
outlineEOL[0] = '\0';
|
|
|
|
vsprintf(outlineEOL, format, args);
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
|
|
|
|
void Assembler::outputf(const char* format, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
|
|
|
outline[0] = '\0';
|
|
|
|
vsprintf(outline, format, args);
|
|
|
|
output(outline);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::output(const char* s)
|
|
|
|
{
|
|
|
|
if (_outputCache)
|
|
|
|
{
|
|
|
|
char* str = (char*)_gc->Alloc(strlen(s)+1);
|
|
|
|
strcpy(str, s);
|
|
|
|
_outputCache->add(str);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_frago->core()->console << s << "\n";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Assembler::output_asm(const char* s)
|
|
|
|
{
|
|
|
|
if (!verbose_enabled())
|
|
|
|
return;
|
|
|
|
output(s);
|
|
|
|
}
|
|
|
|
|
|
|
|
char* Assembler::outputAlign(char *s, int col)
|
|
|
|
{
|
|
|
|
int len = strlen(s);
|
|
|
|
int add = ((col-len)>0) ? col-len : 1;
|
|
|
|
memset(&s[len], ' ', add);
|
|
|
|
s[col] = '\0';
|
|
|
|
return &s[col];
|
|
|
|
}
|
|
|
|
#endif // verbose
|
|
|
|
|
|
|
|
#endif /* FEATURE_NANOJIT */
|
|
|
|
|
|
|
|
#if defined(FEATURE_NANOJIT) || defined(NJ_VERBOSE)
|
|
|
|
uint32_t CallInfo::_count_args(uint32_t mask) const
|
|
|
|
{
|
|
|
|
uint32_t argc = 0;
|
|
|
|
uint32_t argt = _argtypes;
|
2008-10-13 13:29:18 -07:00
|
|
|
for (uint32_t i = 0; i < MAXARGS; ++i) {
|
2008-06-19 10:47:58 -07:00
|
|
|
argt >>= 2;
|
2008-11-13 09:52:26 -08:00
|
|
|
if (!argt)
|
|
|
|
break;
|
2008-06-19 10:47:58 -07:00
|
|
|
argc += (argt & mask) != 0;
|
|
|
|
}
|
|
|
|
return argc;
|
|
|
|
}
|
|
|
|
|
2008-07-16 14:21:31 -07:00
|
|
|
uint32_t CallInfo::get_sizes(ArgSize* sizes) const
|
|
|
|
{
|
|
|
|
uint32_t argt = _argtypes;
|
|
|
|
uint32_t argc = 0;
|
2008-10-13 13:29:18 -07:00
|
|
|
for (uint32_t i = 0; i < MAXARGS; i++) {
|
2008-07-16 14:21:31 -07:00
|
|
|
argt >>= 2;
|
|
|
|
ArgSize a = ArgSize(argt&3);
|
|
|
|
#ifdef NJ_SOFTFLOAT
|
|
|
|
if (a == ARGSIZE_F) {
|
|
|
|
sizes[argc++] = ARGSIZE_LO;
|
|
|
|
sizes[argc++] = ARGSIZE_LO;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (a != ARGSIZE_NONE) {
|
|
|
|
sizes[argc++] = a;
|
2008-11-13 09:52:26 -08:00
|
|
|
} else {
|
|
|
|
break;
|
2008-07-16 14:21:31 -07:00
|
|
|
}
|
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
if (isIndirect()) {
|
|
|
|
// add one more arg for indirect call address
|
|
|
|
argc++;
|
|
|
|
}
|
2008-07-16 14:21:31 -07:00
|
|
|
return argc;
|
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
|
|
|
|
void LabelStateMap::add(LIns *label, NIns *addr, RegAlloc ®s) {
|
2008-10-20 15:51:13 -07:00
|
|
|
LabelState *st = NJ_NEW(gc, LabelState)(addr, regs);
|
2008-10-13 13:29:18 -07:00
|
|
|
labels.put(label, st);
|
|
|
|
}
|
|
|
|
|
2008-11-04 19:22:13 -08:00
|
|
|
LabelStateMap::~LabelStateMap() {
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void LabelStateMap::clear() {
|
|
|
|
LabelState *st;
|
|
|
|
|
|
|
|
while (!labels.isEmpty()) {
|
|
|
|
st = labels.removeLast();
|
|
|
|
delete st;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-13 13:29:18 -07:00
|
|
|
LabelState* LabelStateMap::get(LIns *label) {
|
|
|
|
return labels.get(label);
|
|
|
|
}
|
2008-06-19 10:47:58 -07:00
|
|
|
}
|
2008-10-13 13:29:18 -07:00
|
|
|
#endif // FEATURE_NANOJIT
|