/* -*- 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
 *
 * 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"
#include <stdio.h>
#include <ctype.h>

#ifdef PERFM
#include "../vprof/vprof.h"
#endif /* PERFM */

namespace nanojit
{
    using namespace avmplus;
	#ifdef FEATURE_NANOJIT

	const uint8_t operandCount[] = {
#define OPDEF(op, number, operands) \
        operands,
#define OPDEF64(op, number, operands) \
        operands,
#include "LIRopcode.tbl"
#undef OPDEF
#undef OPDEF64
        0
	};

	// LIR verbose specific
	#ifdef NJ_VERBOSE

	const char* lirNames[] = {
#define OPDEF(op, number, operands) \
        #op,
#define OPDEF64(op, number, operands) \
        #op,
#include "LIRopcode.tbl"
#undef OPDEF
#undef OPDEF64
        NULL
	};

	#endif /* NANOJIT_VEBROSE */
	
	// implementation

#ifdef NJ_PROFILE
	// @todo fixup move to nanojit.h
	#undef counter_value
	#define counter_value(x)		x
#endif /* NJ_PROFILE */

	//static int32_t buffer_count = 0;
	
	// LCompressedBuffer
	LirBuffer::LirBuffer(Fragmento* frago, const CallInfo* functions)
		: _frago(frago),
#ifdef NJ_VERBOSE
		  names(NULL),
#endif
		  _functions(functions), abi(ABI_FASTCALL),
		  state(NULL), param1(NULL), sp(NULL), rp(NULL),
		  _pages(frago->core()->GetGC())
	{
		rewind();
	}

	LirBuffer::~LirBuffer()
	{
		clear();
		verbose_only(if (names) NJ_DELETE(names);)
		_frago = 0;
	}
	
	void LirBuffer::clear()
	{
		// free all the memory and clear the stats
		_frago->pagesRelease(_pages);
		NanoAssert(!_pages.size());
		_unused = 0;
		_stats.lir = 0;
		_noMem = 0;
		_nextPage = 0;
		for (int i = 0; i < NumSavedRegs; ++i)
			savedRegs[i] = NULL;
		explicitSavedRegs = false;
	}

    void LirBuffer::rewind()
	{
		clear();
		// pre-allocate the current and the next page we will be using
		Page* start = pageAlloc();
		_unused = start ? &start->lir[0] : NULL;
		_nextPage = pageAlloc();
		NanoAssert((_unused && _nextPage) || _noMem);
    }

	int32_t LirBuffer::insCount() 
	{
		// doesn't include embedded constants nor LIR_skip payload
		return _stats.lir;
	}

	int32_t LirBuffer::byteCount() 
	{
		return ((_pages.size() ? _pages.size()-1 : 0) * sizeof(Page)) +
			((int32_t)_unused - (int32_t)pageTop(_unused));
	}

	Page* LirBuffer::pageAlloc()
	{
		Page* page = _frago->pageAlloc();
		if (page)
			_pages.add(page);
		else
			_noMem = 1;
		return page;
	}
	
	LInsp LirBuffer::next()
	{
		return _unused;
	}

	void LirBufWriter::ensureRoom(uint32_t count)
	{
		NanoAssert(count * sizeof(LIns) <= MAX_SKIP_BYTES);
		LInsp before = _buf->next();
		LInsp after = before+count+LIR_FAR_SLOTS;
		// transition to the next page?
		if (!samepage(before,after))
		{
			// we don't want this to fail, so we always have a page in reserve
			NanoAssert(_buf->_nextPage);
			_buf->_unused = &_buf->_nextPage->lir[0];	
			// link LIR stream back to prior instruction (careful insLink relies on _unused...)
			insLinkTo(LIR_skip, before-1);
			_buf->_nextPage = _buf->pageAlloc();
			NanoAssert(_buf->_nextPage || _buf->_noMem);
		}
	}

	LInsp LirBufWriter::insLinkTo(LOpcode op, LInsp to)
	{
		LInsp l = _buf->next();
		NanoAssert(samepage(l,l+LIR_FAR_SLOTS)); // must have called ensureRoom()
        if (can24bReach(l,to))
		{
		    NanoStaticAssert(LIR_nearskip == LIR_skip - 1);
		    NanoStaticAssert(LIR_neartramp == LIR_tramp - 1);
            l->initOpcode(LOpcode(op-1)); // nearskip or neartramp
            l->setimm24(to-l);
            _buf->commit(1);
			_buf->_stats.lir++;
        }
        else
		{
			l = insLinkToFar(op,to);
		}
		return l;
	}

	LInsp LirBufWriter::insLinkToFar(LOpcode op, LInsp to)
	{
		LirFarIns* ov = (LirFarIns*) _buf->next();
		ov->v = to;
		ov->i.initOpcode(op);
		_buf->commit(LIR_FAR_SLOTS);
		_buf->_stats.lir++;

		NanoAssert( (LInsp)(ov+1) == _buf->next() );
		return &(ov->i);
	}
	
	void LirBufWriter::makeReachable(LInsp& o, LInsp from)
	{
		if (o && !can8bReach(from,o))
		{
			if (o == _buf->sp && spref && can8bReach(from, spref)) {
				o = spref;
				return;
			}
			if (o == _buf->rp && rpref && can8bReach(from, rpref)) {
				o = rpref;
				return;
			}

			// need a trampoline to get to from
			LInsp tramp = insLinkTo(LIR_tramp, o);  // will produce neartramp if possible
			NanoAssert( tramp->ref() == o && samepage(from,tramp) );
			if (o == _buf->sp)
				spref = tramp;
			else if (o == _buf->rp)
				rpref = tramp;
			o = tramp;
		}
	}

	void LirBufWriter::prepFor(LInsp& i1, LInsp& i2, LInsp& i3)
	{
		uint32_t i = 0;  // count of operands
		i += (i1) ? 1 : 0;
		i += (i2) ? 1 : 0;
		i += (i3) ? 1 : 0;
		
		uint32_t count = (LIR_FAR_SLOTS*i)+1;  // count of LIns if all operands require tramp
		ensureRoom(count);
		NanoAssert( samepage(_buf->next()+count,_buf->next()) );
		
		// guaranteed space for far tramps if necc.
		LInsp from = _buf->next()+count;
		makeReachable(i1, from);
		makeReachable(i2, from);
		makeReachable(i3, from);
		NanoAssert(from>i1 && from>i2 && from>i3);
	}

	LInsp LirBuffer::commit(uint32_t count)
	{
		NanoAssertMsg( samepage(_unused, _unused+count), "You need to call ensureRoom first!" );
		return _unused += count;
	}
	
	uint32_t LIns::reference(LIns *r) const
	{
		int delta = this-r-1;
		NanoAssert(isU8(delta));
		return delta;
	}

    LIns* LIns::deref(int32_t off) const
    {
		LInsp i = (LInsp) this-1 - off;
        while (i && i->isTramp()) {
            i = i->ref();
        }
		return i;
    }

	LInsp LirBufWriter::insStore(LInsp val, LInsp base, LInsp off)
	{
		LOpcode op = val->isQuad() ? LIR_stq : LIR_st;
		NanoAssert(val && base && off);
		prepFor(val, base, off);
		LInsp l = _buf->next();
		l->initOpcode(op);
		l->setOprnd1(val);
		l->setOprnd2(base);
		l->setOprnd3(off);
		_buf->commit(1);
		_buf->_stats.lir++;
		return l;
	}
	
	LInsp LirBufWriter::insStorei(LInsp val, LInsp base, int32_t d)
	{
		if (!isS8(d)) {
			return insStore(val, base, insImm(d));
		}

		LOpcode op = val->isQuad() ? LIR_stqi : LIR_sti;
		NanoAssert(val && base && isS8(d));
		LInsp u3=0;
		prepFor(val, base, u3);
		LInsp l = _buf->next();
		l->initOpcode(op);
		l->setOprnd1(val);
		l->setOprnd2(base);
		l->setDisp(int8_t(d));
		_buf->commit(1);
		_buf->_stats.lir++;
		return l;
	}

	LInsp LirBufWriter::ins0(LOpcode op)
	{
		ensureRoom(1);
        LirBuffer *b = this->_buf;
		LInsp l = b->next();
		l->initOpcode(op);
		b->commit(1);
		b->_stats.lir++;
		return l;
	}
	
	LInsp LirBufWriter::ins1(LOpcode op, LInsp o1)
	{
		LInsp u2=0,u3=0;
		prepFor(o1,u2,u3);
		LInsp l = _buf->next();
		l->initOpcode(op);
		l->setOprnd1(o1);
		_buf->commit(1);
		_buf->_stats.lir++;
		return l;
	}
	
	LInsp LirBufWriter::ins2(LOpcode op, LInsp o1, LInsp o2)
	{
		LInsp u3=0;
		prepFor(o1,o2,u3);
		LInsp l = _buf->next();
		l->initOpcode(op);
		l->setOprnd1(o1);
		l->setOprnd2(o2);		
		_buf->commit(1);
		_buf->_stats.lir++;
		return l;
	}

