gecko/js/src/nanojit/Fragmento.cpp

606 lines
17 KiB
C++
Raw Normal View History

2008-06-30 15:33:41 -07:00
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: t; tab-width: 4 -*- */
/* ***** 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
* Mozilla TraceMonkey Team
* Asko Tontti <atontti@cc.hut.fi>
*
* 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"
#undef MEMORY_INFO
namespace nanojit
{
#ifdef FEATURE_NANOJIT
using namespace avmplus;
2008-07-15 13:06:05 -07:00
static uint32_t calcSaneCacheSize(uint32_t in)
{
if (in < uint32_t(NJ_LOG2_PAGE_SIZE)) return NJ_LOG2_PAGE_SIZE; // at least 1 page
if (in > 30) return 30; // 1GB should be enough for anyone
return in;
}
/**
* This is the main control center for creating and managing fragments.
*/
2008-07-15 13:06:05 -07:00
Fragmento::Fragmento(AvmCore* core, uint32_t cacheSizeLog2)
: _frags(core->GetGC()),
Bug 468484 - LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions, r=danderson. LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions. A new page is allocated LIR_BUF_THRESHOLD instructions prior to reaching the end of page. If the page allocation fails, call to outOmem() will return true. The buffer can still be safely written to during during this period but it is assumed the higher level code will catch this condition and handle it appropriately as writing LIR_BUF_THRESHOLD instructions past this point will cause a crash. This opportunity was also taken to re-factor the code for LirBufWriter making it more platform agnostic. - All non-LInsp data in the instruction stream is now managed through structures that overlay the memory region. - prepFor() was added to replace the multiple ensureReferenceable() calls for each instruction. - insCall() was also modified somewhat in that the arguments are now stored growing downwards from the position of the pseudo instruction LirCallIns. CodegenLIR now has LirBuffer checks at the granularity of each emitXXX() call that is exposed publicly. This seemed like a reasonable approach since a client could potentially call at this level indefinitely. If we want to reduce the frequency of these checks then we'd have to push the check up into the verifier. Assembler OOM handling has also changed. The variable _startingIns was added and contains the location at which the assembler began writing code for the current begin/assem/end sequence. If an OOM condition occurs the assembler will reset the current instruction pointer to _startingIns, effectively overwriting the code that has been generated. This allows the assembler to produce code indefinitely (and without error) until the upper layers have noticed the error and respond accordingly. The constant LARGEST_UNDERRUN_PROT was added and needs to be set to a platform specific value that is equal to or greater than the number of NIns written for the largest possible instruction. i.e. you cannot write more than this number of NIns to the buffer for each call to underrunProtect().
2008-11-14 12:46:35 -08:00
_freePages(core->GetGC(), 1024),
_allocList(core->GetGC()),
_max_pages(1 << (calcSaneCacheSize(cacheSizeLog2) - NJ_LOG2_PAGE_SIZE)),
_pagesGrowth(1)
{
#ifdef _DEBUG
{
// XXX These belong somewhere else, but I can't find the
// right location right now.
NanoStaticAssert((LIR_lt ^ 3) == LIR_ge);
NanoStaticAssert((LIR_le ^ 3) == LIR_gt);
NanoStaticAssert((LIR_ult ^ 3) == LIR_uge);
NanoStaticAssert((LIR_ule ^ 3) == LIR_ugt);
NanoStaticAssert((LIR_flt ^ 3) == LIR_fge);
NanoStaticAssert((LIR_fle ^ 3) == LIR_fgt);
/* Opcodes must be strictly increasing without holes. */
uint32_t count = 0;
#define OPDEF(op, number, operands) \
NanoAssertMsg(LIR_##op == count++, "misnumbered opcode");
#define OPDEF64(op, number, operands) OPDEF(op, number, operands)
#include "LIRopcode.tbl"
#undef OPDEF
#undef OPDEF64
}
#endif
2008-07-01 14:46:10 -07:00
#ifdef MEMORY_INFO
_allocList.set_meminfo_name("Fragmento._allocList");
#endif
NanoAssert(_max_pages > _pagesGrowth); // shrink growth if needed
_core = core;
GC *gc = core->GetGC();
_assm = NJ_NEW(gc, nanojit::Assembler)(this);
verbose_only( enterCounts = NJ_NEW(gc, BlockHist)(gc); )
verbose_only( mergeCounts = NJ_NEW(gc, BlockHist)(gc); )
}
Fragmento::~Fragmento()
{
AllocEntry *entry;
clearFrags();
_frags.clear();
Bug 468484 - LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions, r=danderson. LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions. A new page is allocated LIR_BUF_THRESHOLD instructions prior to reaching the end of page. If the page allocation fails, call to outOmem() will return true. The buffer can still be safely written to during during this period but it is assumed the higher level code will catch this condition and handle it appropriately as writing LIR_BUF_THRESHOLD instructions past this point will cause a crash. This opportunity was also taken to re-factor the code for LirBufWriter making it more platform agnostic. - All non-LInsp data in the instruction stream is now managed through structures that overlay the memory region. - prepFor() was added to replace the multiple ensureReferenceable() calls for each instruction. - insCall() was also modified somewhat in that the arguments are now stored growing downwards from the position of the pseudo instruction LirCallIns. CodegenLIR now has LirBuffer checks at the granularity of each emitXXX() call that is exposed publicly. This seemed like a reasonable approach since a client could potentially call at this level indefinitely. If we want to reduce the frequency of these checks then we'd have to push the check up into the verifier. Assembler OOM handling has also changed. The variable _startingIns was added and contains the location at which the assembler began writing code for the current begin/assem/end sequence. If an OOM condition occurs the assembler will reset the current instruction pointer to _startingIns, effectively overwriting the code that has been generated. This allows the assembler to produce code indefinitely (and without error) until the upper layers have noticed the error and respond accordingly. The constant LARGEST_UNDERRUN_PROT was added and needs to be set to a platform specific value that is equal to or greater than the number of NIns written for the largest possible instruction. i.e. you cannot write more than this number of NIns to the buffer for each call to underrunProtect().
2008-11-14 12:46:35 -08:00
_freePages.clear();
while( _allocList.size() > 0 )
{
//fprintf(stderr,"dealloc %x\n", (intptr_t)_allocList.get(_allocList.size()-1));
2008-07-01 14:46:10 -07:00
#ifdef MEMORY_INFO
ChangeSizeExplicit("NanoJitMem", -1, _gcHeap->Size(_allocList.last()));
#endif
entry = _allocList.removeLast();
_gcHeap->Free( entry->page, entry->allocSize );
NJ_DELETE(entry);
}
NJ_DELETE(_assm);
#if defined(NJ_VERBOSE)
NJ_DELETE(enterCounts);
NJ_DELETE(mergeCounts);
#endif
}
Bug 468484 - LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions, r=danderson. LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions. A new page is allocated LIR_BUF_THRESHOLD instructions prior to reaching the end of page. If the page allocation fails, call to outOmem() will return true. The buffer can still be safely written to during during this period but it is assumed the higher level code will catch this condition and handle it appropriately as writing LIR_BUF_THRESHOLD instructions past this point will cause a crash. This opportunity was also taken to re-factor the code for LirBufWriter making it more platform agnostic. - All non-LInsp data in the instruction stream is now managed through structures that overlay the memory region. - prepFor() was added to replace the multiple ensureReferenceable() calls for each instruction. - insCall() was also modified somewhat in that the arguments are now stored growing downwards from the position of the pseudo instruction LirCallIns. CodegenLIR now has LirBuffer checks at the granularity of each emitXXX() call that is exposed publicly. This seemed like a reasonable approach since a client could potentially call at this level indefinitely. If we want to reduce the frequency of these checks then we'd have to push the check up into the verifier. Assembler OOM handling has also changed. The variable _startingIns was added and contains the location at which the assembler began writing code for the current begin/assem/end sequence. If an OOM condition occurs the assembler will reset the current instruction pointer to _startingIns, effectively overwriting the code that has been generated. This allows the assembler to produce code indefinitely (and without error) until the upper layers have noticed the error and respond accordingly. The constant LARGEST_UNDERRUN_PROT was added and needs to be set to a platform specific value that is equal to or greater than the number of NIns written for the largest possible instruction. i.e. you cannot write more than this number of NIns to the buffer for each call to underrunProtect().
2008-11-14 12:46:35 -08:00
void Fragmento::trackPages()
2008-07-15 13:06:05 -07:00
{
Bug 468484 - LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions, r=danderson. LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions. A new page is allocated LIR_BUF_THRESHOLD instructions prior to reaching the end of page. If the page allocation fails, call to outOmem() will return true. The buffer can still be safely written to during during this period but it is assumed the higher level code will catch this condition and handle it appropriately as writing LIR_BUF_THRESHOLD instructions past this point will cause a crash. This opportunity was also taken to re-factor the code for LirBufWriter making it more platform agnostic. - All non-LInsp data in the instruction stream is now managed through structures that overlay the memory region. - prepFor() was added to replace the multiple ensureReferenceable() calls for each instruction. - insCall() was also modified somewhat in that the arguments are now stored growing downwards from the position of the pseudo instruction LirCallIns. CodegenLIR now has LirBuffer checks at the granularity of each emitXXX() call that is exposed publicly. This seemed like a reasonable approach since a client could potentially call at this level indefinitely. If we want to reduce the frequency of these checks then we'd have to push the check up into the verifier. Assembler OOM handling has also changed. The variable _startingIns was added and contains the location at which the assembler began writing code for the current begin/assem/end sequence. If an OOM condition occurs the assembler will reset the current instruction pointer to _startingIns, effectively overwriting the code that has been generated. This allows the assembler to produce code indefinitely (and without error) until the upper layers have noticed the error and respond accordingly. The constant LARGEST_UNDERRUN_PROT was added and needs to be set to a platform specific value that is equal to or greater than the number of NIns written for the largest possible instruction. i.e. you cannot write more than this number of NIns to the buffer for each call to underrunProtect().
2008-11-14 12:46:35 -08:00
const uint32_t pageUse = _stats.pages - _freePages.size();
2008-07-15 13:06:05 -07:00
if (_stats.maxPageUse < pageUse)
_stats.maxPageUse = pageUse;
}
Page* Fragmento::pageAlloc()
{
NanoAssert(sizeof(Page) == NJ_PAGE_SIZE);
Bug 468484 - LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions, r=danderson. LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions. A new page is allocated LIR_BUF_THRESHOLD instructions prior to reaching the end of page. If the page allocation fails, call to outOmem() will return true. The buffer can still be safely written to during during this period but it is assumed the higher level code will catch this condition and handle it appropriately as writing LIR_BUF_THRESHOLD instructions past this point will cause a crash. This opportunity was also taken to re-factor the code for LirBufWriter making it more platform agnostic. - All non-LInsp data in the instruction stream is now managed through structures that overlay the memory region. - prepFor() was added to replace the multiple ensureReferenceable() calls for each instruction. - insCall() was also modified somewhat in that the arguments are now stored growing downwards from the position of the pseudo instruction LirCallIns. CodegenLIR now has LirBuffer checks at the granularity of each emitXXX() call that is exposed publicly. This seemed like a reasonable approach since a client could potentially call at this level indefinitely. If we want to reduce the frequency of these checks then we'd have to push the check up into the verifier. Assembler OOM handling has also changed. The variable _startingIns was added and contains the location at which the assembler began writing code for the current begin/assem/end sequence. If an OOM condition occurs the assembler will reset the current instruction pointer to _startingIns, effectively overwriting the code that has been generated. This allows the assembler to produce code indefinitely (and without error) until the upper layers have noticed the error and respond accordingly. The constant LARGEST_UNDERRUN_PROT was added and needs to be set to a platform specific value that is equal to or greater than the number of NIns written for the largest possible instruction. i.e. you cannot write more than this number of NIns to the buffer for each call to underrunProtect().
2008-11-14 12:46:35 -08:00
if (!_freePages.size())
pagesGrow(_pagesGrowth); // try to get more mem
if ((_pagesGrowth << 1) < _max_pages)
_pagesGrowth <<= 1;
Bug 468484 - LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions, r=danderson. LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions. A new page is allocated LIR_BUF_THRESHOLD instructions prior to reaching the end of page. If the page allocation fails, call to outOmem() will return true. The buffer can still be safely written to during during this period but it is assumed the higher level code will catch this condition and handle it appropriately as writing LIR_BUF_THRESHOLD instructions past this point will cause a crash. This opportunity was also taken to re-factor the code for LirBufWriter making it more platform agnostic. - All non-LInsp data in the instruction stream is now managed through structures that overlay the memory region. - prepFor() was added to replace the multiple ensureReferenceable() calls for each instruction. - insCall() was also modified somewhat in that the arguments are now stored growing downwards from the position of the pseudo instruction LirCallIns. CodegenLIR now has LirBuffer checks at the granularity of each emitXXX() call that is exposed publicly. This seemed like a reasonable approach since a client could potentially call at this level indefinitely. If we want to reduce the frequency of these checks then we'd have to push the check up into the verifier. Assembler OOM handling has also changed. The variable _startingIns was added and contains the location at which the assembler began writing code for the current begin/assem/end sequence. If an OOM condition occurs the assembler will reset the current instruction pointer to _startingIns, effectively overwriting the code that has been generated. This allows the assembler to produce code indefinitely (and without error) until the upper layers have noticed the error and respond accordingly. The constant LARGEST_UNDERRUN_PROT was added and needs to be set to a platform specific value that is equal to or greater than the number of NIns written for the largest possible instruction. i.e. you cannot write more than this number of NIns to the buffer for each call to underrunProtect().
2008-11-14 12:46:35 -08:00
trackPages();
Page* page = 0;
if (_freePages.size())
page = _freePages.removeLast();
return page;
}
Bug 468484 - LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions, r=danderson. LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions. A new page is allocated LIR_BUF_THRESHOLD instructions prior to reaching the end of page. If the page allocation fails, call to outOmem() will return true. The buffer can still be safely written to during during this period but it is assumed the higher level code will catch this condition and handle it appropriately as writing LIR_BUF_THRESHOLD instructions past this point will cause a crash. This opportunity was also taken to re-factor the code for LirBufWriter making it more platform agnostic. - All non-LInsp data in the instruction stream is now managed through structures that overlay the memory region. - prepFor() was added to replace the multiple ensureReferenceable() calls for each instruction. - insCall() was also modified somewhat in that the arguments are now stored growing downwards from the position of the pseudo instruction LirCallIns. CodegenLIR now has LirBuffer checks at the granularity of each emitXXX() call that is exposed publicly. This seemed like a reasonable approach since a client could potentially call at this level indefinitely. If we want to reduce the frequency of these checks then we'd have to push the check up into the verifier. Assembler OOM handling has also changed. The variable _startingIns was added and contains the location at which the assembler began writing code for the current begin/assem/end sequence. If an OOM condition occurs the assembler will reset the current instruction pointer to _startingIns, effectively overwriting the code that has been generated. This allows the assembler to produce code indefinitely (and without error) until the upper layers have noticed the error and respond accordingly. The constant LARGEST_UNDERRUN_PROT was added and needs to be set to a platform specific value that is equal to or greater than the number of NIns written for the largest possible instruction. i.e. you cannot write more than this number of NIns to the buffer for each call to underrunProtect().
2008-11-14 12:46:35 -08:00
void Fragmento::pagesRelease(PageList& l)
{
Bug 468484 - LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions, r=danderson. LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions. A new page is allocated LIR_BUF_THRESHOLD instructions prior to reaching the end of page. If the page allocation fails, call to outOmem() will return true. The buffer can still be safely written to during during this period but it is assumed the higher level code will catch this condition and handle it appropriately as writing LIR_BUF_THRESHOLD instructions past this point will cause a crash. This opportunity was also taken to re-factor the code for LirBufWriter making it more platform agnostic. - All non-LInsp data in the instruction stream is now managed through structures that overlay the memory region. - prepFor() was added to replace the multiple ensureReferenceable() calls for each instruction. - insCall() was also modified somewhat in that the arguments are now stored growing downwards from the position of the pseudo instruction LirCallIns. CodegenLIR now has LirBuffer checks at the granularity of each emitXXX() call that is exposed publicly. This seemed like a reasonable approach since a client could potentially call at this level indefinitely. If we want to reduce the frequency of these checks then we'd have to push the check up into the verifier. Assembler OOM handling has also changed. The variable _startingIns was added and contains the location at which the assembler began writing code for the current begin/assem/end sequence. If an OOM condition occurs the assembler will reset the current instruction pointer to _startingIns, effectively overwriting the code that has been generated. This allows the assembler to produce code indefinitely (and without error) until the upper layers have noticed the error and respond accordingly. The constant LARGEST_UNDERRUN_PROT was added and needs to be set to a platform specific value that is equal to or greater than the number of NIns written for the largest possible instruction. i.e. you cannot write more than this number of NIns to the buffer for each call to underrunProtect().
2008-11-14 12:46:35 -08:00
_freePages.add(l);
l.clear();
NanoAssert(_freePages.size() <= _stats.pages);
}
void Fragmento::pageFree(Page* page)
{
Bug 468484 - LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions, r=danderson. LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions. A new page is allocated LIR_BUF_THRESHOLD instructions prior to reaching the end of page. If the page allocation fails, call to outOmem() will return true. The buffer can still be safely written to during during this period but it is assumed the higher level code will catch this condition and handle it appropriately as writing LIR_BUF_THRESHOLD instructions past this point will cause a crash. This opportunity was also taken to re-factor the code for LirBufWriter making it more platform agnostic. - All non-LInsp data in the instruction stream is now managed through structures that overlay the memory region. - prepFor() was added to replace the multiple ensureReferenceable() calls for each instruction. - insCall() was also modified somewhat in that the arguments are now stored growing downwards from the position of the pseudo instruction LirCallIns. CodegenLIR now has LirBuffer checks at the granularity of each emitXXX() call that is exposed publicly. This seemed like a reasonable approach since a client could potentially call at this level indefinitely. If we want to reduce the frequency of these checks then we'd have to push the check up into the verifier. Assembler OOM handling has also changed. The variable _startingIns was added and contains the location at which the assembler began writing code for the current begin/assem/end sequence. If an OOM condition occurs the assembler will reset the current instruction pointer to _startingIns, effectively overwriting the code that has been generated. This allows the assembler to produce code indefinitely (and without error) until the upper layers have noticed the error and respond accordingly. The constant LARGEST_UNDERRUN_PROT was added and needs to be set to a platform specific value that is equal to or greater than the number of NIns written for the largest possible instruction. i.e. you cannot write more than this number of NIns to the buffer for each call to underrunProtect().
2008-11-14 12:46:35 -08:00
_freePages.add(page);
NanoAssert(_freePages.size() <= _stats.pages);
}
void Fragmento::pagesGrow(int32_t count)
{
Bug 468484 - LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions, r=danderson. LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions. A new page is allocated LIR_BUF_THRESHOLD instructions prior to reaching the end of page. If the page allocation fails, call to outOmem() will return true. The buffer can still be safely written to during during this period but it is assumed the higher level code will catch this condition and handle it appropriately as writing LIR_BUF_THRESHOLD instructions past this point will cause a crash. This opportunity was also taken to re-factor the code for LirBufWriter making it more platform agnostic. - All non-LInsp data in the instruction stream is now managed through structures that overlay the memory region. - prepFor() was added to replace the multiple ensureReferenceable() calls for each instruction. - insCall() was also modified somewhat in that the arguments are now stored growing downwards from the position of the pseudo instruction LirCallIns. CodegenLIR now has LirBuffer checks at the granularity of each emitXXX() call that is exposed publicly. This seemed like a reasonable approach since a client could potentially call at this level indefinitely. If we want to reduce the frequency of these checks then we'd have to push the check up into the verifier. Assembler OOM handling has also changed. The variable _startingIns was added and contains the location at which the assembler began writing code for the current begin/assem/end sequence. If an OOM condition occurs the assembler will reset the current instruction pointer to _startingIns, effectively overwriting the code that has been generated. This allows the assembler to produce code indefinitely (and without error) until the upper layers have noticed the error and respond accordingly. The constant LARGEST_UNDERRUN_PROT was added and needs to be set to a platform specific value that is equal to or greater than the number of NIns written for the largest possible instruction. i.e. you cannot write more than this number of NIns to the buffer for each call to underrunProtect().
2008-11-14 12:46:35 -08:00
NanoAssert(!_freePages.size());
MMGC_MEM_TYPE("NanojitFragmentoMem");
Page* memory = 0;
Bug 468484 - LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions, r=danderson. LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions. A new page is allocated LIR_BUF_THRESHOLD instructions prior to reaching the end of page. If the page allocation fails, call to outOmem() will return true. The buffer can still be safely written to during during this period but it is assumed the higher level code will catch this condition and handle it appropriately as writing LIR_BUF_THRESHOLD instructions past this point will cause a crash. This opportunity was also taken to re-factor the code for LirBufWriter making it more platform agnostic. - All non-LInsp data in the instruction stream is now managed through structures that overlay the memory region. - prepFor() was added to replace the multiple ensureReferenceable() calls for each instruction. - insCall() was also modified somewhat in that the arguments are now stored growing downwards from the position of the pseudo instruction LirCallIns. CodegenLIR now has LirBuffer checks at the granularity of each emitXXX() call that is exposed publicly. This seemed like a reasonable approach since a client could potentially call at this level indefinitely. If we want to reduce the frequency of these checks then we'd have to push the check up into the verifier. Assembler OOM handling has also changed. The variable _startingIns was added and contains the location at which the assembler began writing code for the current begin/assem/end sequence. If an OOM condition occurs the assembler will reset the current instruction pointer to _startingIns, effectively overwriting the code that has been generated. This allows the assembler to produce code indefinitely (and without error) until the upper layers have noticed the error and respond accordingly. The constant LARGEST_UNDERRUN_PROT was added and needs to be set to a platform specific value that is equal to or greater than the number of NIns written for the largest possible instruction. i.e. you cannot write more than this number of NIns to the buffer for each call to underrunProtect().
2008-11-14 12:46:35 -08:00
GC *gc = _core->GetGC();
2008-07-15 13:06:05 -07:00
if (_stats.pages < _max_pages)
{
AllocEntry *entry;
// make sure we don't grow beyond _max_pages
if (_stats.pages + count > _max_pages)
count = _max_pages - _stats.pages;
if (count < 0)
count = 0;
// @todo nastiness that needs a fix'n
Bug 468484 - LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions, r=danderson. LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions. A new page is allocated LIR_BUF_THRESHOLD instructions prior to reaching the end of page. If the page allocation fails, call to outOmem() will return true. The buffer can still be safely written to during during this period but it is assumed the higher level code will catch this condition and handle it appropriately as writing LIR_BUF_THRESHOLD instructions past this point will cause a crash. This opportunity was also taken to re-factor the code for LirBufWriter making it more platform agnostic. - All non-LInsp data in the instruction stream is now managed through structures that overlay the memory region. - prepFor() was added to replace the multiple ensureReferenceable() calls for each instruction. - insCall() was also modified somewhat in that the arguments are now stored growing downwards from the position of the pseudo instruction LirCallIns. CodegenLIR now has LirBuffer checks at the granularity of each emitXXX() call that is exposed publicly. This seemed like a reasonable approach since a client could potentially call at this level indefinitely. If we want to reduce the frequency of these checks then we'd have to push the check up into the verifier. Assembler OOM handling has also changed. The variable _startingIns was added and contains the location at which the assembler began writing code for the current begin/assem/end sequence. If an OOM condition occurs the assembler will reset the current instruction pointer to _startingIns, effectively overwriting the code that has been generated. This allows the assembler to produce code indefinitely (and without error) until the upper layers have noticed the error and respond accordingly. The constant LARGEST_UNDERRUN_PROT was added and needs to be set to a platform specific value that is equal to or greater than the number of NIns written for the largest possible instruction. i.e. you cannot write more than this number of NIns to the buffer for each call to underrunProtect().
2008-11-14 12:46:35 -08:00
_gcHeap = gc->GetGCHeap();
2008-07-15 13:06:05 -07:00
NanoAssert(int32_t(NJ_PAGE_SIZE)<=_gcHeap->kNativePageSize);
2008-07-15 13:06:05 -07:00
// convert _max_pages to gc page count
int32_t gcpages = (count*NJ_PAGE_SIZE) / _gcHeap->kNativePageSize;
2008-07-15 13:06:05 -07:00
MMGC_MEM_TYPE("NanoJitMem");
memory = (Page*)_gcHeap->Alloc(gcpages);
2008-07-01 14:46:10 -07:00
#ifdef MEMORY_INFO
ChangeSizeExplicit("NanoJitMem", 1, _gcHeap->Size(memory));
#endif
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);
2008-07-16 14:21:31 -07:00
Bug 468484 - LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions, r=danderson. LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions. A new page is allocated LIR_BUF_THRESHOLD instructions prior to reaching the end of page. If the page allocation fails, call to outOmem() will return true. The buffer can still be safely written to during during this period but it is assumed the higher level code will catch this condition and handle it appropriately as writing LIR_BUF_THRESHOLD instructions past this point will cause a crash. This opportunity was also taken to re-factor the code for LirBufWriter making it more platform agnostic. - All non-LInsp data in the instruction stream is now managed through structures that overlay the memory region. - prepFor() was added to replace the multiple ensureReferenceable() calls for each instruction. - insCall() was also modified somewhat in that the arguments are now stored growing downwards from the position of the pseudo instruction LirCallIns. CodegenLIR now has LirBuffer checks at the granularity of each emitXXX() call that is exposed publicly. This seemed like a reasonable approach since a client could potentially call at this level indefinitely. If we want to reduce the frequency of these checks then we'd have to push the check up into the verifier. Assembler OOM handling has also changed. The variable _startingIns was added and contains the location at which the assembler began writing code for the current begin/assem/end sequence. If an OOM condition occurs the assembler will reset the current instruction pointer to _startingIns, effectively overwriting the code that has been generated. This allows the assembler to produce code indefinitely (and without error) until the upper layers have noticed the error and respond accordingly. The constant LARGEST_UNDERRUN_PROT was added and needs to be set to a platform specific value that is equal to or greater than the number of NIns written for the largest possible instruction. i.e. you cannot write more than this number of NIns to the buffer for each call to underrunProtect().
2008-11-14 12:46:35 -08:00
entry = NJ_NEW(gc, AllocEntry);
entry->page = memory;
entry->allocSize = gcpages;
_allocList.add(entry);
_stats.pages += count;
Bug 468484 - LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions, r=danderson. LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions. A new page is allocated LIR_BUF_THRESHOLD instructions prior to reaching the end of page. If the page allocation fails, call to outOmem() will return true. The buffer can still be safely written to during during this period but it is assumed the higher level code will catch this condition and handle it appropriately as writing LIR_BUF_THRESHOLD instructions past this point will cause a crash. This opportunity was also taken to re-factor the code for LirBufWriter making it more platform agnostic. - All non-LInsp data in the instruction stream is now managed through structures that overlay the memory region. - prepFor() was added to replace the multiple ensureReferenceable() calls for each instruction. - insCall() was also modified somewhat in that the arguments are now stored growing downwards from the position of the pseudo instruction LirCallIns. CodegenLIR now has LirBuffer checks at the granularity of each emitXXX() call that is exposed publicly. This seemed like a reasonable approach since a client could potentially call at this level indefinitely. If we want to reduce the frequency of these checks then we'd have to push the check up into the verifier. Assembler OOM handling has also changed. The variable _startingIns was added and contains the location at which the assembler began writing code for the current begin/assem/end sequence. If an OOM condition occurs the assembler will reset the current instruction pointer to _startingIns, effectively overwriting the code that has been generated. This allows the assembler to produce code indefinitely (and without error) until the upper layers have noticed the error and respond accordingly. The constant LARGEST_UNDERRUN_PROT was added and needs to be set to a platform specific value that is equal to or greater than the number of NIns written for the largest possible instruction. i.e. you cannot write more than this number of NIns to the buffer for each call to underrunProtect().
2008-11-14 12:46:35 -08:00
Page* page = memory;
while(--count >= 0)
{
Bug 468484 - LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions, r=danderson. LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions. A new page is allocated LIR_BUF_THRESHOLD instructions prior to reaching the end of page. If the page allocation fails, call to outOmem() will return true. The buffer can still be safely written to during during this period but it is assumed the higher level code will catch this condition and handle it appropriately as writing LIR_BUF_THRESHOLD instructions past this point will cause a crash. This opportunity was also taken to re-factor the code for LirBufWriter making it more platform agnostic. - All non-LInsp data in the instruction stream is now managed through structures that overlay the memory region. - prepFor() was added to replace the multiple ensureReferenceable() calls for each instruction. - insCall() was also modified somewhat in that the arguments are now stored growing downwards from the position of the pseudo instruction LirCallIns. CodegenLIR now has LirBuffer checks at the granularity of each emitXXX() call that is exposed publicly. This seemed like a reasonable approach since a client could potentially call at this level indefinitely. If we want to reduce the frequency of these checks then we'd have to push the check up into the verifier. Assembler OOM handling has also changed. The variable _startingIns was added and contains the location at which the assembler began writing code for the current begin/assem/end sequence. If an OOM condition occurs the assembler will reset the current instruction pointer to _startingIns, effectively overwriting the code that has been generated. This allows the assembler to produce code indefinitely (and without error) until the upper layers have noticed the error and respond accordingly. The constant LARGEST_UNDERRUN_PROT was added and needs to be set to a platform specific value that is equal to or greater than the number of NIns written for the largest possible instruction. i.e. you cannot write more than this number of NIns to the buffer for each call to underrunProtect().
2008-11-14 12:46:35 -08:00
//fprintf(stderr,"Fragmento::pageGrow adding page %x ; %d\n", (unsigned)page, _freePages.size()+1);
_freePages.add(page++);
}
Bug 468484 - LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions, r=danderson. LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions. A new page is allocated LIR_BUF_THRESHOLD instructions prior to reaching the end of page. If the page allocation fails, call to outOmem() will return true. The buffer can still be safely written to during during this period but it is assumed the higher level code will catch this condition and handle it appropriately as writing LIR_BUF_THRESHOLD instructions past this point will cause a crash. This opportunity was also taken to re-factor the code for LirBufWriter making it more platform agnostic. - All non-LInsp data in the instruction stream is now managed through structures that overlay the memory region. - prepFor() was added to replace the multiple ensureReferenceable() calls for each instruction. - insCall() was also modified somewhat in that the arguments are now stored growing downwards from the position of the pseudo instruction LirCallIns. CodegenLIR now has LirBuffer checks at the granularity of each emitXXX() call that is exposed publicly. This seemed like a reasonable approach since a client could potentially call at this level indefinitely. If we want to reduce the frequency of these checks then we'd have to push the check up into the verifier. Assembler OOM handling has also changed. The variable _startingIns was added and contains the location at which the assembler began writing code for the current begin/assem/end sequence. If an OOM condition occurs the assembler will reset the current instruction pointer to _startingIns, effectively overwriting the code that has been generated. This allows the assembler to produce code indefinitely (and without error) until the upper layers have noticed the error and respond accordingly. The constant LARGEST_UNDERRUN_PROT was added and needs to be set to a platform specific value that is equal to or greater than the number of NIns written for the largest possible instruction. i.e. you cannot write more than this number of NIns to the buffer for each call to underrunProtect().
2008-11-14 12:46:35 -08:00
trackPages();
}
}
// Clear the fragment. This *does not* remove the fragment from the
// map--the caller must take care of this.
void Fragmento::clearFragment(Fragment* f)
{
Fragment *peer = f->peer;
while (peer) {
Fragment *next = peer->peer;
peer->releaseTreeMem(this);
NJ_DELETE(peer);
peer = next;
}
f->releaseTreeMem(this);
NJ_DELETE(f);
}
void Fragmento::clearFrag(const void* ip)
{
if (_frags.containsKey(ip)) {
clearFragment(_frags.remove(ip));
}
}
void Fragmento::clearFrags()
{
// reclaim any dangling native pages
_assm->pageReset();
while (!_frags.isEmpty()) {
clearFragment(_frags.removeLast());
}
verbose_only( enterCounts->clear();)
verbose_only( mergeCounts->clear();)
verbose_only( _stats.flushes++ );
verbose_only( _stats.compiles = 0 );
//fprintf(stderr, "Fragmento.clearFrags %d free pages of %d\n", _stats.freePages, _stats.pages);
}
Assembler* Fragmento::assm()
{
return _assm;
}
AvmCore* Fragmento::core()
{
return _core;
}
Fragment* Fragmento::getAnchor(const void* ip)
{
Fragment *f = newFrag(ip);
Fragment *p = _frags.get(ip);
if (p) {
f->first = p;
/* append at the end of the peer list */
Fragment* next;
while ((next = p->peer) != NULL)
p = next;
p->peer = f;
} else {
f->first = f;
_frags.put(ip, f); /* this is the first fragment */
}
f->anchor = f;
f->root = f;
f->kind = LoopTrace;
verbose_only( addLabel(f, "T", _frags.size()); )
return f;
}
2008-07-07 02:47:40 -07:00
Fragment* Fragmento::getLoop(const void* ip)
{
return _frags.get(ip);
}
#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
2008-07-07 02:47:40 -07:00
Fragment *Fragmento::getMerge(GuardRecord *lr, const void* ip)
{
Fragment *anchor = lr->exit->from->anchor;
for (Fragment *f = anchor->branches; f != 0; f = f->nextbranch) {
2008-07-15 13:06:05 -07:00
if (f->kind == MergeTrace && f->ip == ip /*&& f->calldepth == lr->calldepth*/) {
// found existing shared branch on anchor
return f;
}
}
2008-07-07 02:47:40 -07:00
Fragment *f = newBranch(anchor, ip);
f->root = f;
f->kind = MergeTrace;
2008-07-15 13:06:05 -07:00
verbose_only(
int mergeid = 1;
for (Fragment *g = anchor->branches; g != 0; g = g->nextbranch)
if (g->kind == MergeTrace)
mergeid++;
addLabel(f, "M", mergeid);
)
return f;
}
Fragment *Fragmento::createBranch(SideExit* exit, const void* ip)
{
Fragment *f = newBranch(exit->from, ip);
f->kind = BranchTrace;
f->treeBranches = f->root->treeBranches;
f->root->treeBranches = f;
return f;
}
#ifdef NJ_VERBOSE
2008-07-15 13:06:05 -07:00
struct fragstats {
int size;
uint64_t traceDur;
uint64_t interpDur;
int lir, lirbytes;
};
void Fragmento::dumpFragStats(Fragment *f, int level, fragstats &stat)
{
char buf[50];
sprintf(buf, "%*c%s", 1+level, ' ', labels->format(f));
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;
2008-06-30 15:33:41 -07:00
_assm->outputf("%-10s %7d %6d %6d %6d %4d %9llu %9llu %-12s %s", buf,
called, f->guardCount, main, f->_native, f->compileNbr, f->traceTicks/1000, f->interpTicks/1000,
cause, labels->format(f->ip));
2008-06-30 15:33:41 -07:00
2008-07-15 13:06:05 -07:00
stat.size += main;
stat.traceDur += f->traceTicks;
stat.interpDur += f->interpTicks;
stat.lir += f->_lir;
stat.lirbytes += f->_lirbytes;
for (Fragment *x = f->branches; x != 0; x = x->nextbranch)
if (x->kind != MergeTrace)
2008-07-15 13:06:05 -07:00
dumpFragStats(x,level+1,stat);
for (Fragment *x = f->branches; x != 0; x = x->nextbranch)
if (x->kind == MergeTrace)
2008-07-15 13:06:05 -07:00
dumpFragStats(x,level+1,stat);
if (f->isAnchor() && f->branches != 0) {
_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;
2008-07-15 13:06:05 -07:00
int32_t maxPageUse = _stats.maxPageUse;
Bug 468484 - LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions, r=danderson. LirBuffer has been modified to provide advance warning of out of memory (OOM) conditions. A new page is allocated LIR_BUF_THRESHOLD instructions prior to reaching the end of page. If the page allocation fails, call to outOmem() will return true. The buffer can still be safely written to during during this period but it is assumed the higher level code will catch this condition and handle it appropriately as writing LIR_BUF_THRESHOLD instructions past this point will cause a crash. This opportunity was also taken to re-factor the code for LirBufWriter making it more platform agnostic. - All non-LInsp data in the instruction stream is now managed through structures that overlay the memory region. - prepFor() was added to replace the multiple ensureReferenceable() calls for each instruction. - insCall() was also modified somewhat in that the arguments are now stored growing downwards from the position of the pseudo instruction LirCallIns. CodegenLIR now has LirBuffer checks at the granularity of each emitXXX() call that is exposed publicly. This seemed like a reasonable approach since a client could potentially call at this level indefinitely. If we want to reduce the frequency of these checks then we'd have to push the check up into the verifier. Assembler OOM handling has also changed. The variable _startingIns was added and contains the location at which the assembler began writing code for the current begin/assem/end sequence. If an OOM condition occurs the assembler will reset the current instruction pointer to _startingIns, effectively overwriting the code that has been generated. This allows the assembler to produce code indefinitely (and without error) until the upper layers have noticed the error and respond accordingly. The constant LARGEST_UNDERRUN_PROT was added and needs to be set to a platform specific value that is equal to or greater than the number of NIns written for the largest possible instruction. i.e. you cannot write more than this number of NIns to the buffer for each call to underrunProtect().
2008-11-14 12:46:35 -08:00
int32_t free = _freePages.size();
int32_t flushes = _stats.flushes;
if (!count)
{
_assm->outputf("No fragments in cache, %d flushes", flushes);
_assm->_verbose = vsave;
return;
}
_assm->outputf("\nFragment statistics");
_assm->outputf(" loop trees: %d", count);
_assm->outputf(" flushes: %d", flushes);
_assm->outputf(" compiles: %d / %d", _stats.compiles, _stats.totalCompiles);
_assm->outputf(" used: %dk / %dk", (pages-free)<<(NJ_LOG2_PAGE_SIZE-10), pages<<(NJ_LOG2_PAGE_SIZE-10));
2008-07-15 13:06:05 -07:00
_assm->outputf(" maxPageUse: %dk", (maxPageUse)<<(NJ_LOG2_PAGE_SIZE-10));
_assm->output("\ntrace calls guards main native gen T-trace T-interp");
avmplus::SortedMap<uint64_t, DurData, avmplus::LIST_NonGCObjects> durs(_core->gc);
uint64_t totaldur=0;
2008-07-15 13:06:05 -07:00
fragstats totalstat = { 0,0,0,0,0 };
for (int32_t i=0; i<count; i++)
{
Fragment *f = _frags.at(i);
while (true) {
fragstats stat = { 0,0,0,0,0 };
dumpFragStats(f, 0, stat);
if (stat.lir) {
totalstat.lir += stat.lir;
totalstat.lirbytes += stat.lirbytes;
}
uint64_t bothDur = stat.traceDur + stat.interpDur;
if (bothDur) {
totalstat.interpDur += stat.interpDur;
totalstat.traceDur += stat.traceDur;
totalstat.size += stat.size;
totaldur += bothDur;
while (durs.containsKey(bothDur)) bothDur++;
DurData d(f, stat.traceDur, stat.interpDur, stat.size);
durs.put(bothDur, d);
}
if (!f->peer)
break;
f = f->peer;
2008-07-16 14:21:31 -07:00
}
}
2008-07-15 13:06:05 -07:00
uint64_t totaltrace = totalstat.traceDur;
int totalsize = totalstat.size;
_assm->outputf("");
2008-07-16 14:21:31 -07:00
_assm->outputf("lirbytes %d / lir %d = %.1f bytes/lir", totalstat.lirbytes,
totalstat.lir, double(totalstat.lirbytes)/totalstat.lir);
_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%%) %s",
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),
labels->format(d.frag->ip));
}
_assm->_verbose = vsave;
}
2008-07-07 02:47:40 -07:00
void Fragmento::countBlock(BlockHist *hist, const void* ip)
{
int c = hist->count(ip);
if (_assm->_verbose)
_assm->outputf("++ %s %d", labels->format(ip), c);
}
void Fragmento::countIL(uint32_t il, uint32_t abc)
{
_stats.ilsize += il;
_stats.abcsize += abc;
}
2008-06-30 15:33:41 -07:00
#ifdef AVMPLUS_VERBOSE
2008-07-31 13:28:12 -07:00
void Fragmento::drawTrees(char *fileName) {
2008-06-30 15:33:41 -07:00
drawTraceTrees(this, this->_frags, this->_core, fileName);
}
#endif
#endif // NJ_VERBOSE
//
// Fragment
//
2008-07-07 02:47:40 -07:00
Fragment::Fragment(const void* _ip) : ip(_ip)
{
// Fragment is a gc object which is zero'd by the GC, no need to clear fields
}
Fragment::~Fragment()
{
onDestroy();
NanoAssert(_pages == 0);
}
void Fragment::resetHits()
{
blacklistLevel >>= 1;
_hits = 0;
}
void Fragment::blacklist()
{
blacklistLevel++;
_hits = -(1<<blacklistLevel);
}
2008-07-07 02:47:40 -07:00
Fragment *Fragmento::newFrag(const void* ip)
{
GC *gc = _core->gc;
Fragment *f = NJ_NEW(gc, Fragment)(ip);
f->blacklistLevel = 5;
f->recordAttempts = 0;
return f;
}
2008-07-07 02:47:40 -07:00
Fragment *Fragmento::newBranch(Fragment *from, const void* ip)
{
2008-07-07 02:47:40 -07:00
Fragment *f = newFrag(ip);
f->anchor = from->anchor;
f->root = from->root;
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::releaseLirBuffer()
{
lastIns = 0;
}
void Fragment::releaseCode(Fragmento* frago)
{
_code = 0;
while(_pages)
{
Page* next = _pages->next;
frago->pageFree(_pages);
_pages = next;
}
}
void Fragment::releaseTreeMem(Fragmento* frago)
{
releaseLirBuffer();
releaseCode(frago);
2008-08-18 12:32:14 -07:00
// now do it for all branches
Fragment* branch = branches;
while(branch)
{
Fragment* next = branch->nextbranch;
branch->releaseTreeMem(frago); // @todo safer here to recurse in case we support nested trees
NJ_DELETE(branch);
branch = next;
}
}
#endif /* FEATURE_NANOJIT */
}
2008-06-30 15:33:41 -07:00