mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Landed nanojit in TraceMonkey. This is untested and DEBUG must be off for now since we don't support AVM's String class.
This commit is contained in:
parent
f5d51ff347
commit
5a17de3bca
@ -61,7 +61,7 @@ DEFINES += -DNARCISSUS
|
||||
endif
|
||||
|
||||
# Look in OBJDIR to find jsautocfg.h and jsautokw.h
|
||||
INCLUDES += -I$(OBJDIR)
|
||||
INCLUDES += -I. -Inanojit -I$(OBJDIR)
|
||||
|
||||
ifdef JS_THREADSAFE
|
||||
DEFINES += -DJS_THREADSAFE
|
||||
@ -81,6 +81,8 @@ ifdef JS_HAS_FILE_OBJECT
|
||||
DEFINES += -DJS_HAS_FILE_OBJECT
|
||||
endif
|
||||
|
||||
DEFINES += -DFEATURE_NANOJIT -DAVMPLUS_IA32 -DTRACEMONKEY
|
||||
|
||||
#
|
||||
# XCFLAGS may be set in the environment or on the gmake command line
|
||||
#
|
||||
@ -180,6 +182,17 @@ JS_HFILES = \
|
||||
jsstr.h \
|
||||
jsxdrapi.h \
|
||||
jsxml.h \
|
||||
nanojit/Assembler.h \
|
||||
nanojit/LIR.h \
|
||||
nanojit/NativeARM.h \
|
||||
nanojit/Nativei386.h \
|
||||
nanojit/avmplus.h \
|
||||
nanojit/vm_fops.h \
|
||||
nanojit/Fragmento.h \
|
||||
nanojit/Native.h \
|
||||
nanojit/NativeThumb.h \
|
||||
nanojit/RegAlloc.h \
|
||||
nanojit/nanojit.h \
|
||||
$(NULL)
|
||||
|
||||
API_HFILES = \
|
||||
@ -247,6 +260,12 @@ JS_CPPFILES = \
|
||||
jsxdrapi.cpp \
|
||||
jsxml.cpp \
|
||||
prmjtime.cpp \
|
||||
nanojit/Assembler.cpp \
|
||||
nanojit/Fragmento.cpp \
|
||||
nanojit/LIR.cpp \
|
||||
nanojit/Nativei386.cpp \
|
||||
nanojit/RegAlloc.cpp \
|
||||
nanojit/avmplus.cpp \
|
||||
$(NULL)
|
||||
|
||||
ifdef JS_LIVECONNECT
|
||||
|
1951
js/src/nanojit/Assembler.cpp
Executable file
1951
js/src/nanojit/Assembler.cpp
Executable file
File diff suppressed because it is too large
Load Diff
617
js/src/nanojit/Fragmento.cpp
Normal file
617
js/src/nanojit/Fragmento.cpp
Normal file
@ -0,0 +1,617 @@
|
||||
/* ***** 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"
|
||||
|
||||
namespace nanojit
|
||||
{
|
||||
#ifdef FEATURE_NANOJIT
|
||||
|
||||
using namespace avmplus;
|
||||
|
||||
/**
|
||||
* This is the main control center for creating and managing fragments.
|
||||
*/
|
||||
Fragmento::Fragmento(AvmCore* core) : _allocList(core->GetGC())
|
||||
{
|
||||
_core = core;
|
||||
GC *gc = core->GetGC();
|
||||
_frags = new (gc) FragmentMap(gc, 128);
|
||||
_assm = new (gc) nanojit::Assembler(this);
|
||||
verbose_only( enterCounts = new (gc) BlockHist(gc); )
|
||||
verbose_only( mergeCounts = new (gc) BlockHist(gc); )
|
||||
}
|
||||
|
||||
Fragmento::~Fragmento()
|
||||
{
|
||||
debug_only( clearFrags() );
|
||||
NanoAssert(_stats.freePages == _stats.pages);
|
||||
|
||||
_frags->clear();
|
||||
while( _allocList.size() > 0 )
|
||||
{
|
||||
//fprintf(stderr,"dealloc %x\n", (intptr_t)_allocList.get(_allocList.size()-1));
|
||||
_gcHeap->Free( _allocList.removeLast() );
|
||||
}
|
||||
}
|
||||
|
||||
Page* Fragmento::pageAlloc()
|
||||
{
|
||||
NanoAssert(sizeof(Page) == NJ_PAGE_SIZE);
|
||||
if (!_pageList)
|
||||
pagesGrow(NJ_PAGES); // try to get more mem
|
||||
Page *page = _pageList;
|
||||
if (page)
|
||||
{
|
||||
_pageList = page->next;
|
||||
debug_only(_stats.freePages--;)
|
||||
}
|
||||
//fprintf(stderr, "Fragmento::pageAlloc %X, %d free pages of %d\n", (int)page, _stats.freePages, _stats.pages);
|
||||
debug_only( NanoAssert(pageCount()==_stats.freePages); )
|
||||
return page;
|
||||
}
|
||||
|
||||
void Fragmento::pageFree(Page* page)
|
||||
{
|
||||
//fprintf(stderr, "Fragmento::pageFree %X, %d free pages of %d\n", (int)page, _stats.freePages+1, _stats.pages);
|
||||
|
||||
// link in the page
|
||||
page->next = _pageList;
|
||||
_pageList = page;
|
||||
debug_only(_stats.freePages++;)
|
||||
debug_only( NanoAssert(pageCount()==_stats.freePages); )
|
||||
}
|
||||
|
||||
void Fragmento::pagesGrow(int32_t count)
|
||||
{
|
||||
NanoAssert(!_pageList);
|
||||
MMGC_MEM_TYPE("NanojitFragmentoMem");
|
||||
Page* memory = 0;
|
||||
if (NJ_UNLIMITED_GROWTH || _stats.pages < (uint32_t)NJ_PAGES)
|
||||
{
|
||||
// @todo nastiness that needs a fix'n
|
||||
_gcHeap = _core->GetGC()->GetGCHeap();
|
||||
NanoAssert(NJ_PAGE_SIZE<=_gcHeap->kNativePageSize);
|
||||
|
||||
// convert NJ_PAGES to gc page count
|
||||
int32_t gcpages = (count*NJ_PAGE_SIZE) / _gcHeap->kNativePageSize;
|
||||
MMGC_MEM_TYPE("NanojitMem");
|
||||
memory = (Page*)_gcHeap->Alloc(gcpages);
|
||||
NanoAssert((int*)memory == pageTop(memory));
|
||||
//fprintf(stderr,"head alloc of %d at %x of %d pages using nj page size of %d\n", gcpages, (intptr_t)memory, (intptr_t)_gcHeap->kNativePageSize, NJ_PAGE_SIZE);
|
||||
|
||||
// can't add memory if its not addressable from all locations
|
||||
for(uint32_t i=0; i<_allocList.size(); i++)
|
||||
{
|
||||
Page* a = _allocList.get(i);
|
||||
int32_t delta = (a < memory) ? (intptr_t)memory+(NJ_PAGE_SIZE*(count+1))-(intptr_t)a : (intptr_t)a+(NJ_PAGE_SIZE*(count+1))-(intptr_t)memory;
|
||||
if ( delta > 16777215 )
|
||||
{
|
||||
// can't use this memory
|
||||
_gcHeap->Free(memory);
|
||||
return;
|
||||
}
|
||||
}
|
||||
_allocList.add(memory);
|
||||
|
||||
Page* page = memory;
|
||||
_pageList = page;
|
||||
_stats.pages += count;
|
||||
debug_only(_stats.freePages += count;)
|
||||
while(--count > 0)
|
||||
{
|
||||
Page *next = page + 1;
|
||||
//fprintf(stderr,"Fragmento::pageGrow adding page %x ; %d\n", (intptr_t)page, count);
|
||||
page->next = next;
|
||||
page = next;
|
||||
}
|
||||
page->next = 0;
|
||||
debug_only( NanoAssert(pageCount()==_stats.freePages); )
|
||||
//fprintf(stderr,"Fragmento::pageGrow adding page %x ; %d\n", (intptr_t)page, count);
|
||||
}
|
||||
}
|
||||
|
||||
void Fragmento::clearFrags()
|
||||
{
|
||||
//fprintf(stderr, "Fragmento::clearFrags()\n");
|
||||
|
||||
while (!_frags->isEmpty()) {
|
||||
Fragment *f = _frags->removeLast();
|
||||
f->clear();
|
||||
}
|
||||
|
||||
// reclaim native pages @todo this is to be moved into tree code.
|
||||
_assm->pageReset();
|
||||
|
||||
verbose_only( enterCounts->clear();)
|
||||
verbose_only( mergeCounts->clear();)
|
||||
verbose_only( _flushes++ );
|
||||
}
|
||||
|
||||
Assembler* Fragmento::assm()
|
||||
{
|
||||
return _assm;
|
||||
}
|
||||
|
||||
AvmCore* Fragmento::core()
|
||||
{
|
||||
return _core;
|
||||
}
|
||||
|
||||
Fragment* Fragmento::getLoop(const avmplus::InterpState &is)
|
||||
{
|
||||
Fragment* f = _frags->get(is.ip);
|
||||
if (!f) {
|
||||
f = newFrag(is);
|
||||
_frags->put(is.ip, f);
|
||||
f->anchor = f;
|
||||
f->kind = LoopTrace;
|
||||
f->mergeCounts = new (_core->gc) BlockHist(_core->gc);
|
||||
verbose_only( addLabel(f, "T", _frags->size()); )
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
#ifdef NJ_VERBOSE
|
||||
void Fragmento::addLabel(Fragment *f, const char *prefix, int id)
|
||||
{
|
||||
char fragname[20];
|
||||
sprintf(fragname,"%s%d", prefix, id);
|
||||
labels->add(f, sizeof(Fragment), 0, fragname);
|
||||
}
|
||||
#endif
|
||||
|
||||
Fragment *Fragmento::getMerge(GuardRecord *lr, const avmplus::InterpState &is)
|
||||
{
|
||||
Fragment *anchor = lr->from->anchor;
|
||||
for (Fragment *f = anchor->branches; f != 0; f = f->nextbranch) {
|
||||
if (f->kind == MergeTrace && f->frid == is.ip && f->calldepth == lr->calldepth) {
|
||||
// found existing shared branch on anchor
|
||||
return f;
|
||||
}
|
||||
}
|
||||
|
||||
Fragment *f = newBranch(anchor, is);
|
||||
f->kind = MergeTrace;
|
||||
f->calldepth = lr->calldepth;
|
||||
verbose_only(addLabel(f, "M", ++anchor->mergeid); )
|
||||
return f;
|
||||
}
|
||||
|
||||
Fragment *Fragmento::createBranch(GuardRecord *lr, const avmplus::InterpState &is)
|
||||
{
|
||||
Fragment *from = lr->from;
|
||||
Fragment *f = newBranch(from, is);
|
||||
f->kind = BranchTrace;
|
||||
f->calldepth = lr->calldepth;
|
||||
f->treeBranches = f->anchor->treeBranches;
|
||||
f->anchor->treeBranches = f;
|
||||
verbose_only( labels->add(f, sizeof(Fragment), 0, "-"); );
|
||||
return f;
|
||||
}
|
||||
|
||||
#ifdef NJ_VERBOSE
|
||||
uint32_t Fragmento::pageCount()
|
||||
{
|
||||
uint32_t n = 0;
|
||||
for(Page* page=_pageList; page; page = page->next)
|
||||
n++;
|
||||
return n;
|
||||
}
|
||||
|
||||
void Fragmento::dumpFragStats(Fragment *f, int level, int& size,
|
||||
uint64_t &traceDur, uint64_t &interpDur)
|
||||
{
|
||||
avmplus::String *filep = f->file;
|
||||
if (!filep)
|
||||
filep = _core->k_str[avmplus::kstrconst_emptyString];
|
||||
avmplus::StringNullTerminatedUTF8 file(_core->gc, filep);
|
||||
const char *s = file.c_str();
|
||||
const char *t = strrchr(s,'\\');
|
||||
if (!t) t = strrchr(s,'/');
|
||||
if (t) s = t+1;
|
||||
|
||||
char buf[500];
|
||||
int namewidth = 35;
|
||||
sprintf(buf, "%*c%s %.*s:%d", 1+level, ' ', labels->format(f), namewidth, s, f->line);
|
||||
|
||||
int called = f->hits();
|
||||
if (called >= 0)
|
||||
called += f->_called;
|
||||
else
|
||||
called = -(1<<f->blacklistLevel) - called - 1;
|
||||
|
||||
uint32_t main = f->_native - f->_exitNative;
|
||||
|
||||
char cause[200];
|
||||
if (f->_token && strcmp(f->_token,"loop")==0)
|
||||
sprintf(cause,"%s %d", f->_token, f->xjumpCount);
|
||||
else if (f->_token) {
|
||||
if (f->eot_target) {
|
||||
sprintf(cause,"%s %s", f->_token, labels->format(f->eot_target));
|
||||
} else {
|
||||
strcpy(cause, f->_token);
|
||||
}
|
||||
}
|
||||
else
|
||||
cause[0] = 0;
|
||||
|
||||
FOpcodep ip = f->frid;
|
||||
_assm->outputf("%-*s %7d %6d %6d %6d %4d %9llu %9llu %-12s %s", namewidth, buf,
|
||||
called, f->guardCount, main, f->_native, f->compileNbr, f->traceTicks/1000, f->interpTicks/1000,
|
||||
cause, core()->interp.labels->format(ip));
|
||||
|
||||
size += main;
|
||||
traceDur += f->traceTicks;
|
||||
interpDur += f->interpTicks;
|
||||
|
||||
for (Fragment *x = f->branches; x != 0; x = x->nextbranch)
|
||||
if (x->kind != MergeTrace)
|
||||
dumpFragStats(x,level+1,size,traceDur,interpDur);
|
||||
for (Fragment *x = f->branches; x != 0; x = x->nextbranch)
|
||||
if (x->kind == MergeTrace)
|
||||
dumpFragStats(x,level+1,size,traceDur,interpDur);
|
||||
|
||||
if (f->anchor == f && f->branches != 0) {
|
||||
//_assm->outputf("tree size %d ticks %llu",size,dur);
|
||||
_assm->output("");
|
||||
}
|
||||
}
|
||||
|
||||
class DurData { public:
|
||||
DurData(): frag(0), traceDur(0), interpDur(0), size(0) {}
|
||||
DurData(int): frag(0), traceDur(0), interpDur(0), size(0) {}
|
||||
DurData(Fragment* f, uint64_t td, uint64_t id, int32_t s)
|
||||
: frag(f), traceDur(td), interpDur(id), size(s) {}
|
||||
Fragment* frag;
|
||||
uint64_t traceDur;
|
||||
uint64_t interpDur;
|
||||
int32_t size;
|
||||
};
|
||||
|
||||
void Fragmento::dumpRatio(const char *label, BlockHist *hist)
|
||||
{
|
||||
int total=0, unique=0;
|
||||
for (int i = 0, n=hist->size(); i < n; i++) {
|
||||
const void * id = hist->keyAt(i);
|
||||
int c = hist->get(id);
|
||||
if (c > 1) {
|
||||
//_assm->outputf("%d %X", c, id);
|
||||
unique += 1;
|
||||
}
|
||||
else if (c == 1) {
|
||||
unique += 1;
|
||||
}
|
||||
total += c;
|
||||
}
|
||||
_assm->outputf("%s total %d unique %d ratio %.1f%", label, total, unique, double(total)/unique);
|
||||
}
|
||||
|
||||
void Fragmento::dumpStats()
|
||||
{
|
||||
bool vsave = _assm->_verbose;
|
||||
_assm->_verbose = true;
|
||||
|
||||
_assm->output("");
|
||||
dumpRatio("inline", enterCounts);
|
||||
dumpRatio("merges", mergeCounts);
|
||||
_assm->outputf("abc %d il %d (%.1fx) abc+il %d (%.1fx)",
|
||||
_stats.abcsize, _stats.ilsize, (double)_stats.ilsize/_stats.abcsize,
|
||||
_stats.abcsize + _stats.ilsize,
|
||||
double(_stats.abcsize+_stats.ilsize)/_stats.abcsize);
|
||||
|
||||
int32_t count = _frags->size();
|
||||
int32_t pages = _stats.pages;
|
||||
int32_t free = _stats.freePages;
|
||||
if (!count)
|
||||
{
|
||||
_assm->outputf("No fragments in cache, %d flushes", _flushes);
|
||||
_assm->_verbose = vsave;
|
||||
return;
|
||||
}
|
||||
|
||||
_assm->outputf("\nFragment statistics for %d entries after %d cache flushes of %d pages (%dKB) where %d used and %d free",
|
||||
count, _flushes, pages, pages<<NJ_LOG2_PAGE_SIZE>>10, pages-free,free);
|
||||
_assm->outputf("h=loop header, x=exit trace, L=loop");
|
||||
_assm->output(" location calls guards main native gen T-trace T-interp");
|
||||
|
||||
avmplus::SortedMap<uint64_t, DurData, avmplus::LIST_NonGCObjects> durs(_core->gc);
|
||||
uint64_t totaldur=0;
|
||||
uint64_t totaltrace=0;
|
||||
int totalsize=0;
|
||||
for (int32_t i=0; i<count; i++)
|
||||
{
|
||||
Fragment *f = _frags->at(i);
|
||||
int size = 0;
|
||||
uint64_t traceDur=0, interpDur=0;
|
||||
dumpFragStats(f, 0, size, traceDur, interpDur);
|
||||
uint64_t bothDur = traceDur + interpDur;
|
||||
if (bothDur) {
|
||||
totaltrace += traceDur;
|
||||
totaldur += bothDur;
|
||||
totalsize += size;
|
||||
while (durs.containsKey(bothDur)) bothDur++;
|
||||
DurData d(f, traceDur, interpDur, size);
|
||||
durs.put(bothDur, d);
|
||||
}
|
||||
}
|
||||
_assm->outputf("");
|
||||
_assm->outputf(" trace interp");
|
||||
_assm->outputf("%9lld (%2d%%) %9lld (%2d%%)",
|
||||
totaltrace/1000, int(100.0*totaltrace/totaldur),
|
||||
(totaldur-totaltrace)/1000, int(100.0*(totaldur-totaltrace)/totaldur));
|
||||
_assm->outputf("");
|
||||
_assm->outputf("trace ticks trace interp size");
|
||||
for (int32_t i=durs.size()-1; i >= 0; i--) {
|
||||
uint64_t bothDur = durs.keyAt(i);
|
||||
DurData d = durs.get(bothDur);
|
||||
int size = d.size;
|
||||
_assm->outputf("%-4s %9lld (%2d%%) %9lld (%2d%%) %9lld (%2d%%) %6d (%2d%%)",
|
||||
labels->format(d.frag),
|
||||
bothDur/1000, int(100.0*bothDur/totaldur),
|
||||
d.traceDur/1000, int(100.0*d.traceDur/totaldur),
|
||||
d.interpDur/1000, int(100.0*d.interpDur/totaldur),
|
||||
size, int(100.0*size/totalsize));
|
||||
}
|
||||
|
||||
_assm->_verbose = vsave;
|
||||
|
||||
}
|
||||
|
||||
void Fragmento::countBlock(BlockHist *hist, FOpcodep ip)
|
||||
{
|
||||
int c = hist->count(ip);
|
||||
if (_assm->_verbose)
|
||||
_assm->outputf("++ %s %d", core()->interp.labels->format(ip), c);
|
||||
}
|
||||
|
||||
void Fragmento::countIL(uint32_t il, uint32_t abc)
|
||||
{
|
||||
_stats.ilsize += il;
|
||||
_stats.abcsize += abc;
|
||||
}
|
||||
#endif // NJ_VERBOSE
|
||||
|
||||
//
|
||||
// Fragment
|
||||
//
|
||||
Fragment::Fragment(FragID id) : frid(id)
|
||||
{
|
||||
// Fragment is a gc object which is zero'd by the GC, no need to clear fields
|
||||
}
|
||||
|
||||
void Fragment::addLink(GuardRecord* lnk)
|
||||
{
|
||||
//fprintf(stderr,"addLink %x from %X target %X\n",(int)lnk,(int)lnk->from,(int)lnk->target);
|
||||
lnk->next = _links;
|
||||
_links = lnk;
|
||||
}
|
||||
|
||||
void Fragment::removeLink(GuardRecord* lnk)
|
||||
{
|
||||
GuardRecord* lr = _links;
|
||||
GuardRecord** lrp = &_links;
|
||||
while(lr)
|
||||
{
|
||||
if (lr == lnk)
|
||||
{
|
||||
*lrp = lr->next;
|
||||
lnk->next = 0;
|
||||
break;
|
||||
}
|
||||
lrp = &(lr->next);
|
||||
lr = lr->next;
|
||||
}
|
||||
}
|
||||
|
||||
void Fragment::link(Assembler* assm)
|
||||
{
|
||||
// patch all jumps into this fragment
|
||||
GuardRecord* lr = _links;
|
||||
while (lr)
|
||||
{
|
||||
GuardRecord* next = lr->next;
|
||||
Fragment* from = lr->target;
|
||||
if (from && from->fragEntry) assm->patch(lr);
|
||||
lr = next;
|
||||
}
|
||||
|
||||
// and then patch all jumps leading out
|
||||
lr = outbound;
|
||||
while(lr)
|
||||
{
|
||||
GuardRecord* next = lr->outgoing;
|
||||
Fragment* targ = lr->target;
|
||||
if (targ && targ->fragEntry) assm->patch(lr);
|
||||
lr = next;
|
||||
}
|
||||
}
|
||||
|
||||
void Fragment::unlink(Assembler* assm)
|
||||
{
|
||||
// remove our guards from others' in-bound list, so they don't patch to us
|
||||
GuardRecord* lr = outbound;
|
||||
while (lr)
|
||||
{
|
||||
GuardRecord* next = lr->outgoing;
|
||||
Fragment* targ = lr->target;
|
||||
if (targ) targ->removeLink(lr);
|
||||
verbose_only( lr->gid = 0; )
|
||||
lr = next;
|
||||
}
|
||||
|
||||
// then unpatch all jumps into this fragment
|
||||
lr = _links;
|
||||
while (lr)
|
||||
{
|
||||
GuardRecord* next = lr->next;
|
||||
Fragment* from = lr->target;
|
||||
if (from && from->fragEntry) assm->unpatch(lr);
|
||||
verbose_only( lr->gid = 0; )
|
||||
lr = next;
|
||||
}
|
||||
}
|
||||
|
||||
bool Fragment::hasOnlyTreeLinks()
|
||||
{
|
||||
// check that all incoming links are on the same tree
|
||||
bool isIt = true;
|
||||
GuardRecord *lr = _links;
|
||||
while (lr)
|
||||
{
|
||||
GuardRecord *next = lr->next;
|
||||
NanoAssert(lr->target == this); // def'n of GuardRecord
|
||||
if (lr->from->anchor != anchor)
|
||||
{
|
||||
isIt = false;
|
||||
break;
|
||||
}
|
||||
lr = next;
|
||||
}
|
||||
return isIt;
|
||||
}
|
||||
|
||||
void Fragment::removeIntraLinks()
|
||||
{
|
||||
// should only be called on root of tree
|
||||
NanoAssert(this == anchor);
|
||||
GuardRecord *lr = _links;
|
||||
while (lr)
|
||||
{
|
||||
GuardRecord *next = lr->next;
|
||||
NanoAssert(lr->target == this); // def'n of GuardRecord
|
||||
if (lr->from->anchor == anchor && lr->from->kind != MergeTrace)
|
||||
removeLink(lr);
|
||||
lr = next;
|
||||
}
|
||||
}
|
||||
|
||||
void Fragment::unlinkBranches(Assembler* /*assm*/)
|
||||
{
|
||||
// should only be called on root of tree
|
||||
NanoAssert(this == anchor);
|
||||
Fragment* frag = treeBranches;
|
||||
while(frag)
|
||||
{
|
||||
NanoAssert(frag->kind == BranchTrace && frag->hasOnlyTreeLinks());
|
||||
frag->_links = 0;
|
||||
frag->fragEntry = 0;
|
||||
frag = frag->treeBranches;
|
||||
}
|
||||
}
|
||||
|
||||
void Fragment::linkBranches(Assembler* assm)
|
||||
{
|
||||
// should only be called on root of tree
|
||||
NanoAssert(this == anchor);
|
||||
Fragment* frag = treeBranches;
|
||||
while(frag)
|
||||
{
|
||||
if (frag->fragEntry) frag->link(assm);
|
||||
frag = frag->treeBranches;
|
||||
}
|
||||
}
|
||||
|
||||
void Fragment::blacklist()
|
||||
{
|
||||
blacklistLevel++;
|
||||
_hits = -(1<<blacklistLevel);
|
||||
}
|
||||
|
||||
Fragment *Fragmento::newFrag(const avmplus::InterpState &interp)
|
||||
{
|
||||
FragID frid = interp.ip;
|
||||
GC *gc = _core->gc;
|
||||
Fragment *f = new (gc) Fragment(frid);
|
||||
f->blacklistLevel = 5;
|
||||
#ifdef AVMPLUS_VERBOSE
|
||||
if (interp.f->filename) {
|
||||
f->line = interp.f->linenum;
|
||||
f->file = interp.f->filename;
|
||||
}
|
||||
#endif
|
||||
return f;
|
||||
}
|
||||
|
||||
Fragment *Fragmento::newBranch(Fragment *from, const avmplus::InterpState &interp)
|
||||
{
|
||||
Fragment *f = newFrag(interp);
|
||||
f->anchor = from->anchor;
|
||||
f->mergeCounts = from->anchor->mergeCounts;
|
||||
f->xjumpCount = from->xjumpCount;
|
||||
/*// prepend
|
||||
f->nextbranch = from->branches;
|
||||
from->branches = f;*/
|
||||
// append
|
||||
if (!from->branches) {
|
||||
from->branches = f;
|
||||
} else {
|
||||
Fragment *p = from->branches;
|
||||
while (p->nextbranch != 0)
|
||||
p = p->nextbranch;
|
||||
p->nextbranch = f;
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
void Fragment::clear()
|
||||
{
|
||||
if (lirbuf) {
|
||||
lirbuf->clear();
|
||||
lirbuf = 0;
|
||||
}
|
||||
lastIns = 0;
|
||||
}
|
||||
|
||||
void Fragment::removeExit(Fragment *target)
|
||||
{
|
||||
if (target && target == branches) {
|
||||
branches = branches->nextbranch;
|
||||
// @todo this doesn't seem right : target->clear();
|
||||
} else {
|
||||
for (Fragment *x = branches; x && x->nextbranch; x = x->nextbranch) {
|
||||
if (target == x->nextbranch) {
|
||||
x->nextbranch = x->nextbranch->nextbranch;
|
||||
// @todo this doesn't seem righ : target->clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* FEATURE_NANOJIT */
|
||||
}
|
2058
js/src/nanojit/LIR.cpp
Executable file
2058
js/src/nanojit/LIR.cpp
Executable file
File diff suppressed because it is too large
Load Diff
580
js/src/nanojit/Nativei386.cpp
Normal file
580
js/src/nanojit/Nativei386.cpp
Normal file
@ -0,0 +1,580 @@
|
||||
/* ***** 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 ***** */
|
||||
|
||||
#ifdef _MAC
|
||||
// for MakeDataExecutable
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#endif
|
||||
|
||||
#include "nanojit.h"
|
||||
|
||||
namespace nanojit
|
||||
{
|
||||
#ifdef FEATURE_NANOJIT
|
||||
|
||||
#ifdef NJ_VERBOSE
|
||||
const char *regNames[] = {
|
||||
"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
|
||||
"xmm0","xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7",
|
||||
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7"
|
||||
};
|
||||
#endif
|
||||
|
||||
const Register Assembler::argRegs[] = { ECX, EDX };
|
||||
const Register Assembler::retRegs[] = { EAX, EDX };
|
||||
|
||||
void Assembler::nInit(AvmCore* core)
|
||||
{
|
||||
sse2 = core->use_sse2();
|
||||
// CMOVcc is actually available on most PPro+ chips (except for a few
|
||||
// oddballs like Via C3) but for now tie to SSE2 detection
|
||||
has_cmov = sse2;
|
||||
OSDep::getDate();
|
||||
}
|
||||
|
||||
NIns* Assembler::genPrologue(RegisterMask needSaving)
|
||||
{
|
||||
/**
|
||||
* Prologue
|
||||
*/
|
||||
uint32_t stackNeeded = 4 * _activation.highwatermark;
|
||||
uint32_t savingCount = 0;
|
||||
|
||||
for(Register i=FirstReg; i <= LastReg; i = nextreg(i))
|
||||
if (needSaving&rmask(i))
|
||||
savingCount++;
|
||||
|
||||
// so for alignment purposes we've pushed return addr, fp, and savingCount registers
|
||||
uint32_t stackPushed = 4 * (3+savingCount);
|
||||
uint32_t aligned = alignUp(stackNeeded + stackPushed, NJ_ALIGN_STACK);
|
||||
uint32_t amt = aligned - stackPushed;
|
||||
|
||||
if (amt)
|
||||
SUBi(SP, amt);
|
||||
|
||||
verbose_only( verbose_outputf(" %p:",_nIns); )
|
||||
verbose_only( verbose_output(" patch entry:"); )
|
||||
NIns *patchEntry = _nIns;
|
||||
MR(FP, SP);
|
||||
PUSHr(FP); // push ebp twice to align frame on 8bytes
|
||||
PUSHr(FP);
|
||||
|
||||
for(Register i=FirstReg; i <= LastReg; i = nextreg(i))
|
||||
if (needSaving&rmask(i))
|
||||
PUSHr(i);
|
||||
|
||||
#ifndef DARWIN
|
||||
// dynamically align the stack
|
||||
PUSHr(FP);//fake returnaddr.
|
||||
ANDi(SP, -NJ_ALIGN_STACK);
|
||||
MR(FP,SP);
|
||||
PUSHr(FP);
|
||||
#endif
|
||||
|
||||
return patchEntry;
|
||||
}
|
||||
|
||||
GuardRecord * Assembler::nFragExit(SideExit *exit)
|
||||
{
|
||||
bool trees = _frago->core()->config.tree_opt;
|
||||
Fragment *frag = exit->target;
|
||||
GuardRecord *lr = 0;
|
||||
bool destKnown = (frag && frag->fragEntry);
|
||||
if (destKnown && !trees)
|
||||
{
|
||||
// already exists, emit jump now. no patching required.
|
||||
JMP(frag->fragEntry);
|
||||
lr = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// target doesn't exit yet. emit jump to epilog, and set up to patch later.
|
||||
lr = placeGuardRecord(exit);
|
||||
JMP_long(_epilogue);
|
||||
lr->jmp = _nIns;
|
||||
#if 0
|
||||
// @todo optimization ; is it worth it? It means we can remove the loop over outbound in Fragment.link()
|
||||
// for trees we need the patch entry on the incoming fragment so we can unhook it later if needed
|
||||
if (tress && destKnown)
|
||||
patch(lr);
|
||||
#endif
|
||||
}
|
||||
// first restore ESP from EBP, undoing SUBi(SP,amt) from genPrologue
|
||||
MR(SP,FP);
|
||||
|
||||
|
||||
#ifdef NJ_VERBOSE
|
||||
if (_frago->core()->config.show_stats) {
|
||||
// load EDX (arg1) with Fragment *fromFrag, target fragment
|
||||
// will make use of this when calling fragenter().
|
||||
int fromfrag = int((Fragment*)_thisfrag);
|
||||
LDi(argRegs[1], fromfrag);
|
||||
}
|
||||
#endif
|
||||
|
||||
// return value is GuardRecord*
|
||||
LDi(EAX, int(lr));
|
||||
|
||||
// if/when we patch this exit to jump over to another fragment,
|
||||
// that fragment will need its parameters set up just like ours.
|
||||
LInsp param0 = _thisfrag->param0;
|
||||
Register state = findSpecificRegFor(param0, Register(param0->imm8()));
|
||||
|
||||
// update InterpState
|
||||
|
||||
if (exit->rp_adj)
|
||||
ADDmi((int32_t)offsetof(avmplus::InterpState, rp), state, exit->rp_adj);
|
||||
|
||||
if (exit->sp_adj)
|
||||
ADDmi((int32_t)offsetof(avmplus::InterpState, sp), state, exit->sp_adj);
|
||||
|
||||
if (exit->ip_adj)
|
||||
ADDmi((int32_t)offsetof(avmplus::InterpState, ip), state, exit->ip_adj);
|
||||
|
||||
if (exit->f_adj)
|
||||
ADDmi((int32_t)offsetof(avmplus::InterpState, f), state, exit->f_adj);
|
||||
|
||||
return lr;
|
||||
}
|
||||
|
||||
NIns *Assembler::genEpilogue(RegisterMask restore)
|
||||
{
|
||||
RET();
|
||||
|
||||
#ifndef DARWIN
|
||||
// undo dynamic alignment
|
||||
POP(FP);
|
||||
MR(SP,FP);
|
||||
#endif
|
||||
|
||||
for (Register i=UnknownReg; i >= FirstReg; i = prevreg(i))
|
||||
if (restore&rmask(i)) { POP(i); }
|
||||
|
||||
POP(FP);
|
||||
POP(FP);
|
||||
return _nIns;
|
||||
}
|
||||
|
||||
void Assembler::nArgEmitted(const CallInfo* call, uint32_t stackSlotCount, uint32_t iargs, uint32_t fargs)
|
||||
{
|
||||
// see if we have finished emitting all args. If so then make sure the
|
||||
// new stack pointer is NJ_ALIGN_STACK aligned
|
||||
const uint32_t istack = call->count_iargs();
|
||||
const uint32_t fstack = call->count_args() - istack;
|
||||
//printf("call %s iargs %d fargs %d istack %d fstack %d\n",call->_name,iargs,fargs,istack,fstack);
|
||||
AvmAssert(iargs <= istack);
|
||||
AvmAssert(fargs <= fstack);
|
||||
if (iargs == istack && fargs == fstack)
|
||||
{
|
||||
const int32_t size = 4*stackSlotCount;
|
||||
const int32_t extra = alignUp(size, NJ_ALIGN_STACK) - size;
|
||||
if (extra > 0)
|
||||
SUBi(SP, extra);
|
||||
}
|
||||
}
|
||||
|
||||
void Assembler::nPostCallCleanup(const CallInfo* call)
|
||||
{
|
||||
// must be signed, not unsigned
|
||||
int32_t istack = call->count_iargs();
|
||||
int32_t fstack = call->count_args() - istack;
|
||||
|
||||
istack -= 2; // first 2 4B args are in registers
|
||||
if (istack <= 0)
|
||||
{
|
||||
istack = 0;
|
||||
if (fstack == 0)
|
||||
return; // only using ECX/EDX nothing passed on the stack so no cleanup needed
|
||||
}
|
||||
|
||||
const int32_t size = 4*istack + 8*fstack; // actual stack space used
|
||||
NanoAssert( size > 0 );
|
||||
|
||||
const int32_t extra = alignUp(size, NJ_ALIGN_STACK) - (size);
|
||||
|
||||
// stack re-alignment
|
||||
// only pop our adjustment amount since callee pops args in FASTCALL mode
|
||||
if (extra > 0)
|
||||
{ ADDi(SP, extra); }
|
||||
}
|
||||
|
||||
void Assembler::nMarkExecute(Page* page, int32_t count, bool enable)
|
||||
{
|
||||
#ifdef _MAC
|
||||
MakeDataExecutable(page, count*NJ_PAGE_SIZE);
|
||||
#else
|
||||
(void)page;
|
||||
(void)count;
|
||||
#endif
|
||||
(void)enable;
|
||||
}
|
||||
|
||||
Register Assembler::nRegisterAllocFromSet(int set)
|
||||
{
|
||||
Register r;
|
||||
RegAlloc ®s = _allocator;
|
||||
#ifdef WIN32
|
||||
_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"
|
||||
"movl %%eax, %0\n\t"
|
||||
: "=m"(r) : "m"(set), "m"(regs.free) : "%eax", "memory" );
|
||||
#endif /* WIN32 */
|
||||
return r;
|
||||
}
|
||||
|
||||
void Assembler::nRegisterResetAll(RegAlloc& a)
|
||||
{
|
||||
// add scratch registers to our free list for the allocator
|
||||
a.clear();
|
||||
a.used = 0;
|
||||
a.free = SavedRegs | ScratchRegs;
|
||||
if (!sse2)
|
||||
a.free &= ~XmmRegs;
|
||||
debug_only( a.managed = a.free; )
|
||||
}
|
||||
|
||||
void Assembler::nPatchBranch(NIns* branch, NIns* location)
|
||||
{
|
||||
uint32_t offset = location - branch;
|
||||
if (branch[0] == JMPc)
|
||||
*(uint32_t*)&branch[1] = offset - 5;
|
||||
else
|
||||
*(uint32_t*)&branch[2] = offset - 6;
|
||||
}
|
||||
|
||||
RegisterMask Assembler::hint(LIns* i, RegisterMask allow)
|
||||
{
|
||||
uint32_t op = i->opcode();
|
||||
int prefer = allow;
|
||||
if (op == LIR_call)
|
||||
prefer &= rmask(EAX);
|
||||
else if (op == LIR_param)
|
||||
prefer &= rmask(Register(i->imm8()));
|
||||
else if (op == LIR_callh || op == LIR_rsh && i->oprnd1()->opcode()==LIR_callh)
|
||||
prefer &= rmask(EDX);
|
||||
else if (i->isCmp())
|
||||
prefer &= AllowableFlagRegs;
|
||||
else if (i->isconst())
|
||||
prefer &= ScratchRegs;
|
||||
return (_allocator.free & prefer) ? prefer : allow;
|
||||
}
|
||||
|
||||
void Assembler::asm_qjoin(LIns *ins)
|
||||
{
|
||||
int d = findMemFor(ins);
|
||||
AvmAssert(d);
|
||||
LIns* lo = ins->oprnd1();
|
||||
LIns* hi = ins->oprnd2();
|
||||
|
||||
Reservation *resv = getresv(ins);
|
||||
Register rr = resv->reg;
|
||||
|
||||
if (rr != UnknownReg && (rmask(rr) & FpRegs))
|
||||
evict(rr);
|
||||
|
||||
if (hi->isconst())
|
||||
{
|
||||
STi(FP, d+4, hi->constval());
|
||||
}
|
||||
else
|
||||
{
|
||||
Register r = findRegFor(hi, GpRegs);
|
||||
ST(FP, d+4, r);
|
||||
}
|
||||
|
||||
if (lo->isconst())
|
||||
{
|
||||
STi(FP, d, lo->constval());
|
||||
}
|
||||
else
|
||||
{
|
||||
// okay if r gets recycled.
|
||||
Register r = findRegFor(lo, GpRegs);
|
||||
ST(FP, d, r);
|
||||
}
|
||||
|
||||
freeRsrcOf(ins, false); // if we had a reg in use, emit a ST to flush it to mem
|
||||
}
|
||||
|
||||
void Assembler::asm_restore(LInsp i, Reservation *resv, Register r)
|
||||
{
|
||||
if (i->isconst())
|
||||
{
|
||||
if (!resv->arIndex) {
|
||||
reserveFree(i);
|
||||
}
|
||||
LDi(r, i->constval());
|
||||
}
|
||||
else
|
||||
{
|
||||
int d = findMemFor(i);
|
||||
if (rmask(r) & FpRegs)
|
||||
{
|
||||
if (rmask(r) & XmmRegs) {
|
||||
LDQ(r, d, FP);
|
||||
} else {
|
||||
FLDQ(d, FP);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LD(r, d, FP);
|
||||
}
|
||||
verbose_only(if (_verbose) {
|
||||
outputf(" restore %s", _thisfrag->lirbuf->names->formatRef(i));
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
void Assembler::asm_store32(LIns *value, int dr, LIns *base)
|
||||
{
|
||||
if (value->isconst())
|
||||
{
|
||||
Register rb = findRegFor(base, GpRegs);
|
||||
int c = value->constval();
|
||||
STi(rb, dr, c);
|
||||
}
|
||||
else
|
||||
{
|
||||
// make sure what is in a register
|
||||
Reservation *rA, *rB;
|
||||
findRegFor2(GpRegs, value, rA, base, rB);
|
||||
Register ra = rA->reg;
|
||||
Register rb = rB->reg;
|
||||
ST(rb, dr, ra);
|
||||
}
|
||||
}
|
||||
|
||||
void Assembler::asm_spill(LInsp i, Reservation *resv, bool pop)
|
||||
{
|
||||
(void)i;
|
||||
int d = disp(resv);
|
||||
Register rr = resv->reg;
|
||||
if (d)
|
||||
{
|
||||
// save to spill location
|
||||
if (rmask(rr) & FpRegs)
|
||||
{
|
||||
if (rmask(rr) & XmmRegs) {
|
||||
STQ(d, FP, rr);
|
||||
} else {
|
||||
FSTQ((pop?1:0), d, FP);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ST(FP, d, rr);
|
||||
}
|
||||
verbose_only(if (_verbose) {
|
||||
outputf(" spill %s",_thisfrag->lirbuf->names->formatRef(i));
|
||||
})
|
||||
}
|
||||
else if (pop && (rmask(rr) & x87Regs))
|
||||
{
|
||||
// pop the fpu result since it isn't used
|
||||
FSTP(FST0);
|
||||
}
|
||||
}
|
||||
|
||||
void Assembler::asm_load64(LInsp ins)
|
||||
{
|
||||
LIns* base = ins->oprnd1();
|
||||
int db = ins->oprnd2()->constval();
|
||||
Reservation *resv = getresv(ins);
|
||||
int dr = disp(resv);
|
||||
Register rr = resv->reg;
|
||||
|
||||
if (rr != UnknownReg && rmask(rr) & XmmRegs)
|
||||
{
|
||||
freeRsrcOf(ins, false);
|
||||
Register rb = findRegFor(base, GpRegs);
|
||||
LDQ(rr, db, rb);
|
||||
}
|
||||
else
|
||||
{
|
||||
Register rb = findRegFor(base, GpRegs);
|
||||
resv->reg = UnknownReg;
|
||||
|
||||
// don't use an fpu reg to simply load & store the value.
|
||||
if (dr)
|
||||
asm_mmq(FP, dr, rb, db);
|
||||
|
||||
freeRsrcOf(ins, false);
|
||||
|
||||
if (rr != UnknownReg)
|
||||
{
|
||||
NanoAssert(rmask(rr)&FpRegs);
|
||||
_allocator.retire(rr);
|
||||
FLDQ(db, rb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Assembler::asm_store64(LInsp value, int dr, LInsp base)
|
||||
{
|
||||
if (value->isconstq())
|
||||
{
|
||||
// if a constant 64-bit value just store it now rather than
|
||||
// generating a pointless store/load/store sequence
|
||||
Register rb = findRegFor(base, GpRegs);
|
||||
const int32_t* p = (const int32_t*) (value-2);
|
||||
STi(rb, dr+4, p[1]);
|
||||
STi(rb, dr, p[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (value->isop(LIR_ldq) || value->isop(LIR_qjoin))
|
||||
{
|
||||
// value is 64bit struct or int64_t, or maybe a double.
|
||||
// it may be live in an FPU reg. Either way, don't
|
||||
// put it in an FPU reg just to load & store it.
|
||||
|
||||
// a) if we know it's not a double, this is right.
|
||||
// b) if we guarded that its a double, this store could be on
|
||||
// the side exit, copying a non-double.
|
||||
// c) maybe its a double just being stored. oh well.
|
||||
|
||||
if (sse2) {
|
||||
Register rv = findRegFor(value, XmmRegs);
|
||||
Register rb = findRegFor(base, GpRegs);
|
||||
STQ(dr, rb, rv);
|
||||
return;
|
||||
}
|
||||
|
||||
int da = findMemFor(value);
|
||||
Register rb = findRegFor(base, GpRegs);
|
||||
asm_mmq(rb, dr, FP, da);
|
||||
return;
|
||||
}
|
||||
|
||||
Reservation* rA = getresv(value);
|
||||
int pop = !rA || rA->reg==UnknownReg;
|
||||
Register rv = findRegFor(value, FpRegs);
|
||||
Register rb = findRegFor(base, GpRegs);
|
||||
|
||||
if (rmask(rv) & XmmRegs) {
|
||||
STQ(dr, rb, rv);
|
||||
} else {
|
||||
FSTQ(pop, dr, rb);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* copy 64 bits: (rd+dd) <- (rs+ds)
|
||||
*/
|
||||
void Assembler::asm_mmq(Register rd, int dd, Register rs, int ds)
|
||||
{
|
||||
// value is either a 64bit struct or maybe a float
|
||||
// that isn't live in an FPU reg. Either way, don't
|
||||
// put it in an FPU reg just to load & store it.
|
||||
if (sse2)
|
||||
{
|
||||
// use SSE to load+store 64bits
|
||||
Register t = registerAlloc(XmmRegs);
|
||||
_allocator.addFree(t);
|
||||
STQ(dd, rd, t);
|
||||
LDQ(t, ds, rs);
|
||||
}
|
||||
else
|
||||
{
|
||||
// get a scratch reg
|
||||
Register t = registerAlloc(GpRegs & ~(rmask(rd)|rmask(rs)));
|
||||
_allocator.addFree(t);
|
||||
ST(rd, dd+4, t);
|
||||
LD(t, ds+4, rs);
|
||||
ST(rd, dd, t);
|
||||
LD(t, ds, rs);
|
||||
}
|
||||
}
|
||||
|
||||
void Assembler::asm_pusharg(LInsp p)
|
||||
{
|
||||
// arg goes on stack
|
||||
Reservation* rA = getresv(p);
|
||||
if (rA == 0)
|
||||
{
|
||||
if (p->isconst())
|
||||
{
|
||||
// small const we push directly
|
||||
PUSHi(p->constval());
|
||||
}
|
||||
else
|
||||
{
|
||||
Register ra = findRegFor(p, GpRegs);
|
||||
PUSHr(ra);
|
||||
}
|
||||
}
|
||||
else if (rA->reg == UnknownReg)
|
||||
{
|
||||
PUSHm(disp(rA), FP);
|
||||
}
|
||||
else
|
||||
{
|
||||
PUSHr(rA->reg);
|
||||
}
|
||||
}
|
||||
|
||||
NIns* Assembler::asm_adjustBranch(NIns* at, NIns* target)
|
||||
{
|
||||
NIns* save = _nIns;
|
||||
NIns* was = (NIns*)( (intptr_t)*(int32_t*)(at+1)+(intptr_t)(at+5) );
|
||||
_nIns = at +5; // +5 is size of JMP
|
||||
intptr_t tt = (intptr_t)target - (intptr_t)_nIns;
|
||||
IMM32(tt);
|
||||
*(--_nIns) = JMPc;
|
||||
_nIns = save;
|
||||
return was;
|
||||
}
|
||||
|
||||
void Assembler::nativePageReset() {}
|
||||
|
||||
void Assembler::nativePageSetup()
|
||||
{
|
||||
if (!_nIns) _nIns = pageAlloc();
|
||||
if (!_nExitIns) _nExitIns = pageAlloc(true);
|
||||
}
|
||||
#endif /* FEATURE_NANOJIT */
|
||||
}
|
182
js/src/nanojit/RegAlloc.cpp
Normal file
182
js/src/nanojit/RegAlloc.cpp
Normal file
@ -0,0 +1,182 @@
|
||||
/* ***** 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"
|
||||
|
||||
namespace nanojit
|
||||
{
|
||||
#ifdef FEATURE_NANOJIT
|
||||
|
||||
/**
|
||||
* Generic register allocation routines.
|
||||
*/
|
||||
void RegAlloc::clear()
|
||||
{
|
||||
free = 0;
|
||||
used = 0;
|
||||
memset(active, 0, NJ_MAX_REGISTERS * sizeof(LIns*));
|
||||
}
|
||||
|
||||
bool RegAlloc::isFree(Register r)
|
||||
{
|
||||
NanoAssert(r != UnknownReg);
|
||||
return (free & rmask(r)) != 0;
|
||||
}
|
||||
|
||||
void RegAlloc::addFree(Register r)
|
||||
{
|
||||
NanoAssert(!isFree(r));
|
||||
free |= rmask(r);
|
||||
}
|
||||
|
||||
void RegAlloc::removeFree(Register r)
|
||||
{
|
||||
NanoAssert(isFree(r));
|
||||
free &= ~rmask(r);
|
||||
}
|
||||
|
||||
void RegAlloc::addActive(Register r, LIns* v)
|
||||
{
|
||||
//addActiveCount++;
|
||||
NanoAssert(v && r != UnknownReg && active[r] == NULL );
|
||||
active[r] = v;
|
||||
}
|
||||
|
||||
void RegAlloc::removeActive(Register r)
|
||||
{
|
||||
//registerReleaseCount++;
|
||||
NanoAssert(r != UnknownReg);
|
||||
NanoAssert(active[r] != NULL);
|
||||
|
||||
// remove the given register from the active list
|
||||
active[r] = NULL;
|
||||
}
|
||||
|
||||
LIns* RegAlloc::getActive(Register r)
|
||||
{
|
||||
NanoAssert(r != UnknownReg);
|
||||
return active[r];
|
||||
}
|
||||
|
||||
void RegAlloc::retire(Register r)
|
||||
{
|
||||
NanoAssert(r != UnknownReg);
|
||||
NanoAssert(active[r] != NULL);
|
||||
active[r] = NULL;
|
||||
free |= rmask(r);
|
||||
}
|
||||
|
||||
// scan table for instruction with longest span
|
||||
LIns* Assembler::findVictim(RegAlloc ®s, RegisterMask allow, RegisterMask prefer)
|
||||
{
|
||||
NanoAssert(allow != 0 && (allow&prefer)==prefer);
|
||||
LIns *i, *a=0, *p = 0;
|
||||
int acost=10, pcost=10;
|
||||
for (Register r=FirstReg; r <= LastReg; r = nextreg(r))
|
||||
{
|
||||
if ((allow & rmask(r)) && (i = regs.getActive(r)) != 0)
|
||||
{
|
||||
int cost = getresv(i)->cost;
|
||||
if (!a || cost < acost || cost == acost && nbr(i) < nbr(a)) {
|
||||
a = i;
|
||||
acost = cost;
|
||||
}
|
||||
if (prefer & rmask(r)) {
|
||||
if (!p || cost < pcost || cost == pcost && nbr(i) < nbr(p)) {
|
||||
p = i;
|
||||
pcost = cost;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return acost < pcost ? a : p;
|
||||
}
|
||||
|
||||
#ifdef NJ_VERBOSE
|
||||
/* static */ void RegAlloc::formatRegisters(RegAlloc& regs, char* s, LirNameMap *names)
|
||||
{
|
||||
for(int i=0; i<NJ_MAX_REGISTERS; i++)
|
||||
{
|
||||
LIns* ins = regs.active[i];
|
||||
Register r = (Register)i;
|
||||
if (ins && regs.isFree(r))
|
||||
{ NanoAssertMsg( 0, "Coding error; register is both free and active! " ); }
|
||||
//if (!ins && !regs.isFree(r))
|
||||
// { NanoAssertMsg( 0, "Coding error; register is not in the free list when it should be" ); }
|
||||
if (!ins)
|
||||
continue;
|
||||
|
||||
s += strlen(s);
|
||||
const char* rname = ins->isQuad() ? fpn(r) : gpn(r);
|
||||
sprintf(s, " %s(%s)", rname, names->formatRef(ins));
|
||||
}
|
||||
}
|
||||
#endif /* NJ_VERBOSE */
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
uint32_t RegAlloc::countFree()
|
||||
{
|
||||
int cnt = 0;
|
||||
for(Register i=FirstReg; i <= LastReg; i = nextreg(i))
|
||||
cnt += isFree(i) ? 1 : 0;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
uint32_t RegAlloc::countActive()
|
||||
{
|
||||
int cnt = 0;
|
||||
for(Register i=FirstReg; i <= LastReg; i = nextreg(i))
|
||||
cnt += active[i] ? 1 : 0;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
void RegAlloc::checkCount()
|
||||
{
|
||||
NanoAssert(count == (countActive() + countFree()));
|
||||
}
|
||||
|
||||
bool RegAlloc::isConsistent(Register r, LIns* i)
|
||||
{
|
||||
NanoAssert(r != UnknownReg);
|
||||
return (isFree(r) && !getActive(r) && !i) ||
|
||||
(!isFree(r) && getActive(r)== i && i );
|
||||
}
|
||||
|
||||
#endif /*DEBUG*/
|
||||
#endif /* FEATURE_NANOJIT */
|
||||
}
|
41
js/src/nanojit/avmplus.cpp
Normal file
41
js/src/nanojit/avmplus.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
/* ***** 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-2006 ] Adobe Systems Incorporated. All Rights
|
||||
* Reserved.
|
||||
*
|
||||
* Contributor(s): Adobe AS3 Team
|
||||
* Andreas Gal <gal@uci.edu>
|
||||
*
|
||||
* 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 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 "avmplus.h"
|
||||
|
||||
using namespace avmplus;
|
||||
|
||||
AvmConfiguration AvmCore::config;
|
||||
static GC _gc;
|
||||
GC* AvmCore::gc = &_gc;
|
||||
GCHeap GC::heap;
|
||||
|
@ -35,6 +35,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "jstypes.h"
|
||||
|
||||
#ifdef _MSC_VER
|
||||
@ -45,21 +46,20 @@
|
||||
|
||||
#define FASTCALL
|
||||
|
||||
//#ifdef DEBUG
|
||||
//#define _DEBUG
|
||||
//#define NJ_VERBOSE
|
||||
//#endif
|
||||
|
||||
#define AvmAssert(x) assert(x)
|
||||
#define AvmAssertMsg(x, y)
|
||||
#define AvmDebugLog(x) printf x
|
||||
|
||||
typedef JSUint8 uint8_t;
|
||||
typedef JSUint16 uint16_t;
|
||||
typedef JSUint32 uint32_t;
|
||||
typedef JSUint64 uint64_t;
|
||||
|
||||
class GC
|
||||
{
|
||||
};
|
||||
|
||||
class GCHeap
|
||||
{
|
||||
};
|
||||
|
||||
class GCObject
|
||||
{
|
||||
};
|
||||
@ -68,20 +68,108 @@ class GCFinalizedObject
|
||||
{
|
||||
};
|
||||
|
||||
class GCHeap
|
||||
{
|
||||
public:
|
||||
uint32_t kNativePageSize;
|
||||
|
||||
GCHeap()
|
||||
{
|
||||
kNativePageSize = 4096; // @todo: what is this?
|
||||
}
|
||||
|
||||
inline void*
|
||||
Alloc(uint32_t pages)
|
||||
{
|
||||
void* p = malloc((pages + 1) * kNativePageSize);
|
||||
p = (void*)(((int)(((char*)p) + kNativePageSize)) & (~0xfff));
|
||||
return p;
|
||||
}
|
||||
|
||||
inline void
|
||||
Free(void* p)
|
||||
{
|
||||
// @todo: don't know how to free
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class GC
|
||||
{
|
||||
static GCHeap heap;
|
||||
|
||||
public:
|
||||
static inline void
|
||||
Free(void* p)
|
||||
{
|
||||
}
|
||||
|
||||
static inline GCHeap*
|
||||
GetGCHeap()
|
||||
{
|
||||
return &heap;
|
||||
}
|
||||
};
|
||||
|
||||
inline void*
|
||||
operator new(size_t size, GC* gc)
|
||||
{
|
||||
return (void*)new char[size];
|
||||
}
|
||||
|
||||
#define DWB(x) x
|
||||
|
||||
#define MMGC_MEM_TYPE(x)
|
||||
|
||||
typedef int FunctionID;
|
||||
|
||||
namespace avmplus
|
||||
{
|
||||
class InterpState
|
||||
{
|
||||
public:
|
||||
void* f;
|
||||
const uint16_t* ip;
|
||||
void* rp;
|
||||
void* sp;
|
||||
};
|
||||
|
||||
class AvmConfiguration
|
||||
{
|
||||
public:
|
||||
AvmConfiguration() {
|
||||
memset(this, 0, sizeof(AvmConfiguration));
|
||||
}
|
||||
|
||||
uint32_t tree_opt:1;
|
||||
};
|
||||
|
||||
class AvmCore
|
||||
{
|
||||
public:
|
||||
static AvmConfiguration config;
|
||||
static GC* gc;
|
||||
|
||||
static inline bool
|
||||
use_sse2()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline GC*
|
||||
GetGC()
|
||||
{
|
||||
return gc;
|
||||
}
|
||||
};
|
||||
|
||||
class OSDep
|
||||
{
|
||||
public:
|
||||
static inline void
|
||||
getDate()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -127,6 +215,17 @@ namespace avmplus
|
||||
delete data;
|
||||
}
|
||||
|
||||
// 'this' steals the guts of 'that' and 'that' gets reset.
|
||||
void FASTCALL become(List& that)
|
||||
{
|
||||
this->destroy();
|
||||
|
||||
this->data = that.data;
|
||||
this->len = that.len;
|
||||
|
||||
that.data = 0;
|
||||
that.len = 0;
|
||||
}
|
||||
uint32_t FASTCALL add(T value)
|
||||
{
|
||||
if (len >= capacity) {
|
||||
|
@ -35,3 +35,5 @@
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
INTERP_FOPCODE_LIST_BEGIN
|
||||
INTERP_FOPCODE_LIST_END
|
||||
|
Loading…
Reference in New Issue
Block a user