	LInsp LirBufWriter::insLoad(LOpcode op, LInsp base, LInsp d)
	{
		return ins2(op,base,d);
	}

	LInsp LirBufWriter::insGuard(LOpcode op, LInsp c, LInsp data)
	{
		return ins2(op, c, data);
	}

	LInsp LirBufWriter::insBranch(LOpcode op, LInsp condition, LInsp toLabel)
	{
		if (!toLabel)
			toLabel = insFar(LIR_tramp,0); //empty tramp
        if (!condition) {
            // unconditional, just point to something
            condition = toLabel;
        }
	    return ins2(op,condition,toLabel);
	}

    LInsp LirBufWriter::insAlloc(int32_t size)
    {
        size = (size+3)>>2; // # of required 32bit words
        NanoAssert(isU16(size));
		ensureRoom(1);
		LInsp l = _buf->next();
		l->initOpcode(LIR_alloc);
		l->i.imm16 = uint16_t(size);
		_buf->commit(1);
		_buf->_stats.lir++;
		return l;
    }

    LInsp LirBufWriter::insParam(int32_t arg, int32_t kind)
    {
		ensureRoom(1);
        LirBuffer *b = this->_buf;
		LInsp l = b->next();
		l->initOpcode(LIR_param);
        NanoAssert(isU8(arg) && isU8(kind));
		l->c.imm8a = arg;
        l->c.imm8b = kind;
        if (kind) {
            NanoAssert(arg < NumSavedRegs);
            b->savedRegs[arg] = l;
            b->explicitSavedRegs = true;
        }
		b->commit(1);
		b->_stats.lir++;
		return l;
    }
	
	LInsp LirBufWriter::insFar(LOpcode op, LInsp target)
	{
		ensureRoom(LIR_FAR_SLOTS);  // make room for it
		LInsp l = insLinkToFar(op, target);
		_buf->_stats.lir++;
		return l;
	}
	
	LInsp LirBufWriter::insImm(int32_t imm)
	{
		if (isS16(imm)) {
			ensureRoom(1);
			LInsp l = _buf->next();
			l->initOpcode(LIR_short);
			l->setimm16(imm);
			_buf->commit(1);
			_buf->_stats.lir++;
			return l;
		} else {
			ensureRoom(LIR_IMM32_SLOTS);
			LirImm32Ins* l = (LirImm32Ins*)_buf->next();
			l->v = imm;
			l->i.initOpcode(LIR_int);
			_buf->commit(LIR_IMM32_SLOTS);	
			_buf->_stats.lir++;
			NanoAssert((LInsp)(l+1)==_buf->next());
			return &(l->i);
		}
	}
	
	LInsp LirBufWriter::insImmq(uint64_t imm)
	{
		ensureRoom(LIR_IMM64_SLOTS);
		LirImm64Ins* l = (LirImm64Ins*)_buf->next();
		l->v[0] = int32_t(imm);
		l->v[1] = int32_t(imm>>32);
		l->i.initOpcode(LIR_quad);
		_buf->commit(LIR_IMM64_SLOTS);	
		_buf->_stats.lir++;
		NanoAssert((LInsp)(l+1)==_buf->next());
		return &(l->i);
	}

	LInsp LirBufWriter::skip(size_t size)
	{
        const uint32_t n = (size+sizeof(LIns)-1)/sizeof(LIns);
		ensureRoom(n); // make room for it
 		LInsp last = _buf->next()-1;  // safe, next()-1+n guaranteed to be on same page
		_buf->commit(n);
		NanoAssert(samepage(last,_buf->next()));
		ensureRoom(LIR_FAR_SLOTS);
		return insLinkTo(LIR_skip, last);
	}

	LInsp LirReader::read()	
	{
		LInsp cur = _i;
		if (!cur)
			return 0;
		LIns* i = cur;
		LOpcode iop = i->opcode();
		do
		{
			switch (iop)
			{					
				default:
					i--;
					break;

#if defined NANOJIT_64BIT
            	case LIR_callh:
#endif
				case LIR_call:
				case LIR_fcall:
                case LIR_calli:
                case LIR_fcalli:
					NanoAssert( samepage(i,i+1-i->callInsWords()) );
					i -= i->callInsWords();
					break;

				case LIR_skip:
				case LIR_nearskip:
					NanoAssert(i->ref() != i);
					i = i->ref();
					break;

                case LIR_tramp:
                    NanoAssert(samepage(i,i+1-LIR_FAR_SLOTS));
					i -= LIR_FAR_SLOTS;
                    break;

				case LIR_int:
                    NanoAssert(samepage(i,i+1-LIR_IMM32_SLOTS));
					i -= LIR_IMM32_SLOTS;					
					break;

				case LIR_quad:
                    NanoAssert(samepage(i,i+1-LIR_IMM64_SLOTS));
					i -= LIR_IMM64_SLOTS;					
					break;

				case LIR_start:
					_i = 0;  // start of trace
					return cur;
			}
			iop = i->opcode();
		}
		while (is_trace_skip_tramp(iop)||iop==LIR_2);
		_i = i;
		return cur;
	}

	bool FASTCALL isCmp(LOpcode c) {
		return (c >= LIR_eq && c <= LIR_uge) || (c >= LIR_feq && c <= LIR_fge);
	}
    
	bool FASTCALL isCond(LOpcode c) {
		return (c == LIR_ov) || (c == LIR_cs) || isCmp(c);
	}

    bool FASTCALL isFloat(LOpcode c) {
        switch (c) {
            default:
                return false;
            case LIR_fadd:
            case LIR_fsub:
            case LIR_fmul:
            case LIR_fdiv:
            case LIR_fneg:
            case LIR_fcall:
            case LIR_fcalli:
            case LIR_i2f:
            case LIR_u2f:
                return true;
        }
    }
    
	bool LIns::isCmp() const {
		return nanojit::isCmp(u.code);
	}

    bool LIns::isCond() const {
        return nanojit::isCond(u.code);
    }
	
	bool LIns::isQuad() const {
		#ifdef AVMPLUS_64BIT
			// callh in 64bit cpu's means a call that returns an int64 in a single register
			return (u.code & LIR64) != 0 || u.code == LIR_callh;
		#else
			// callh in 32bit cpu's means the 32bit MSW of an int64 result in 2 registers
			return (u.code & LIR64) != 0;
		#endif
	}
    
	bool LIns::isconstval(int32_t val) const
	{
		return isconst() && constval()==val;
	}

	bool LIns::isconstq() const
	{	
		return isop(LIR_quad);
	}

	bool LIns::isconstp() const
	{
    #ifdef AVMPLUS_64BIT
	    return isconstq();
	#else
	    return isconst();
    #endif
	}

	bool FASTCALL isCse(LOpcode op) {
		op = LOpcode(op & ~LIR64);
		return op >= LIR_ldcs && op <= LIR_uge;
	}

    bool LIns::isCse(const CallInfo *functions) const
    { 
		return nanojit::isCse(u.code) || (isCall() && callInfo()->_cse);
    }

	void LIns::setimm16(int32_t x)
	{
		NanoAssert(isS16(x));
		i.imm16 = int16_t(x);
	}

	void LIns::setimm24(int32_t x)
	{
		NanoAssert(isS24(x));
		t.imm24 = x;
	}

	void LIns::setresv(uint32_t resv)
	{
		NanoAssert(isU8(resv));
		g.resv = resv;
	}

	void LIns::initOpcode(LOpcode op)
	{
		i.code = op;
		i.imm16 = 0;
        i.resv = 0;
	}

	void LIns::setOprnd1(LInsp r)
	{
		u.oprnd_1 = reference(r);
	}

	void LIns::setOprnd2(LInsp r)
	{
		u.oprnd_2 = reference(r);
	}

	void LIns::setOprnd3(LInsp r)
	{
		u.oprnd_3 = reference(r);
	}

    void LIns::setDisp(int8_t d)
    {
        sti.disp = d;
    }

    LIns **LIns::targetAddr() {
		NanoAssert(isBranch());
		LInsp i = (LInsp) this-1 - u.oprnd_2;
        NanoAssert(i->isTramp());
        LInsp ref;
        while ((ref=i->ref()) != 0 && ref->isTramp())
            i = ref;
		NanoAssert(i->isop(LIR_tramp));
		LirFarIns* ov = (LirFarIns*)(i-LIR_FAR_SLOTS+1);
		return &(ov->v);
    }

    void LIns::target(LInsp label) {
        NanoAssert(label && label->isop(LIR_label));
        *(targetAddr()) = label;
	}

	LInsp LIns::getTarget()
	{
        NanoAssert(isBranch());
        return oprnd2();
	}

	LInsp	LIns::oprnd1() const	
	{
        return deref(u.oprnd_1);
	}
	
	LInsp	LIns::oprnd2() const
	{ 
        return deref(u.oprnd_2);
	}

	LInsp	LIns::oprnd3() const
	{ 
        return deref(u.oprnd_3);
	}

    void *LIns::payload() const
    {
        NanoAssert(opcode()==LIR_skip || opcode()==LIR_nearskip);
        return (void*) (ref()+1);
    }

	LIns* LIns::ref() const	
	{ 
		LIns const *r = 0;
		if (t.code&1)
			r = this + t.imm24;
		else
		{
			LirFarIns* l = (LirFarIns*)(this-LIR_FAR_SLOTS+1);
			r = l->v;
		}
		return (const LInsp)r;
	}

	int32_t LIns::imm32() const	
	{ 
		LirImm32Ins* l = (LirImm32Ins*)(this-LIR_IMM32_SLOTS+1);
		return l->v; 
	}

	uint64_t LIns::constvalq() const
	{
		LirImm64Ins* l = (LirImm64Ins*)(this-LIR_IMM64_SLOTS+1);
    #ifdef AVMPLUS_UNALIGNED_ACCESS
        int* ptr = (int*)l->v;
        return *(const uint64_t*)ptr;
    #else
        union { uint64_t tmp; int32_t dst[2]; } u;
		#ifdef AVMPLUS_BIG_ENDIAN
        u.dst[0] = l->v[1];
        u.dst[1] = l->v[0];
		#else
        u.dst[0] = l->v[0];
        u.dst[1] = l->v[1];
		#endif
        return u.tmp;
    #endif
	}

	double LIns::constvalf() const
	{
		LirImm64Ins* l = (LirImm64Ins*)(this-LIR_IMM64_SLOTS+1);
		NanoAssert(isconstq());
	#ifdef AVMPLUS_UNALIGNED_ACCESS
        int* ptr = (int*)l->v;
		return *(const double*)ptr;
	#else
		union { uint32_t dst[2]; double tmpf; } u;
		#ifdef AVMPLUS_BIG_ENDIAN
		u.dst[0] = l->v[1];
		u.dst[1] = l->v[0];
		#else
		u.dst[0] = l->v[0];
		u.dst[1] = l->v[1];
		#endif
		return u.tmpf;
	#endif
	}

	size_t LIns::callInsWords() const
	{
		return LIR_CALL_SLOTS + argwords(argc());
	}

	const CallInfo* LIns::callInfo() const
	{
		LirCallIns* l = (LirCallIns*)(this-LIR_CALL_SLOTS+1);
		return l->ci; 
	}

	// index args in r-l order.  arg(0) is rightmost arg
	LIns* LIns::arg(uint32_t i) 
	{
		NanoAssert(i < argc());
		LirCallIns* l = (LirCallIns*)(this-LIR_CALL_SLOTS+1);
		uint8_t* offs = (uint8_t*)l - (i+1);
		return deref(*offs);
	}

    LIns* LirWriter::ins2i(LOpcode v, LIns* oprnd1, int32_t imm)
    {
        return ins2(v, oprnd1, insImm(imm));
    }

    bool insIsS16(LInsp i)
    {
        if (i->isconst()) {
            int c = i->constval();
            return isS16(c);
        }
        if (i->isop(LIR_cmov) || i->isop(LIR_qcmov)) {
            LInsp vals = i->oprnd2();
            return insIsS16(vals->oprnd1()) && insIsS16(vals->oprnd2());
        }
        if (i->isCmp())
            return true;
        // many other possibilities too.
        return false;
    }

	LIns* ExprFilter::ins1(LOpcode v, LIns* i)
	{
		if (v == LIR_qlo) {
			if (i->isconstq())
				return insImm(int32_t(i->constvalq()));
			if (i->isop(LIR_qjoin))
				return i->oprnd1();
		}
		else if (v == LIR_qhi) {
			if (i->isconstq())
				return insImm(int32_t(i->constvalq()>>32));
			if (i->isop(LIR_qjoin))
				return i->oprnd2();
		}
		else if (i->isconst()) {
			int32_t c = i->constval();
			if (v == LIR_neg)
				return insImm(-c);
			if (v == LIR_not)
				return insImm(~c);
		}
		else if (v == i->opcode() && (v == LIR_not || v == LIR_neg || v == LIR_fneg)) {
            // not(not(x)) = x;  neg(neg(x)) = x;  fneg(fneg(x)) = x;
			return i->oprnd1();
		}
        /* [ed 8.27.08] this causes a big slowdown in gameoflife.as.  why?
        else if (i->isconst()) {
            if (v == LIR_i2f) {
                return insImmf(i->constval());
            }
            else if (v == LIR_u2f) {
                return insImmf((uint32_t)i->constval());
            }
        }*/

		// todo
		// -(a-b) = b-a

		return out->ins1(v, i);
	}

	LIns* ExprFilter::ins2(LOpcode v, LIns* oprnd1, LIns* oprnd2)
	{
		NanoAssert(oprnd1 && oprnd2);
		if (v == LIR_cmov || v == LIR_qcmov) {
			if (oprnd2->oprnd1() == oprnd2->oprnd2()) {
				// c ? a : a => a
				return oprnd2->oprnd1();
			}
			if (oprnd1->isconst()) {
			    // const ? x : y => return x or y depending on const
			    return oprnd1->constval() ? oprnd2->oprnd1() : oprnd2->oprnd2();
			}
		}
		if (oprnd1 == oprnd2)
		{
			if (v == LIR_xor || v == LIR_sub ||
				v == LIR_ult || v == LIR_ugt || v == LIR_gt || v == LIR_lt)
				return insImm(0);
			if (v == LIR_or || v == LIR_and)
				return oprnd1;
			if (v == LIR_le || v == LIR_ule || v == LIR_ge || v == LIR_uge) {
				// x <= x == 1; x >= x == 1
				return insImm(1);
			}
		}
		if (oprnd1->isconst() && oprnd2->isconst())
		{
			int c1 = oprnd1->constval();
			int c2 = oprnd2->constval();
			if (v == LIR_qjoin) {
				uint64_t q = c1 | uint64_t(c2)<<32;
				return insImmq(q);
			}
			if (v == LIR_eq)
				return insImm(c1 == c2);
            if (v == LIR_ov)
                return insImm((c2 != 0) && ((c1 + c2) <= c1)); 
            if (v == LIR_cs)
                return insImm((c2 != 0) && ((uint32_t(c1) + uint32_t(c2)) <= uint32_t(c1)));
			if (v == LIR_lt)
				return insImm(c1 < c2);
			if (v == LIR_gt)
				return insImm(c1 > c2);
			if (v == LIR_le)
				return insImm(c1 <= c2);
			if (v == LIR_ge)
				return insImm(c1 >= c2);
			if (v == LIR_ult)
				return insImm(uint32_t(c1) < uint32_t(c2));
			if (v == LIR_ugt)
				return insImm(uint32_t(c1) > uint32_t(c2));
			if (v == LIR_ule)
				return insImm(uint32_t(c1) <= uint32_t(c2));
			if (v == LIR_uge)
				return insImm(uint32_t(c1) >= uint32_t(c2));
			if (v == LIR_rsh)
				return insImm(int32_t(c1) >> int32_t(c2));
			if (v == LIR_lsh)
				return insImm(int32_t(c1) << int32_t(c2));
			if (v == LIR_ush)
				return insImm(uint32_t(c1) >> int32_t(c2));
            if (v == LIR_or)
                return insImm(uint32_t(c1) | int32_t(c2));
            if (v == LIR_and)
                return insImm(uint32_t(c1) & int32_t(c2));
            if (v == LIR_xor)
                return insImm(uint32_t(c1) ^ int32_t(c2));
		}
		else if (oprnd1->isconstq() && oprnd2->isconstq())
		{
			double c1 = oprnd1->constvalf();
			double c2 = oprnd2->constvalf();
			if (v == LIR_feq)
				return insImm(c1 == c2);
			if (v == LIR_flt)
				return insImm(c1 < c2);
			if (v == LIR_fgt)
				return insImm(c1 > c2);
			if (v == LIR_fle)
				return insImm(c1 <= c2);
			if (v == LIR_fge)
				return insImm(c1 >= c2);
		}
		else if (oprnd1->isconst() && !oprnd2->isconst())
		{
			if (v == LIR_add || v == LIR_addp || v == LIR_mul ||
				v == LIR_fadd || v == LIR_fmul ||
				v == LIR_xor || v == LIR_or || v == LIR_and ||
				v == LIR_eq) {
				// move const to rhs
				LIns* t = oprnd2;
				oprnd2 = oprnd1;
				oprnd1 = t;
			}
			else if (v >= LIR_lt && v <= LIR_uge) {
				NanoStaticAssert((LIR_lt ^ 1) == LIR_gt);
				NanoStaticAssert((LIR_le ^ 1) == LIR_ge);
				NanoStaticAssert((LIR_ult ^ 1) == LIR_ugt);
				NanoStaticAssert((LIR_ule ^ 1) == LIR_uge);

				// move const to rhs, swap the operator
				LIns *t = oprnd2;
				oprnd2 = oprnd1;
				oprnd1 = t;
				v = LOpcode(v^1);
			}
		}

		if (oprnd2->isconst())
		{
			int c = oprnd2->constval();
			if (v == LIR_add && oprnd1->isop(LIR_add) && oprnd1->oprnd2()->isconst()) {
				// add(add(x,c1),c2) => add(x,c1+c2)
				c += oprnd1->oprnd2()->constval();
				oprnd2 = insImm(c);
				oprnd1 = oprnd1->oprnd1();
			}
			else if (v == LIR_sub && oprnd1->isop(LIR_add) && oprnd1->oprnd2()->isconst()) {
				// sub(add(x,c1),c2) => add(x,c1-c2)
				c = oprnd1->oprnd2()->constval() - c;
				oprnd2 = insImm(c);
				oprnd1 = oprnd1->oprnd1();
				v = LIR_add;
			}
			else if (v == LIR_rsh && c == 16 && oprnd1->isop(LIR_lsh) &&
					 oprnd1->oprnd2()->isconstval(16)) {
				if (insIsS16(oprnd1->oprnd1())) {
					// rsh(lhs(x,16),16) == x, if x is S16
					return oprnd1->oprnd1();
				}
			}
			else if (v == LIR_ult) {
				if (oprnd1->isop(LIR_cmov) || oprnd1->isop(LIR_qcmov)) {
					LInsp a = oprnd1->oprnd2()->oprnd1();
					LInsp b = oprnd1->oprnd2()->oprnd2();
					if (a->isconst() && b->isconst()) {
						bool a_lt = uint32_t(a->constval()) < uint32_t(oprnd2->constval());
						bool b_lt = uint32_t(b->constval()) < uint32_t(oprnd2->constval());
						if (a_lt == b_lt)
							return insImm(a_lt);
					}
				}
			}

			if (c == 0)
			{
				if (v == LIR_add || v == LIR_addp || v == LIR_or || v == LIR_xor ||
					v == LIR_sub || v == LIR_lsh || v == LIR_rsh || v == LIR_ush)
					return oprnd1;
				else if (v == LIR_and || v == LIR_mul)
					return oprnd2;
				else if (v == LIR_eq && oprnd1->isop(LIR_or) && 
					oprnd1->oprnd2()->isconst() &&
					oprnd1->oprnd2()->constval() != 0) {
					// (x or c) != 0 if c != 0
					return insImm(0);
				}
			}
			else if (c == -1 || (c == 1 && oprnd1->isCmp())) {
				if (v == LIR_or) {
					// x | -1 = -1, cmp | 1 = 1
					return oprnd2;
				}
				else if (v == LIR_and) {
					// x & -1 = x, cmp & 1 = cmp
					return oprnd1;
				}
			}
		}

		LInsp i;
		if (v == LIR_qjoin && oprnd1->isop(LIR_qlo) && oprnd2->isop(LIR_qhi) 
			&& (i = oprnd1->oprnd1()) == oprnd2->oprnd1()) {
			// qjoin(qlo(x),qhi(x)) == x
			return i;
		}

		return out->ins2(v, oprnd1, oprnd2);
	}

	LIns* ExprFilter::insGuard(LOpcode v, LInsp c, LInsp x)
	{
		if (v == LIR_xt || v == LIR_xf) {
			if (c->isconst()) {
				if ((v == LIR_xt && !c->constval()) || (v == LIR_xf && c->constval())) {
					return 0; // no guard needed
				}
				else {
#ifdef JS_TRACER
					// We're emitting a guard that will always fail. Any code
					// emitted after this guard is dead code. We could
					// silently optimize out the rest of the emitted code, but
					// this could indicate a performance problem or other bug,
					// so assert in debug builds.
					NanoAssertMsg(0, "Constantly false guard detected");
#endif
					return out->insGuard(LIR_x, out->insImm(1), x);
				}
			}
			else {
				NanoStaticAssert((LIR_xt ^ 1) == LIR_xf);
				while (c->isop(LIR_eq) && c->oprnd1()->isCmp() && 
					c->oprnd2()->isconstval(0)) {
				    // xt(eq(cmp,0)) => xf(cmp)   or   xf(eq(cmp,0)) => xt(cmp)
				    v = LOpcode(v^1);
				    c = c->oprnd1();
				}
			}
		}
		return out->insGuard(v, c, x);
	}

    LIns* ExprFilter::insBranch(LOpcode v, LIns *c, LIns *t)
    {
        if (v == LIR_jt || v == LIR_jf) {
            while (c->isop(LIR_eq) && c->oprnd1()->isCmp() && c->oprnd2()->isconstval(0)) {
                // jt(eq(cmp,0)) => jf(cmp)   or   jf(eq(cmp,0)) => jt(cmp)
                v = LOpcode(v ^ 1);
                c = c->oprnd1();
            }
        }
        return out->insBranch(v, c, t);
    }

    LIns* LirWriter::insLoadi(LIns *base, int disp) 
    { 
        return insLoad(LIR_ld,base,disp);
    }

	LIns* LirWriter::insLoad(LOpcode op, LIns *base, int disp)
	{
		return insLoad(op, base, insImm(disp));
	}

    LIns* LirWriter::store(LInsp value, LInsp base, int32_t d)
    {
		return isS8(d) ? insStorei(value, base, d)
			: insStore(value, base, insImm(d));
    }

	LIns* LirWriter::ins_eq0(LIns* oprnd1)
	{
		return ins2i(LIR_eq, oprnd1, 0);
	}

    LIns* LirWriter::insImmf(double f)
    {
        union {
            double f;
            uint64_t q;
        } u;
        u.f = f;
        return insImmq(u.q);
    }

	LIns* LirWriter::qjoin(LInsp lo, LInsp hi)
	{
		return ins2(LIR_qjoin, lo, hi);
	}

	LIns* LirWriter::insImmPtr(const void *ptr)
	{
		return sizeof(ptr) == 8 ? insImmq((uintptr_t)ptr) : insImm((intptr_t)ptr);
	}

	LIns* LirWriter::ins_choose(LIns* cond, LIns* iftrue, LIns* iffalse)
	{
		// if not a conditional, make it implicitly an ==0 test (then flop results)
		if (!cond->isCmp())
		{
			cond = ins_eq0(cond);
			LInsp tmp = iftrue;
			iftrue = iffalse;
			iffalse = tmp;
		}

		if (avmplus::AvmCore::use_cmov())
		{
			return ins2((iftrue->isQuad() || iffalse->isQuad()) ? LIR_qcmov : LIR_cmov, cond, ins2(LIR_2, iftrue, iffalse));
		}

		// @todo -- it might be better to use a short conditional branch rather than
		// the bit-twiddling on systems that don't provide a conditional move instruction.
		LInsp ncond = ins1(LIR_neg, cond); // cond ? -1 : 0
		return ins2(LIR_or, 
					ins2(LIR_and, iftrue, ncond), 
					ins2(LIR_and, iffalse, ins1(LIR_not, ncond)));
	}

    LIns* LirBufWriter::insCall(const CallInfo *ci, LInsp args[])
	{
		static const LOpcode k_callmap[] = { LIR_call, LIR_fcall, LIR_call, LIR_callh };
		static const LOpcode k_callimap[] = { LIR_calli, LIR_fcalli, LIR_calli, LIR_skip };

		uint32_t argt = ci->_argtypes;
        LOpcode op = (ci->isIndirect() ? k_callimap : k_callmap)[argt & 3];
        NanoAssert(op != LIR_skip); // LIR_skip here is just an error condition

        ArgSize sizes[MAXARGS];
        int32_t argc = ci->get_sizes(sizes);

		if (AvmCore::config.soft_float) {
			if (op == LIR_fcall)
				op = LIR_callh;
		}

		//
		// An example of the what we're trying to serialize:
		//
		// byte                                             word
		// ----                                             ----
		//    N  [ arg tramp #0 ------------------------ ]  K
		//  N+4  [ arg tramp #1 ------------------------ ]  K+1
		//  N+8  [ arg tramp #2 ------------------------ ]  K+2
		// N+12  [ arg tramp #3 ------------------------ ]  K+3
		// N+16  [ argoff3 | argoff2 | argoff1 | argoff0 ]  K+4
		// N+20  [ CallInfo* --------------------------- ]  K+5
		// N+24  [ LIR_call ---------| imm8a=0 | imm8b=4 ]  K+6
		//
		// In this example:
		//    32 bit words
		//    'argc' = 4
		//    'words' = argwords(argc) = 1  (word K+4       )
		//    'LIR_CALL_SLOTS' = 2          (words K+5 - K+6)
		//    'insSz' = 1+2 = 3             (words K+4 - K+6)
		//    'from' = next + (insSz - 1)   (word K+6       )
		//

		NanoAssert(argc <= (int)MAXARGS);
		uint32_t words = argwords(argc);
		int32_t insSz = words + LIR_CALL_SLOTS; // words need for offsets + size of instruction
		ensureRoom(argc * LIR_FAR_SLOTS + insSz);  // argc=# possible tramps for args

		// Argument deltas are calculated relative to the final LIns,
		// which is the last word in the cluster.
		LInsp from = _buf->next() + argc * LIR_FAR_SLOTS + insSz - 1; 
		for (int32_t i=0; i < argc; i++)
			makeReachable(args[i], from);

		// skip 'words' needed for call parameters
		LirCallIns *l = (LirCallIns*) (_buf->next()+words);
		l->ci = ci;

		// call parameters laid in reverse order
		uint8_t* offs = (uint8_t*)l;
		for (int32_t i=0; i < argc; i++)
			*--offs = (uint8_t) l->i.reference(args[i]);
		NanoAssert((LInsp)offs>=_buf->next());
		
#ifndef NANOJIT_64BIT
		l->i.initOpcode(op==LIR_callh ? LIR_call : op);
#else
		l->i.initOpcode(op);
#endif
		l->i.c.imm8a = 0;
		l->i.c.imm8b = argc;
		_buf->commit(insSz);	
		_buf->_stats.lir++;
		NanoAssert((LInsp)(l+1)==_buf->next());
		return &(l->i);
	}

    using namespace avmplus;

	StackFilter::StackFilter(LirFilter *in, GC *gc, LirBuffer *lirbuf, LInsp sp) 
		: LirFilter(in), gc(gc), lirbuf(lirbuf), sp(sp), top(0)
	{}

	LInsp StackFilter::read() 
	{
		for (;;) 
		{
			LInsp i = in->read();
			if (!i)
				return i;
			if (i->isStore())
			{
				LInsp base = i->oprnd2();
				if (base == sp) 
				{
					LInsp v = i->oprnd1();
					int d = i->immdisp() >> 2;
					if (d >= top) {
						continue;
					} else {
						d = top - d;
						if (v->isQuad()) {
							// storing 8 bytes
							if (stk.get(d) && stk.get(d-1)) {
								continue;
							} else {
								stk.set(gc, d);
								stk.set(gc, d-1);
							}
						}
						else {
							// storing 4 bytes
							if (stk.get(d))
								continue;
							else
								stk.set(gc, d);
						}
					}
				}
			}
			/* 
			 * NB: If there is a backward branch other than the loop-restart branch, this is
			 * going to be wrong. Unfortunately there doesn't seem to be an easy way to detect
			 * such branches. Just do not create any.
			 */
			else if (i->isGuard())
			{
				stk.reset();
				top = getTop(i) >> 2;
			}
			return i;
		}
	}

	//
	// inlined/separated version of SuperFastHash
	// This content is copyrighted by Paul Hsieh, For reference see : http://www.azillionmonkeys.com/qed/hash.html
	//
	inline uint32_t _hash8(uint32_t hash, const uint8_t data)
	{
		hash += data;
		hash ^= hash << 10;
		hash += hash >> 1;
		return hash;
	}

	inline uint32_t _hash32(uint32_t hash, const uint32_t data)
	{
		const uint32_t dlo = data & 0xffff;
		const uint32_t dhi = data >> 16;
		hash += dlo;
		const uint32_t tmp = (dhi << 11) ^ hash;
		hash = (hash << 16) ^ tmp;
		hash += hash >> 11;
		return hash;
	}
	
	inline uint32_t _hashptr(uint32_t hash, const void* data)
	{
#ifdef NANOJIT_64BIT
		hash = _hash32(hash, uint32_t(uintptr_t(data) >> 32));
		hash = _hash32(hash, uint32_t(uintptr_t(data)));
		return hash;
#else
		return _hash32(hash, uint32_t(data));
#endif
	}

	inline uint32_t _hashfinish(uint32_t hash)
	{
		/* Force "avalanching" of final 127 bits */
		hash ^= hash << 3;
		hash += hash >> 5;
		hash ^= hash << 4;
		hash += hash >> 17;
		hash ^= hash << 25;
		hash += hash >> 6;
		return hash;
	}

	LInsHashSet::LInsHashSet(GC* gc) : 
			m_used(0), m_cap(kInitialCap), m_gc(gc)
	{
#ifdef MEMORY_INFO
//		m_list.set_meminfo_name("LInsHashSet.list");
#endif
        LInsp *list = (LInsp*) gc->Alloc(sizeof(LInsp)*m_cap, GC::kZero);
        WB(gc, this, &m_list, list);
	}

    LInsHashSet::~LInsHashSet()
    {
        m_gc->Free(m_list);
    }

    void LInsHashSet::clear() {
        memset(m_list, 0, sizeof(LInsp)*m_cap);
        m_used = 0;
    }
	
	/*static*/ uint32_t FASTCALL LInsHashSet::hashcode(LInsp i)
	{
		const LOpcode op = i->opcode();
		switch (op)
		{
			case LIR_short:
				return hashimm(i->imm16());
			case LIR_int:
				return hashimm(i->imm32());
			case LIR_quad:
				return hashimmq(i->constvalq());
			case LIR_call:
			case LIR_fcall:
#if defined NANOJIT_64BIT
			case LIR_callh:
#endif
			{
				LInsp args[10];
				int32_t argc = i->argc();
				NanoAssert(argc < 10);
				for (int32_t j=0; j < argc; j++)
					args[j] = i->arg(j);
				return hashcall(i->callInfo(), argc, args);
			} 
			default:
				if (operandCount[op] == 2)
					return hash2(op, i->oprnd1(), i->oprnd2());
				else
					return hash1(op, i->oprnd1());
		}
	}

	/*static*/ bool FASTCALL LInsHashSet::equals(LInsp a, LInsp b) 
	{
		if (a==b)
			return true;
		AvmAssert(a->opcode() == b->opcode());
		const LOpcode op = a->opcode();
		switch (op)
		{
			case LIR_short:
			{
				return a->imm16() == b->imm16();
			} 
			case LIR_int:
			{
				return a->imm32() == b->imm32();
			} 
			case LIR_quad:
			{
				return a->constvalq() == b->constvalq();
			}
			case LIR_call:
			case LIR_fcall:
#if defined NANOJIT_64BIT
			case LIR_callh:
#endif
			{
				if (a->callInfo() != b->callInfo()) return false;
				uint32_t argc=a->argc();
                NanoAssert(argc == b->argc());
				for (uint32_t i=0; i < argc; i++)
					if (a->arg(i) != b->arg(i))
						return false;
				return true;
			} 
			default:
			{
				const uint32_t count = operandCount[op];
				if ((count >= 1 && a->oprnd1() != b->oprnd1()) ||
					(count >= 2 && a->oprnd2() != b->oprnd2()))
					return false;
				return true;
			}
		}
	}

	void FASTCALL LInsHashSet::grow()
	{
		const uint32_t newcap = m_cap << 1;
        LInsp *newlist = (LInsp*) m_gc->Alloc(newcap * sizeof(LInsp), GC::kZero);
        LInsp *list = m_list;
#ifdef MEMORY_INFO
//		newlist.set_meminfo_name("LInsHashSet.list");
#endif
		for (uint32_t i=0, n=m_cap; i < n; i++) {
			LInsp name = list[i];
			if (!name) continue;
			uint32_t j = find(name, hashcode(name), newlist, newcap);
            newlist[j] = name;
		}
        m_cap = newcap;
        m_gc->Free(list);
        WB(m_gc, this, &m_list, newlist);
	}

	uint32_t FASTCALL LInsHashSet::find(LInsp name, uint32_t hash, const LInsp *list, uint32_t cap)
	{
		const uint32_t bitmask = (cap - 1) & ~0x1;

		uint32_t n = 7 << 1;
		hash &= bitmask;  
		LInsp k;
		while ((k = list[hash]) != NULL &&
			(!LIns::sameop(k,name) || !equals(k, name)))
		{
			hash = (hash + (n += 2)) & bitmask;		// quadratic probe
		}
		return hash;
	}

	LInsp LInsHashSet::add(LInsp name, uint32_t k)
	{
		// this is relatively short-lived so let's try a more aggressive load factor
		// in the interest of improving performance
		if (((m_used+1)<<1) >= m_cap) // 0.50
		{
			grow();
			k = find(name, hashcode(name), m_list, m_cap);
		}
		NanoAssert(!m_list[k]);
		m_used++;
        return m_list[k] = name;
	}

	void LInsHashSet::replace(LInsp i)
	{
        LInsp *list = m_list;
		uint32_t k = find(i, hashcode(i), list, m_cap);
		if (list[k]) {
			// already there, so replace it
			list[k] = i;
		} else {
			add(i, k);
		}
	}

	uint32_t LInsHashSet::hashimm(int32_t a) {
		return _hashfinish(_hash32(0,a));
	}

	uint32_t LInsHashSet::hashimmq(uint64_t a) {
		uint32_t hash = _hash32(0, uint32_t(a >> 32));
		return _hashfinish(_hash32(hash, uint32_t(a)));
	}

	uint32_t LInsHashSet::hash1(LOpcode op, LInsp a) {
		uint32_t hash = _hash8(0,uint8_t(op));
		return _hashfinish(_hashptr(hash, a));
	}

	uint32_t LInsHashSet::hash2(LOpcode op, LInsp a, LInsp b) {
		uint32_t hash = _hash8(0,uint8_t(op));
		hash = _hashptr(hash, a);
		return _hashfinish(_hashptr(hash, b));
	}

	uint32_t LInsHashSet::hashcall(const CallInfo *ci, uint32_t argc, LInsp args[]) {
		uint32_t hash = _hashptr(0, ci);
		for (int32_t j=argc-1; j >= 0; j--)
			hash = _hashptr(hash,args[j]);
		return _hashfinish(hash);
	}

	LInsp LInsHashSet::find32(int32_t a, uint32_t &i)
	{
		uint32_t cap = m_cap;
		const LInsp *list = m_list;
		const uint32_t bitmask = (cap - 1) & ~0x1;
		uint32_t hash = hashimm(a) & bitmask;
		uint32_t n = 7 << 1;
		LInsp k;
		while ((k = list[hash]) != NULL && 
			(!k->isconst() || k->constval() != a))
		{
			hash = (hash + (n += 2)) & bitmask;		// quadratic probe
		}
		i = hash;
		return k;
	}

	LInsp LInsHashSet::find64(uint64_t a, uint32_t &i)
	{
		uint32_t cap = m_cap;
		const LInsp *list = m_list;
		const uint32_t bitmask = (cap - 1) & ~0x1;
		uint32_t hash = hashimmq(a) & bitmask;  
		uint32_t n = 7 << 1;
		LInsp k;
		while ((k = list[hash]) != NULL && 
			(!k->isconstq() || k->constvalq() != a))
		{
			hash = (hash + (n += 2)) & bitmask;		// quadratic probe
		}
		i = hash;
		return k;
	}

	LInsp LInsHashSet::find1(LOpcode op, LInsp a, uint32_t &i)
	{
		uint32_t cap = m_cap;
		const LInsp *list = m_list;
		const uint32_t bitmask = (cap - 1) & ~0x1;
		uint32_t hash = hash1(op,a) & bitmask;  
		uint32_t n = 7 << 1;
		LInsp k;
		while ((k = list[hash]) != NULL && 
			(k->opcode() != op || k->oprnd1() != a))
		{
			hash = (hash + (n += 2)) & bitmask;		// quadratic probe
		}
		i = hash;
		return k;
	}

	LInsp LInsHashSet::find2(LOpcode op, LInsp a, LInsp b, uint32_t &i)
	{
		uint32_t cap = m_cap;
		const LInsp *list = m_list;
		const uint32_t bitmask = (cap - 1) & ~0x1;
		uint32_t hash = hash2(op,a,b) & bitmask;  
		uint32_t n = 7 << 1;
		LInsp k;
		while ((k = list[hash]) != NULL && 
			(k->opcode() != op || k->oprnd1() != a || k->oprnd2() != b))
		{
			hash = (hash + (n += 2)) & bitmask;		// quadratic probe
		}
		i = hash;
		return k;
	}

	bool argsmatch(LInsp i, uint32_t argc, LInsp args[])
	{
		for (uint32_t j=0; j < argc; j++)
			if (i->arg(j) != args[j])
				return false;
		return true;
	}

	LInsp LInsHashSet::findcall(const CallInfo *ci, uint32_t argc, LInsp args[], uint32_t &i)
	{
		uint32_t cap = m_cap;
		const LInsp *list = m_list;
		const uint32_t bitmask = (cap - 1) & ~0x1;
		uint32_t hash = hashcall(ci, argc, args) & bitmask;  
		uint32_t n = 7 << 1;
		LInsp k;
		while ((k = list[hash]) != NULL &&
			(!k->isCall() || k->callInfo() != ci || !argsmatch(k, argc, args)))
		{
			hash = (hash + (n += 2)) & bitmask;		// quadratic probe
		}
		i = hash;
		return k;
	}

    GuardRecord *LIns::record()
    {
        NanoAssert(isGuard());
        return (GuardRecord*)oprnd2()->payload();
    }

#ifdef NJ_VERBOSE
    class RetiredEntry: public GCObject
    {
    public:
        List<LInsp, LIST_NonGCObjects> live;
        LInsp i;
        RetiredEntry(GC *gc): live(gc) {}
    };
	class LiveTable 
	{
	public:
		SortedMap<LInsp,LInsp,LIST_NonGCObjects> live;
        List<RetiredEntry*, LIST_GCObjects> retired;
		int maxlive;
		LiveTable(GC *gc) : live(gc), retired(gc), maxlive(0) {}
        ~LiveTable()
        {
            for (size_t i = 0; i < retired.size(); i++) {
                NJ_DELETE(retired.get(i));
            }

        }
		void add(LInsp i, LInsp use) {
            if (!i->isconst() && !i->isconstq() && !live.containsKey(i)) {
                NanoAssert(size_t(i->opcode()) < sizeof(lirNames) / sizeof(lirNames[0]));
                live.put(i,use);
            }
		}
        void retire(LInsp i, GC *gc) {
            RetiredEntry *e = NJ_NEW(gc, RetiredEntry)(gc);
            e->i = i;
            for (int j=0, n=live.size(); j < n; j++) {
                LInsp l = live.keyAt(j);
                if (!l->isStore() && !l->isGuard())
                    e->live.add(l);
            }
            int size=0;
		    if ((size = e->live.size()) > maxlive)
			    maxlive = size;

            live.remove(i);
            retired.add(e);
		}
		bool contains(LInsp i) {
			return live.containsKey(i);
		}
	};

    void live(GC *gc, LirBuffer *lirbuf)
	{
		// traverse backwards to find live exprs and a few other stats.

		LiveTable live(gc);
		uint32_t exits = 0;
        LirReader br(lirbuf);
		StackFilter sf(&br, gc, lirbuf, lirbuf->sp);
		StackFilter r(&sf, gc, lirbuf, lirbuf->rp);
        int total = 0;
        if (lirbuf->state)
            live.add(lirbuf->state, r.pos());
		for (LInsp i = r.read(); i != 0; i = r.read())
		{
            total++;

            // first handle side-effect instructions
			if (!i->isCse(lirbuf->_functions))
			{
				live.add(i,0);
                if (i->isGuard())
                    exits++;
			}

			// now propagate liveness
			if (live.contains(i))
			{
				live.retire(i,gc);
                NanoAssert(size_t(i->opcode()) < sizeof(operandCount) / sizeof(operandCount[0]));
				if (i->isStore()) {
					live.add(i->oprnd2(),i); // base
					live.add(i->oprnd1(),i); // val
				}
                else if (i->isop(LIR_cmov) || i->isop(LIR_qcmov)) {
                    live.add(i->oprnd1(),i);
                    live.add(i->oprnd2()->oprnd1(),i);
                    live.add(i->oprnd2()->oprnd2(),i);
                }
				else if (operandCount[i->opcode()] == 1) {
				    live.add(i->oprnd1(),i);
				}
				else if (operandCount[i->opcode()] == 2) {
					live.add(i->oprnd1(),i);
					live.add(i->oprnd2(),i);
				}
				else if (i->isCall()) {
					for (int j=0, c=i->argc(); j < c; j++)
						live.add(i->arg(j),i);
				}
			}
		}
 
		printf("live instruction count %d, total %u, max pressure %d\n",
			live.retired.size(), total, live.maxlive);
        printf("side exits %u\n", exits);

		// print live exprs, going forwards
		LirNameMap *names = lirbuf->names;
        bool newblock = true;
		for (int j=live.retired.size()-1; j >= 0; j--) 
        {
            RetiredEntry *e = live.retired[j];
            char livebuf[4000], *s=livebuf;
            *s = 0;
            if (!newblock && e->i->isop(LIR_label)) {
                printf("\n");
            }
            newblock = false;
            for (int k=0,n=e->live.size(); k < n; k++) {
				strcpy(s, names->formatRef(e->live[k]));
				s += strlen(s);
				*s++ = ' '; *s = 0;
				NanoAssert(s < livebuf+sizeof(livebuf));
            }
			printf("%-60s %s\n", livebuf, names->formatIns(e->i));
            if (e->i->isGuard() || e->i->isBranch() || isRet(e->i->opcode())) {
				printf("\n");
                newblock = true;
            }
		}
	}

    LabelMap::Entry::~Entry()
    {
    }

    LirNameMap::Entry::~Entry()
    {
    }

    LirNameMap::~LirNameMap()
    {
        Entry *e;

        while ((e = names.removeLast()) != NULL) {
            labels->core->freeString(e->name);
            NJ_DELETE(e);
        }
    }

	bool LirNameMap::addName(LInsp i, Stringp name) {
		if (!names.containsKey(i)) { 
			Entry *e = NJ_NEW(labels->core->gc, Entry)(name);
			names.put(i, e);
            return true;
		}
        return false;
	}
	void LirNameMap::addName(LInsp i, const char *name) {
        Stringp new_name = labels->core->newString(name);
        if (!addName(i, new_name)) {
            labels->core->freeString(new_name);
        }
	}

	void LirNameMap::copyName(LInsp i, const char *s, int suffix) {
		char s2[200];
		if (isdigit(s[strlen(s)-1])) {
			// if s ends with a digit, add '_' to clarify the suffix
			sprintf(s2,"%s_%d", s, suffix);
		} else {
			sprintf(s2,"%s%d", s, suffix);
		}
		addName(i, labels->core->newString(s2));
	}

	void LirNameMap::formatImm(int32_t c, char *buf) {
		if (c >= 10000 || c <= -10000)
			sprintf(buf,"#%s",labels->format((void*)c));
        else
            sprintf(buf,"%d", c);
	}

	const char* LirNameMap::formatRef(LIns *ref)
	{
		char buffer[200], *buf=buffer;
		buf[0]=0;
		GC *gc = labels->core->gc;
		if (names.containsKey(ref)) {
			StringNullTerminatedUTF8 cname(gc, names.get(ref)->name);
			strcat(buf, cname.c_str());
		}
		else if (ref->isconstq()) {
#if defined NANOJIT_64BIT
            sprintf(buf, "#0x%lx", (nj_printf_ld)ref->constvalq());
#else
			formatImm(uint32_t(ref->constvalq()>>32), buf);
			buf += strlen(buf);
			*buf++ = ':';
			formatImm(uint32_t(ref->constvalq()), buf);
#endif
		}
		else if (ref->isconst()) {
			formatImm(ref->constval(), buf);
		}
		else {
			if (ref->isCall()) {
#if !defined NANOJIT_64BIT
				if (ref->isop(LIR_callh)) {
					// we've presumably seen the other half already
					ref = ref->oprnd1();
				} else {
#endif
					copyName(ref, ref->callInfo()->_name, funccounts.add(ref->callInfo()));
#if !defined NANOJIT_64BIT
				}
#endif
			} else {
                NanoAssert(size_t(ref->opcode()) < sizeof(lirNames) / sizeof(lirNames[0]));
				copyName(ref, lirNames[ref->opcode()], lircounts.add(ref->opcode()));
			}
			StringNullTerminatedUTF8 cname(gc, names.get(ref)->name);
			strcat(buf, cname.c_str());
		}
		return labels->dup(buffer);
	}

	const char* LirNameMap::formatIns(LIns* i)
	{
		char sbuf[200];
		char *s = sbuf;
		LOpcode op = i->opcode();
		switch(op)
		{
			case LIR_short:
			case LIR_int:
			{
                sprintf(s, "%s", formatRef(i));
				break;
			}

            case LIR_alloc: {
                sprintf(s, "%s = %s %d", formatRef(i), lirNames[op], i->size());
                break;
            }

			case LIR_quad:
			{
				int32_t *p = (int32_t*) (i-2);
				sprintf(s, "#%X:%X /* %g */", p[1], p[0], i->constvalf());
				break;
			}

			case LIR_loop:
			case LIR_start:
				sprintf(s, "%s", lirNames[op]);
				break;

#if defined NANOJIT_64BIT
			case LIR_callh:
#endif
			case LIR_fcall:
			case LIR_call: {
				sprintf(s, "%s = %s ( ", formatRef(i), i->callInfo()->_name);
				for (int32_t j=i->argc()-1; j >= 0; j--) {
					s += strlen(s);
					sprintf(s, "%s ",formatRef(i->arg(j)));
				}
				s += strlen(s);
				sprintf(s, ")");
				break;
			}
			case LIR_fcalli:
			case LIR_calli: {
                int32_t argc = i->argc();
				sprintf(s, "%s = [%s] ( ", formatRef(i), formatRef(i->arg(argc-1)));
                s += strlen(s);
                argc--;
				for (int32_t j=argc-1; j >= 0; j--) {
					s += strlen(s);
					sprintf(s, "%s ",formatRef(i->arg(j)));
				}
				s += strlen(s);
				sprintf(s, ")");
				break;
			}

			case LIR_param: { 
				uint32_t arg = i->imm8();
				if (!i->imm8b()) {
					if (arg < sizeof(Assembler::argRegs)/sizeof(Assembler::argRegs[0])) {
						sprintf(s, "%s = %s %d %s", formatRef(i), lirNames[op],
							arg, gpn(Assembler::argRegs[arg]));
					} else {
						sprintf(s, "%s = %s %d", formatRef(i), lirNames[op], arg);
					}
				} else {
					sprintf(s, "%s = %s %d %s", formatRef(i), lirNames[op],
						arg, gpn(Assembler::savedRegs[arg]));
				}
				break;
			}

			case LIR_label:
                sprintf(s, "%s:", formatRef(i));
				break;

			case LIR_jt:
			case LIR_jf:
                sprintf(s, "%s %s -> %s", lirNames[op], formatRef(i->oprnd1()), 
                    i->oprnd2() ? formatRef(i->oprnd2()) : "unpatched");
				break;

			case LIR_j:
                sprintf(s, "%s -> %s", lirNames[op], 
                    i->oprnd2() ? formatRef(i->oprnd2()) : "unpatched");
				break;

            case LIR_live:
			case LIR_ret:
            case LIR_fret:
                sprintf(s, "%s %s", lirNames[op], formatRef(i->oprnd1()));
				break;
				
            case LIR_callh:
			case LIR_neg:
			case LIR_fneg:
			case LIR_i2f:
			case LIR_u2f:
			case LIR_qlo:
			case LIR_qhi:
            case LIR_ov:
            case LIR_cs:
			case LIR_not: 
				sprintf(s, "%s = %s %s", formatRef(i), lirNames[op], formatRef(i->oprnd1()));
				break;

			case LIR_x:
			case LIR_xt:
			case LIR_xf:
			case LIR_xbarrier:
			case LIR_xtbl:
				formatGuard(i, s);
				break;

			case LIR_add:
			case LIR_addp:
			case LIR_sub: 
		 	case LIR_mul: 
			case LIR_fadd:
			case LIR_fsub: 
		 	case LIR_fmul: 
			case LIR_fdiv: 
			case LIR_and: 
			case LIR_or: 
			case LIR_xor: 
			case LIR_lsh: 
			case LIR_rsh:
			case LIR_ush:
			case LIR_eq:
			case LIR_lt:
			case LIR_le:
			case LIR_gt:
			case LIR_ge:
			case LIR_ult:
			case LIR_ule:
			case LIR_ugt:
			case LIR_uge:
			case LIR_feq:
			case LIR_flt:
			case LIR_fle:
			case LIR_fgt:
			case LIR_fge:
            case LIR_qiadd:
            case LIR_qiand:
            case LIR_qilsh:
            case LIR_qior:
				sprintf(s, "%s = %s %s, %s", formatRef(i), lirNames[op],
					formatRef(i->oprnd1()), 
					formatRef(i->oprnd2()));
				break;

			case LIR_qjoin:
				sprintf(s, "%s (%s), %s", lirNames[op],
					formatIns(i->oprnd1()), 
 					formatRef(i->oprnd2()));
 				break;

			case LIR_qcmov:
			case LIR_cmov:
                sprintf(s, "%s = %s %s ? %s : %s", formatRef(i), lirNames[op],
					formatRef(i->oprnd1()), 
					formatRef(i->oprnd2()->oprnd1()), 
					formatRef(i->oprnd2()->oprnd2()));
				break;

			case LIR_ld: 
			case LIR_ldc: 
			case LIR_ldq: 
			case LIR_ldqc: 
			case LIR_ldcb:
			case LIR_ldcs:
				sprintf(s, "%s = %s %s[%s]", formatRef(i), lirNames[op],
					formatRef(i->oprnd1()), 
					formatRef(i->oprnd2()));
				break;

			case LIR_st: 
            case LIR_sti:
			case LIR_stq: 
            case LIR_stqi:
				sprintf(s, "%s %s[%d] = %s", lirNames[op],
					formatRef(i->oprnd2()), 
					i->immdisp(), 
					formatRef(i->oprnd1()));
				break;

			default:
				sprintf(s, "?");
				break;
		}
		return labels->dup(sbuf);
	}


#endif
	CseFilter::CseFilter(LirWriter *out, GC *gc)
		: LirWriter(out), exprs(gc) {}

	LIns* CseFilter::insImm(int32_t imm)
	{
		uint32_t k;
		LInsp found = exprs.find32(imm, k);
		if (found)
			return found;
		return exprs.add(out->insImm(imm), k);
	}

	LIns* CseFilter::insImmq(uint64_t q)
	{
		uint32_t k;
		LInsp found = exprs.find64(q, k);
		if (found)
			return found;
		return exprs.add(out->insImmq(q), k);
	}

	LIns* CseFilter::ins0(LOpcode v)
	{
	    if (v == LIR_label)
	        exprs.clear();
	    return out->ins0(v);
	}
	
	LIns* CseFilter::ins1(LOpcode v, LInsp a)
	{
		if (isCse(v)) {
			NanoAssert(operandCount[v]==1);
			uint32_t k;
			LInsp found = exprs.find1(v, a, k);
			if (found)
				return found;
			return exprs.add(out->ins1(v,a), k);
		}
		return out->ins1(v,a);
	}

	LIns* CseFilter::ins2(LOpcode v, LInsp a, LInsp b)
	{
		if (isCse(v)) {
			NanoAssert(operandCount[v]==2);
			uint32_t k;
			LInsp found = exprs.find2(v, a, b, k);
			if (found)
				return found;
			return exprs.add(out->ins2(v,a,b), k);
		}
		return out->ins2(v,a,b);
	}

	LIns* CseFilter::insLoad(LOpcode v, LInsp base, LInsp disp)
	{
		if (isCse(v)) {
			NanoAssert(operandCount[v]==2);
			uint32_t k;
			LInsp found = exprs.find2(v, base, disp, k);
			if (found)
				return found;
			return exprs.add(out->insLoad(v,base,disp), k);
		}
		return out->insLoad(v,base,disp);
	}

	LInsp CseFilter::insGuard(LOpcode v, LInsp c, LInsp x)
	{
		if (isCse(v)) {
			// conditional guard
			NanoAssert(operandCount[v]==1);
			uint32_t k;
			LInsp found = exprs.find1(v, c, k);
			if (found)
				return 0;
			return exprs.add(out->insGuard(v,c,x), k);
		}
		return out->insGuard(v, c, x);
	}

	LInsp CseFilter::insCall(const CallInfo *ci, LInsp args[])
	{
		if (ci->_cse) {
			uint32_t k;
            uint32_t argc = ci->count_args();
			LInsp found = exprs.findcall(ci, argc, args, k);
			if (found)
				return found;
			return exprs.add(out->insCall(ci, args), k);
		}
		return out->insCall(ci, args);
	}

	CseReader::CseReader(LirFilter *in, LInsHashSet *exprs, const CallInfo *functions)
		: LirFilter(in), exprs(exprs), functions(functions)
	{}

	LInsp CseReader::read()
	{
		LInsp i = in->read();
		if (i) {
			if (i->isCse(functions))
				exprs->replace(i);
		}
		return i;
	}

    LIns* FASTCALL callArgN(LIns* i, uint32_t n)
	{
		return i->arg(i->argc()-n-1);
	}

    void compile(Assembler* assm, Fragment* triggerFrag)
    {
        Fragmento *frago = triggerFrag->lirbuf->_frago;
        AvmCore *core = frago->core();
        GC *gc = core->gc;

		verbose_only( StringList asmOutput(gc); )
		verbose_only( assm->_outputCache = &asmOutput; )

		verbose_only(if (assm->_verbose && core->config.verbose_live)
			live(gc, triggerFrag->lirbuf);)

		bool treeCompile = core->config.tree_opt && (triggerFrag->kind == BranchTrace);
		RegAllocMap regMap(gc);
		NInsList loopJumps(gc);
#ifdef MEMORY_INFO
//		loopJumps.set_meminfo_name("LIR loopjumps");
#endif
		assm->beginAssembly(triggerFrag, &regMap);
		if (assm->error())
			return;

		//fprintf(stderr, "recompile trigger %X kind %d\n", (int)triggerFrag, triggerFrag->kind);
		Fragment* root = triggerFrag;
		if (treeCompile)
		{
			// recompile the entire tree
			root = triggerFrag->root;
			root->fragEntry = 0;
			root->loopEntry = 0;
			root->releaseCode(frago);
			
			// do the tree branches
			Fragment* frag = root->treeBranches;
			while(frag)
			{
				// compile til no more frags
				if (frag->lastIns)
				{
					assm->assemble(frag, loopJumps);
					verbose_only(if (assm->_verbose) 
						assm->outputf("compiling branch %s ip %s",
							frago->labels->format(frag),
							frago->labels->format(frag->ip)); )
					
					NanoAssert(frag->kind == BranchTrace);
					RegAlloc* regs = NJ_NEW(gc, RegAlloc)();
					assm->copyRegisters(regs);
					assm->releaseRegisters();
					SideExit* exit = frag->spawnedFrom;
					regMap.put(exit, regs);
				}
				frag = frag->treeBranches;
			}
		}
		
		// now the the main trunk
		assm->assemble(root, loopJumps);
		verbose_only(if (assm->_verbose) 
			assm->outputf("compiling trunk %s",
				frago->labels->format(root));)
		NanoAssert(!frago->core()->config.tree_opt || root == root->anchor || root->kind == MergeTrace);			
		assm->endAssembly(root, loopJumps);
			
		// reverse output so that assembly is displayed low-to-high
		verbose_only( assm->_outputCache = 0; )
		verbose_only(for(int i=asmOutput.size()-1; i>=0; --i) { assm->outputf("%s",asmOutput.get(i)); } );

		if (assm->error()) {
			root->fragEntry = 0;
			root->loopEntry = 0;
		}
    }

    LInsp LoadFilter::insLoad(LOpcode v, LInsp base, LInsp disp)
    {
        if (base != sp && base != rp && (v == LIR_ld || v == LIR_ldq)) {
            uint32_t k;
            LInsp found = exprs.find2(v, base, disp, k);
            if (found)
                return found;
            return exprs.add(out->insLoad(v,base,disp), k);
        }
        return out->insLoad(v, base, disp);
    }

    void LoadFilter::clear(LInsp p)
    {
        if (p != sp && p != rp)
            exprs.clear();
    }

    LInsp LoadFilter::insStore(LInsp v, LInsp b, LInsp d)
    {
        clear(b);
        return out->insStore(v, b, d);
    }

    LInsp LoadFilter::insStorei(LInsp v, LInsp b, int32_t d)
    {
        clear(b);
        return out->insStorei(v, b, d);
    }

    LInsp LoadFilter::insCall(const CallInfo *ci, LInsp args[])
    {
        if (!ci->_cse)
            exprs.clear();
        return out->insCall(ci, args);
    }

    LInsp LoadFilter::ins0(LOpcode op)
    {
        if (op == LIR_label)
            exprs.clear();
        return out->ins0(op);
    }

	#endif /* FEATURE_NANOJIT */

#if defined(NJ_VERBOSE)
    LabelMap::LabelMap(AvmCore *core, LabelMap* parent)
        : parent(parent), names(core->gc), addrs(core->config.verbose_addrs), end(buf), core(core)
	{}

    LabelMap::~LabelMap()
    {
        clear();
    }

    void LabelMap::clear()
    {
        Entry *e;
        while ((e = names.removeLast()) != NULL) {
            core->freeString(e->name);
            NJ_DELETE(e);
        } 
    }

    void LabelMap::add(const void *p, size_t size, size_t align, const char *name)
	{
		if (!this || names.containsKey(p))
			return;
		add(p, size, align, core->newString(name));
	}

    void LabelMap::add(const void *p, size_t size, size_t align, Stringp name)
    {
		if (!this || names.containsKey(p))
			return;
		Entry *e = NJ_NEW(core->gc, Entry)(name, size<<align, align);
		names.put(p, e);
    }

    const char *LabelMap::format(const void *p)
    {
		char b[200];
		int i = names.findNear(p);
		if (i >= 0) {
			const void *start = names.keyAt(i);
			Entry *e = names.at(i);
			const void *end = (const char*)start + e->size;
			avmplus::StringNullTerminatedUTF8 cname(core->gc, e->name);
			const char *name = cname.c_str();
			if (p == start) {
				if (addrs)
					sprintf(b,"%p %s",p,name);
				else
					strcpy(b, name);
				return dup(b);
			}
			else if (p > start && p < end) {
				int32_t d = int32_t(intptr_t(p)-intptr_t(start)) >> e->align;
				if (addrs)
					sprintf(b, "%p %s+%d", p, name, d);
				else
					sprintf(b,"%s+%d", name, d);
				return dup(b);
			}
			else {
				if (parent)
					return parent->format(p);

				sprintf(b, "%p", p);
				return dup(b);
			}
		}
		if (parent)
			return parent->format(p);

		sprintf(b, "%p", p);
		return dup(b);
    }

	const char *LabelMap::dup(const char *b)
	{
		size_t need = strlen(b)+1;
		char *s = end;
		end += need;
		if (end > buf+sizeof(buf)) {
			s = buf;
			end = s+need;
		}
		strcpy(s, b);
		return s;
	}

	// copy all labels to parent, adding newbase to label addresses
	void LabelMap::promoteAll(const void *newbase)
	{
		for (int i=0, n=names.size(); i < n; i++) {
			void *base = (char*)newbase + (intptr_t)names.keyAt(i);
			parent->names.put(base, names.at(i));
		}
	}
#endif // NJ_VERBOSE
}