/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=4 sw=4 et tw=99:
 * ***** 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 LIR Assembler code, released 2009.
 *
 * The Initial Developer of the Original Code is
 * Mozilla Corporation.
 * Portions created by the Initial Developer are Copyright (C) 2009
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *  Graydon Hoare <graydon@mozilla.com>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of 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 <vector>
#include <algorithm>
#include <map>
#include <string>
#include <iostream>
#include <sstream>
#include <fstream>

#ifdef AVMPLUS_UNIX
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif

#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include <assert.h>

#include "nanojit/nanojit.h"

using namespace nanojit;
using namespace std;

/* Allocator SPI implementation. */

void*
nanojit::Allocator::allocChunk(size_t nbytes, bool /*fallible*/)
{
    void *p = malloc(nbytes);
    if (!p)
        exit(1);
    return p;
}

void
nanojit::Allocator::freeChunk(void *p) {
    free(p);
}

void
nanojit::Allocator::postReset() {
}


struct LasmSideExit : public SideExit {
    size_t line;
};


/* LIR SPI implementation */

int
nanojit::StackFilter::getTop(LIns*)
{
    return 0;
}

// We lump everything into a single access region for lirasm.
static const AccSet ACCSET_OTHER = (1 << 0);
static const uint8_t LIRASM_NUM_USED_ACCS = 1;

#if defined NJ_VERBOSE
void
nanojit::LInsPrinter::formatGuard(InsBuf *buf, LIns *ins)
{
    RefBuf b1, b2;
    LasmSideExit *x = (LasmSideExit *)ins->record()->exit;
    VMPI_snprintf(buf->buf, buf->len,
            "%s: %s %s -> line=%ld (GuardID=%03d)",
            formatRef(&b1, ins),
            lirNames[ins->opcode()],
            ins->oprnd1() ? formatRef(&b2, ins->oprnd1()) : "",
            (long)x->line,
            ins->record()->profGuardID);
}

void
nanojit::LInsPrinter::formatGuardXov(InsBuf *buf, LIns *ins)
{
    RefBuf b1, b2, b3;
    LasmSideExit *x = (LasmSideExit *)ins->record()->exit;
    VMPI_snprintf(buf->buf, buf->len,
            "%s = %s %s, %s -> line=%ld (GuardID=%03d)",
            formatRef(&b1, ins),
            lirNames[ins->opcode()],
            formatRef(&b2, ins->oprnd1()),
            formatRef(&b3, ins->oprnd2()),
            (long)x->line,
            ins->record()->profGuardID);
}

const char*
nanojit::LInsPrinter::accNames[] = {
    "o",    // (1 << 0) == ACCSET_OTHER
    "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",   //  1..10 (unused)
    "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",   // 11..20 (unused)
    "?", "?", "?", "?", "?", "?", "?", "?", "?", "?",   // 21..30 (unused)
    "?"                                                 //     31 (unused)
};
#endif

#ifdef DEBUG
void ValidateWriter::checkAccSet(LOpcode op, LIns* base, int32_t disp, AccSet accSet)
{
    (void)op;
    (void)base;
    (void)disp;
    NanoAssert(accSet == ACCSET_OTHER);
}
#endif

typedef int32_t (FASTCALL *RetInt)();
typedef int64_t (FASTCALL *RetQuad)();
typedef double (FASTCALL *RetDouble)();
typedef GuardRecord* (FASTCALL *RetGuard)();

struct Function {
    const char *name;
    struct nanojit::CallInfo callInfo;
};

enum ReturnType {
    RT_INT = 1,
#ifdef NANOJIT_64BIT
    RT_QUAD = 2,
#endif
    RT_DOUBLE = 4,
    RT_GUARD = 8
};

#ifdef DEBUG
#define DEBUG_ONLY_NAME(name)   ,#name
#else
#define DEBUG_ONLY_NAME(name)
#endif

#define CI(name, args) \
    {(uintptr_t) (&name), args, nanojit::ABI_CDECL, /*isPure*/0, ACCSET_STORE_ANY \
     DEBUG_ONLY_NAME(name)}

#define FN(name, args) \
    {#name, CI(name, args)}

enum LirTokenType {
    NAME, NUMBER, PUNCT, NEWLINE
};

struct LirToken {
    LirTokenType type;
    string data;
    int lineno;
};

inline bool
startsWith(const string &s, const string &prefix)
{
    return s.size() >= prefix.size() && s.compare(0, prefix.length(), prefix) == 0;
}

// LIR files must be ASCII, for simplicity.
class LirTokenStream {
public:
    LirTokenStream(istream &in) : mIn(in), mLineno(0) {}

    bool get(LirToken &token) {
        if (mLine.empty()) {
            if (!getline(mIn, mLine))
                return false;
            mLine += '\n';
            mLineno++;
        }
        mLine.erase(0, mLine.find_first_not_of(" \t\v\r"));
        char c = mLine[0];
        size_t e = mLine.find_first_not_of("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$.+-");
        if (startsWith(mLine, "->")) {
            mLine.erase(0, 2);
            token.type = PUNCT;
            token.data = "->";
        } else if (e > 0) {
            string s = mLine.substr(0, e);
            mLine.erase(0, e);
            if (e > 1 && s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
                token.type = NUMBER;
            else if (isdigit(s[0]) || (e > 1 && s[0] == '.' && isdigit(s[1])))
                token.type = NUMBER;
            else
                token.type = NAME;
            token.data = s;
        } else if (strchr(":,=[]()", c)) {
            token.type = PUNCT;
            token.data = c;
            mLine.erase(0, 1);
        } else if (c == ';' || c == '\n') {
            token.type = NEWLINE;
            token.data.clear();
            mLine.clear();
        } else {
            cerr << "line " << mLineno << ": error: Unrecognized character in file." << endl;
            return false;
        }

        token.lineno = mLineno;
        return true;
    }

    bool eat(LirTokenType type, const char *exact = NULL) {
        LirToken token;
        return (get(token) && token.type == type && (exact == NULL || token.data == exact));
    }

    bool getName(string &name) {
        LirToken t;
        if (get(t) && t.type == NAME) {
            name = t.data;
            return true;
        }
        return false;
    }

private:
    istream &mIn;
    string mLine;
    int mLineno;
};

class LirasmFragment {
public:
    union {
        RetInt rint;
#ifdef NANOJIT_64BIT
        RetQuad rquad;
#endif
        RetDouble rdouble;
        RetGuard rguard;
    };
    ReturnType mReturnType;
    Fragment *fragptr;
    map<string, LIns*> mLabels;
};

typedef map<string, LirasmFragment> Fragments;

class Lirasm {
public:
    Lirasm(bool verbose, Config& config);
    ~Lirasm();

    void assemble(istream &in, bool optimize);
    void assembleRandom(int nIns, bool optimize);
    bool lookupFunction(const string &name, CallInfo *&ci);

    LirBuffer *mLirbuf;
    LogControl mLogc;
    Config mConfig;
    Allocator mAlloc;
    CodeAlloc mCodeAlloc;
    bool mVerbose;
    Fragments mFragments;
    Assembler mAssm;
    map<string, LOpcode> mOpMap;

    void bad(const string &msg) {
        cerr << "error: " << msg << endl;
        exit(1);
    }

private:
    void handlePatch(LirTokenStream &in);
};

class FragmentAssembler {
public:
    FragmentAssembler(Lirasm &parent, const string &fragmentName, bool optimize);
    ~FragmentAssembler();

    void assembleFragment(LirTokenStream &in,
                          bool implicitBegin,
                          const LirToken *firstToken);

    void assembleRandomFragment(int nIns);

private:
    static uint32_t sProfId;
    // Prohibit copying.
    FragmentAssembler(const FragmentAssembler &);
    FragmentAssembler & operator=(const FragmentAssembler &);
    LasmSideExit *createSideExit();
    GuardRecord *createGuardRecord(LasmSideExit *exit);

    Lirasm &mParent;
    const string mFragName;
    Fragment *mFragment;
    bool optimize;
    vector<CallInfo*> mCallInfos;
    map<string, LIns*> mLabels;
    LirWriter *mLir;
    LirBufWriter *mBufWriter;
    LirWriter *mCseFilter;
    LirWriter *mExprFilter;
    LirWriter *mSoftFloatFilter;
    LirWriter *mVerboseWriter;
    LirWriter *mValidateWriter1;
    LirWriter *mValidateWriter2;
    vector< pair<string, LIns*> > mJumps;
    map<string, LIns*> mJumpLabels;

    size_t mLineno;
    LOpcode mOpcode;
    size_t mOpcount;

    char mReturnTypeBits;
    vector<string> mTokens;

    void tokenizeLine(LirTokenStream &in, LirToken &token);
    void need(size_t);
    LIns *ref(const string &);
    LIns *assemble_jump(bool isCond);
    LIns *assemble_load();
    LIns *assemble_call(const string &);
    LIns *assemble_ret(ReturnType rt);
    LIns *assemble_guard(bool isCond);
    LIns *assemble_guard_xov();
    LIns *assemble_jump_jov();
    void bad(const string &msg);
    void nyi(const string &opname);
    void extract_any_label(string &lab, char lab_delim);
    void resolve_jumps();
    void add_jump_label(const string& lab, LIns* ins);
    void endFragment();
};

// 'sin' is overloaded on some platforms, so taking its address
// doesn't quite work. Provide a do-nothing function here
// that's not overloaded.
double sinFn(double d) {
    return sin(d);
}
#define sin sinFn

double calld1(double x, double i, double y, double l, double x1, double i1, double y1, double l1) { 
    return x + i * y - l + x1 / i1 - y1 * l1; 
}

// The calling tests with mixed argument types are sensible for all platforms, but they highlight
// the differences between the supported ABIs on ARM.

double callid1(int i, double x, double y, int j, int k, double z) {
    return (x + y + z) / (double)(i + j + k);
}

double callid2(int i, int j, int k, double x) {
    return x / (double)(i + j + k);
}

double callid3(int i, int j, double x, int k, double y, double z) {
    return (x + y + z) / (double)(i + j + k);
}

// Simple print function for testing void calls.
void printi(int x) {
    cout << x << endl;
}

Function functions[] = {
    FN(puts,    CallInfo::typeSig1(ARGTYPE_I, ARGTYPE_P)),
    FN(sin,     CallInfo::typeSig1(ARGTYPE_D, ARGTYPE_D)),
    FN(malloc,  CallInfo::typeSig1(ARGTYPE_P, ARGTYPE_P)),
    FN(free,    CallInfo::typeSig1(ARGTYPE_V, ARGTYPE_P)),
    FN(calld1,  CallInfo::typeSig8(ARGTYPE_D, ARGTYPE_D, ARGTYPE_D, ARGTYPE_D,
                                   ARGTYPE_D, ARGTYPE_D, ARGTYPE_D, ARGTYPE_D, ARGTYPE_D)),
    FN(callid1, CallInfo::typeSig6(ARGTYPE_D, ARGTYPE_I, ARGTYPE_D, ARGTYPE_D,
                                   ARGTYPE_I, ARGTYPE_I, ARGTYPE_D)),
    FN(callid2, CallInfo::typeSig4(ARGTYPE_D, ARGTYPE_I, ARGTYPE_I, ARGTYPE_I, ARGTYPE_D)),
    FN(callid3, CallInfo::typeSig6(ARGTYPE_D, ARGTYPE_I, ARGTYPE_I, ARGTYPE_D,
                                   ARGTYPE_I, ARGTYPE_D, ARGTYPE_D)),
    FN(printi,  CallInfo::typeSig1(ARGTYPE_V, ARGTYPE_I)),
};

template<typename out, typename in> out
lexical_cast(in arg)
{
    stringstream tmp;
    out ret;
    if ((tmp << arg && tmp >> ret && tmp.eof()))
        return ret;
    cerr << "bad lexical cast from " << arg << endl;
    exit(1);
}

int32_t
immI(const string &s)
{
    stringstream tmp(s);
    int32_t ret;
    if ((s.find("0x") == 0 || s.find("0X") == 0) &&
        (tmp >> hex >> ret && tmp.eof())) {
        return ret;
    }
    return lexical_cast<int32_t>(s);
}

uint64_t
immQ(const string &s)
{
    stringstream tmp(s);
    uint64_t ret;
    if ((s.find("0x") == 0 || s.find("0X") == 0) &&
        (tmp >> hex >> ret && tmp.eof())) {
        return ret;
    }
    return lexical_cast<uint64_t>(s);
}

double
immD(const string &s)
{
    return lexical_cast<double>(s);
}

template<typename t> t
pop_front(vector<t> &vec)
{
    if (vec.empty()) {
        cerr << "pop_front of empty vector" << endl;
        exit(1);
    }
   t tmp = vec[0];
   vec.erase(vec.begin());
   return tmp;
}

void
dep_u8(char *&buf, uint8_t byte, uint32_t &cksum)
{
    sprintf(buf, "%2.2X", byte);
    cksum += byte;
    buf += 2;
}

void
dep_u32(char *&buf, uint32_t word, uint32_t &cksum)
{
    dep_u8(buf, (uint8_t)((word >> 24) & 0xff), cksum);
    dep_u8(buf, (uint8_t)((word >> 16) & 0xff), cksum);
    dep_u8(buf, (uint8_t)((word >> 8) & 0xff), cksum);
    dep_u8(buf, (uint8_t)((word) & 0xff), cksum);
}

void
dump_srecords(ostream &, Fragment *)
{
    // FIXME: Disabled until we work out a sane way to walk through
    // code chunks under the new CodeAlloc regime.
/*
    // Write S-records. Can only do 4-byte addresses at the moment.

    // FIXME: this presently dumps out the entire set of code pages
    // written-to, which means it often dumps *some* bytes on the last
    // page that are not necessarily initialized at all; they're
    // beyond the last instruction written. Fix this to terminate
    // s-record writing early.

    assert(sizeof(uintptr_t) == 4);
    for (Page *page = frag->pages(); page; page = page->next) {
        size_t step = 32;
        uintptr_t p0 = (uintptr_t) &(page->code);
        for (uintptr_t p = p0; p < p0 + sizeof(page->code); p += step) {
            char buf[1024];

            // S-record type S3: 8-char / 4-byte address.
            //
            //     +2 char code 'S3'.
            //     +2 char / 1 byte count of remaining bytes (37 = addr, payload, cksum).
            //     +8 char / 4 byte addr.
            //    ---
            //    +64 char / 32 byte payload.
            //    ---
            //     +2 char / 1 byte checksum.

            uint32_t cksum = 0;
            size_t count = sizeof(p) + step + 1;

            sprintf(buf, "S3");

            char *b = buf + 2; // 2 chars for the "S3" code.

            dep_u8(b, (uint8_t) count, cksum); // Count of data bytes
            dep_u32(b, p, cksum); // Address of the data byte being emitted
            uint8_t *c = (uint8_t*) p;
            for (size_t i = 0; i < step; ++i) { // Actual object code being emitted
                dep_u8(b, c[i], cksum);
            }
            dep_u8(b, (uint8_t)((~cksum) & 0xff), cksum);
            out << string(buf) << endl;
        }
    }
*/
}



uint32_t
FragmentAssembler::sProfId = 0;

FragmentAssembler::FragmentAssembler(Lirasm &parent, const string &fragmentName, bool optimize)
    : mParent(parent), mFragName(fragmentName), optimize(optimize),
      mBufWriter(NULL), mCseFilter(NULL), mExprFilter(NULL), mSoftFloatFilter(NULL), mVerboseWriter(NULL),
      mValidateWriter1(NULL), mValidateWriter2(NULL)
{
    mFragment = new Fragment(NULL verbose_only(, (mParent.mLogc.lcbits &
                                                  nanojit::LC_FragProfile) ?
                                                  sProfId++ : 0));
    mFragment->lirbuf = mParent.mLirbuf;
    mParent.mFragments[mFragName].fragptr = mFragment;

    mLir = mBufWriter  = new LirBufWriter(mParent.mLirbuf, mParent.mConfig);
#ifdef DEBUG
    if (optimize) {     // don't re-validate if no optimization has taken place
        mLir = mValidateWriter2 =
            new ValidateWriter(mLir, mFragment->lirbuf->printer, "end of writer pipeline");
    }
#endif
#ifdef DEBUG
    if (mParent.mVerbose) {
        mLir = mVerboseWriter = new VerboseWriter(mParent.mAlloc, mLir,
                                                  mParent.mLirbuf->printer,
                                                  &mParent.mLogc);
    }
#endif
    if (optimize) {
        mLir = mCseFilter = new CseFilter(mLir, LIRASM_NUM_USED_ACCS, mParent.mAlloc);
    }
#if NJ_SOFTFLOAT_SUPPORTED
    if (mParent.mConfig.soft_float) {
        mLir = new SoftFloatFilter(mLir);
    }
#endif
    if (optimize) {
        mLir = mExprFilter = new ExprFilter(mLir);
    }
#ifdef DEBUG
    mLir = mValidateWriter1 =
            new ValidateWriter(mLir, mFragment->lirbuf->printer, "start of writer pipeline");
#endif

    mReturnTypeBits = 0;
    mLir->ins0(LIR_start);
    for (int i = 0; i < nanojit::NumSavedRegs; ++i)
        mLir->insParam(i, 1);

    mLineno = 0;
}

FragmentAssembler::~FragmentAssembler()
{
    delete mValidateWriter1;
    delete mValidateWriter2;
    delete mVerboseWriter;
    delete mExprFilter;
    delete mSoftFloatFilter;
    delete mCseFilter;
    delete mBufWriter;
}


void
FragmentAssembler::bad(const string &msg)
{
    cerr << "line " << mLineno << ": " << msg << endl;
    exit(1);
}

void
FragmentAssembler::nyi(const string &opname)
{
    cerr << "line " << mLineno << ": '" << opname << "' not yet implemented, sorry" << endl;
    exit(1);
}

void
FragmentAssembler::need(size_t n)
{
    if (mTokens.size() != n) {
        bad("need " + lexical_cast<string>(n)
            + " tokens, have " + lexical_cast<string>(mTokens.size()));
    }
}

LIns *
FragmentAssembler::ref(const string &lab)
{
    if (mLabels.find(lab) == mLabels.end())
        bad("unknown label '" + lab + "'");
    return mLabels.find(lab)->second;
}

LIns *
FragmentAssembler::assemble_jump(bool isCond)
{
    LIns *condition;

    if (isCond) {
        need(2);
        string cond = pop_front(mTokens);
        condition = ref(cond);
    } else {
        need(1);
        condition = NULL;
    }
    string name = pop_front(mTokens);
    LIns *ins = mLir->insBranch(mOpcode, condition, NULL);
    mJumps.push_back(make_pair<string, LIns*>(name, ins));
    return ins;
}

LIns *
FragmentAssembler::assemble_load()
{
    // Support implicit immediate-as-second-operand modes
    // since, unlike sti/stqi, no immediate-displacement
    // load opcodes were defined in LIR.
    need(2);
    if (mTokens[1].find("0x") == 0 ||
        mTokens[1].find("0x") == 0 ||
        mTokens[1].find_first_of("0123456789") == 0) {
        return mLir->insLoad(mOpcode,
                             ref(mTokens[0]),
                             immI(mTokens[1]), ACCSET_OTHER);
    }
    bad("immediate offset required for load");
    return NULL;  // not reached
}

LIns *
FragmentAssembler::assemble_call(const string &op)
{
    CallInfo *ci = new (mParent.mAlloc) CallInfo;
    mCallInfos.push_back(ci);
    LIns *args[MAXARGS];
    memset(&args[0], 0, sizeof(args));

    // Assembler syntax for a call:
    //
    //   call 0x1234 fastcall a b c
    //
    // requires at least 2 args,
    // fn address immediate and ABI token.

    if (mTokens.size() < 2)
        bad("need at least address and ABI code for " + op);

    string func = pop_front(mTokens);
    string abi = pop_front(mTokens);

    AbiKind _abi = ABI_CDECL;
    if (abi == "fastcall")
        _abi = ABI_FASTCALL;
    else if (abi == "stdcall")
        _abi = ABI_STDCALL;
    else if (abi == "thiscall")
        _abi = ABI_THISCALL;
    else if (abi == "cdecl")
        _abi = ABI_CDECL;
    else
        bad("call abi name '" + abi + "'");

    if (mTokens.size() > MAXARGS)
    bad("too many args to " + op);

    bool isBuiltin = mParent.lookupFunction(func, ci);
    if (isBuiltin) {
        // Built-in:  use its CallInfo.  Also check (some) CallInfo details
        // against those from the call site.
        if (_abi != ci->_abi)
            bad("invalid calling convention for " + func);

        size_t i;
        for (i = 0; i < mTokens.size(); ++i) {
            args[i] = ref(mTokens[mTokens.size() - (i+1)]);
        }
        if (i != ci->count_args())
            bad("wrong number of arguments for " + func);

    } else {
        // User-defined function:  infer CallInfo details (ABI, arg types, ret
        // type) from the call site.
        ci->_abi = _abi;
        size_t argc = mTokens.size();
        ArgType argTypes[MAXARGS];
        for (size_t i = 0; i < argc; ++i) {
            NanoAssert(i < MAXARGS);    // should give a useful error msg if this fails
            args[i] = ref(mTokens[mTokens.size() - (i+1)]);
            if      (args[i]->isD()) argTypes[i] = ARGTYPE_D;
#ifdef NANOJIT_64BIT
            else if (args[i]->isQ()) argTypes[i] = ARGTYPE_Q;
#endif
            else                     argTypes[i] = ARGTYPE_I;
        }

        // Select return type from opcode.
        ArgType retType = ARGTYPE_P;
        if      (mOpcode == LIR_callv) retType = ARGTYPE_V;
        else if (mOpcode == LIR_calli) retType = ARGTYPE_I;
#ifdef NANOJIT_64BIT
        else if (mOpcode == LIR_callq) retType = ARGTYPE_Q;
#endif
        else if (mOpcode == LIR_calld) retType = ARGTYPE_D;
        else                           nyi("callh");
        ci->_typesig = CallInfo::typeSigN(retType, argc, argTypes);
    }

    return mLir->insCall(ci, args);
}

LIns *
FragmentAssembler::assemble_ret(ReturnType rt)
{
    need(1);
    mReturnTypeBits |= rt;
    return mLir->ins1(mOpcode, ref(mTokens[0]));
}

LasmSideExit*
FragmentAssembler::createSideExit()
{
    LasmSideExit* exit = new (mParent.mAlloc) LasmSideExit();
    memset(exit, 0, sizeof(LasmSideExit));
    exit->from = mFragment;
    exit->target = NULL;
    exit->line = mLineno;
    return exit;
}

GuardRecord*
FragmentAssembler::createGuardRecord(LasmSideExit *exit)
{
    GuardRecord *rec = new (mParent.mAlloc) GuardRecord;
    memset(rec, 0, sizeof(GuardRecord));
    rec->exit = exit;
    exit->addGuard(rec);
    return rec;
}

LIns *
FragmentAssembler::assemble_guard(bool isCond)
{
    GuardRecord* guard = createGuardRecord(createSideExit());

    LIns *ins_cond;
    if (isCond) {
        need(1);
        ins_cond = ref(pop_front(mTokens));
    } else {
        need(0);
        ins_cond = NULL;
    }

    mReturnTypeBits |= RT_GUARD;

    if (!mTokens.empty())
        bad("too many arguments");

    return mLir->insGuard(mOpcode, ins_cond, guard);
}

LIns*
FragmentAssembler::assemble_guard_xov()
{
    GuardRecord* guard = createGuardRecord(createSideExit());

    need(2);

    mReturnTypeBits |= RT_GUARD;

    return mLir->insGuardXov(mOpcode, ref(mTokens[0]), ref(mTokens[1]), guard);
}

LIns *
FragmentAssembler::assemble_jump_jov()
{
    need(3);

    LIns *a = ref(mTokens[0]);
    LIns *b = ref(mTokens[1]);
    string name = mTokens[2];

    LIns *ins = mLir->insBranchJov(mOpcode, a, b, NULL);
    mJumps.push_back(make_pair<string, LIns*>(name, ins));
    return ins;
}

void
FragmentAssembler::endFragment()
{
    // Resolve all of the jumps in this fragment.
    resolve_jumps();

    if (mReturnTypeBits == 0) {
        cerr << "warning: no return type in fragment '"
             << mFragName << "'" << endl;

    } else if (mReturnTypeBits != RT_INT && 
#ifdef NANOJIT_64BIT
               mReturnTypeBits != RT_QUAD &&
#endif
               mReturnTypeBits != RT_DOUBLE &&
               mReturnTypeBits != RT_GUARD)
    {
        cerr << "warning: multiple return types in fragment '"
             << mFragName << "'" << endl;
    }

    mFragment->lastIns =
        mLir->insGuard(LIR_x, NULL, createGuardRecord(createSideExit()));

    mParent.mAssm.compile(mFragment, mParent.mAlloc, optimize
              verbose_only(, mParent.mLirbuf->printer));

    if (mParent.mAssm.error() != nanojit::None) {
        cerr << "error during assembly: ";
        switch (mParent.mAssm.error()) {
          case nanojit::BranchTooFar: cerr << "BranchTooFar"; break;
          case nanojit::StackFull: cerr << "StackFull"; break;
          case nanojit::UnknownBranch:  cerr << "UnknownBranch"; break;
          case nanojit::None: cerr << "None"; break;
          default: NanoAssert(0); break;
        }
        cerr << endl;
        std::exit(1);
    }

    LirasmFragment *f;
    f = &mParent.mFragments[mFragName];

    switch (mReturnTypeBits) {
    case RT_INT:
        f->rint = (RetInt)((uintptr_t)mFragment->code());
        f->mReturnType = RT_INT;
        break;
#ifdef NANOJIT_64BIT
    case RT_QUAD:
        f->rquad = (RetQuad)((uintptr_t)mFragment->code());
        f->mReturnType = RT_QUAD;
        break;
#endif
    case RT_DOUBLE:
        f->rdouble = (RetDouble)((uintptr_t)mFragment->code());
        f->mReturnType = RT_DOUBLE;
        break;
    case RT_GUARD:
        f->rguard = (RetGuard)((uintptr_t)mFragment->code());
        f->mReturnType = RT_GUARD;
        break;
    default:
        NanoAssert(0);
        break;
    }

    mParent.mFragments[mFragName].mLabels = mLabels;
}

void
FragmentAssembler::tokenizeLine(LirTokenStream &in, LirToken &token)
{
    mTokens.clear();
    mTokens.push_back(token.data);

    while (in.get(token)) {
        if (token.type == NEWLINE)
            break;
        mTokens.push_back(token.data);
    }
}

void
FragmentAssembler::extract_any_label(string &lab, char lab_delim)
{
    if (mTokens.size() > 2 && mTokens[1].size() == 1 && mTokens[1][0] == lab_delim) {
        lab = pop_front(mTokens);
        pop_front(mTokens);  // remove punctuation

        if (mLabels.find(lab) != mLabels.end())
            bad("duplicate label");
    }
}

void
FragmentAssembler::resolve_jumps()
{
    typedef vector< pair<string, LIns*> > pairvec;
    typedef pairvec::const_iterator pv_ci;

    typedef map< string, LIns* > labelmap;
    typedef labelmap::const_iterator lm_ci;

    for ( pv_ci i = mJumps.begin(); i != mJumps.end(); ++i ) {
        lm_ci target = mJumpLabels.find(i->first);
        if ( target == mJumpLabels.end() )
            bad("No label exists for jump target '" + i->first + "'");
        i->second->setTarget( target->second );
    }
}

void
FragmentAssembler::add_jump_label(const string& lab, LIns* ins)
{
    if ( mJumpLabels.find(lab) != mJumpLabels.end() )
        bad("Label '" + lab + "' found at multiple locations.");
    mJumpLabels[lab] = ins;
}

void
FragmentAssembler::assembleFragment(LirTokenStream &in, bool implicitBegin, const LirToken *firstToken)
{
    LirToken token;
    while (true) {
        if (firstToken) {
            token = *firstToken;
            firstToken = NULL;
        } else if (!in.get(token)) {
            if (!implicitBegin)
                bad("unexpected end of file in fragment '" + mFragName + "'");
            break;
        }
        if (token.type == NEWLINE)
            continue;
        if (token.type != NAME)
            bad("unexpected token '" + token.data + "'");

        string op = token.data;
        if (op == ".begin")
            bad("nested fragments are not supported");
        if (op == ".end") {
            if (implicitBegin)
                bad(".end without .begin");
            if (!in.eat(NEWLINE))
                bad("extra junk after .end");
            break;
        }

        mLineno = token.lineno;
        tokenizeLine(in, token);

        string lab;
        LIns *ins = NULL;
        extract_any_label(lab, ':');

        /* Save label as a jump label */
        if (!lab.empty()) {
            ins = mLir->ins0(LIR_label);
            add_jump_label(lab, ins);
            lab.clear();
        }
        extract_any_label(lab, '=');

        assert(!mTokens.empty());
        op = pop_front(mTokens);
        if (mParent.mOpMap.find(op) == mParent.mOpMap.end())
            bad("unknown instruction '" + op + "'");

        mOpcode = mParent.mOpMap[op];

        switch (mOpcode) {
          case LIR_start:
            bad("start instructions cannot be specified explicitly");
            break;

          case LIR_regfence:
            need(0);
            ins = mLir->ins0(mOpcode);
            break;

          case LIR_livei:
          CASE64(LIR_liveq:)
          case LIR_lived:
          case LIR_negi:
          case LIR_negd:
          case LIR_noti:
          CASESF(LIR_dlo2i:)
          CASESF(LIR_dhi2i:)
          CASE64(LIR_q2i:)
          CASE64(LIR_i2q:)
          CASE64(LIR_ui2uq:)
          CASE64(LIR_dasq:)
          CASE64(LIR_qasd:)
          case LIR_i2d:
          case LIR_ui2d:
          case LIR_d2i:
#if defined NANOJIT_IA32 || defined NANOJIT_X64
          case LIR_modi:
#endif
            need(1);
            ins = mLir->ins1(mOpcode,
                             ref(mTokens[0]));
            break;

          case LIR_addi:
          case LIR_subi:
          case LIR_muli:
#if defined NANOJIT_IA32 || defined NANOJIT_X64
          case LIR_divi:
#endif
          case LIR_addd:
          case LIR_subd:
          case LIR_muld:
          case LIR_divd:
          CASE64(LIR_addq:)
          CASE64(LIR_subq:)
          case LIR_andi:
          case LIR_ori:
          case LIR_xori:
          CASE64(LIR_andq:)
          CASE64(LIR_orq:)
          CASE64(LIR_xorq:)
          case LIR_lshi:
          case LIR_rshi:
          case LIR_rshui:
          CASE64(LIR_lshq:)
          CASE64(LIR_rshq:)
          CASE64(LIR_rshuq:)
          case LIR_eqi:
          case LIR_lti:
          case LIR_gti:
          case LIR_lei:
          case LIR_gei:
          case LIR_ltui:
          case LIR_gtui:
          case LIR_leui:
          case LIR_geui:
          case LIR_eqd:
          case LIR_ltd:
          case LIR_gtd:
          case LIR_led:
          case LIR_ged:
          CASE64(LIR_eqq:)
          CASE64(LIR_ltq:)
          CASE64(LIR_gtq:)
          CASE64(LIR_leq:)
          CASE64(LIR_geq:)
          CASE64(LIR_ltuq:)
          CASE64(LIR_gtuq:)
          CASE64(LIR_leuq:)
          CASE64(LIR_geuq:)
          CASESF(LIR_ii2d:)
            need(2);
            ins = mLir->ins2(mOpcode,
                             ref(mTokens[0]),
                             ref(mTokens[1]));
            break;

          case LIR_cmovi:
          CASE64(LIR_cmovq:)
          case LIR_cmovd:
            need(3);
            ins = mLir->ins3(mOpcode,
                             ref(mTokens[0]),
                             ref(mTokens[1]),
                             ref(mTokens[2]));
            break;

          case LIR_j:
            ins = assemble_jump(/*isCond*/false);
            break;

          case LIR_jt:
          case LIR_jf:
            ins = assemble_jump(/*isCond*/true);
            break;

          case LIR_immi:
            need(1);
            ins = mLir->insImmI(immI(mTokens[0]));
            break;

#ifdef NANOJIT_64BIT
          case LIR_immq:
            need(1);
            ins = mLir->insImmQ(immQ(mTokens[0]));
            break;
#endif

          case LIR_immd:
            need(1);
            ins = mLir->insImmD(immD(mTokens[0]));
            break;

#if NJ_EXPANDED_LOADSTORE_SUPPORTED 
          case LIR_sti2c:
          case LIR_sti2s:
          case LIR_std2f:
#endif
          case LIR_sti:
          CASE64(LIR_stq:)
          case LIR_std:
            need(3);
            ins = mLir->insStore(mOpcode, ref(mTokens[0]),
                                  ref(mTokens[1]),
                                  immI(mTokens[2]), ACCSET_OTHER);
            break;

#if NJ_EXPANDED_LOADSTORE_SUPPORTED 
          case LIR_ldc2i:
          case LIR_lds2i:
          case LIR_ldf2d:
#endif
          case LIR_lduc2ui:
          case LIR_ldus2ui:
          case LIR_ldi:
          CASE64(LIR_ldq:)
          case LIR_ldd:
            ins = assemble_load();
            break;

          // XXX: insParam gives the one appropriate for the platform.  Eg. if
          // you specify qparam on x86 you'll end up with iparam anyway.  Fix
          // this.
          case LIR_paramp:
            need(2);
            ins = mLir->insParam(immI(mTokens[0]),
                                 immI(mTokens[1]));
            break;

          // XXX: similar to iparam/qparam above.
          case LIR_allocp:
            need(1);
            ins = mLir->insAlloc(immI(mTokens[0]));
            break;

          case LIR_skip:
            bad("skip instruction is deprecated");
            break;

          case LIR_x:
          case LIR_xbarrier:
            ins = assemble_guard(/*isCond*/false);
            break;

          case LIR_xt:
          case LIR_xf:
            ins = assemble_guard(/*isCond*/true);
            break;

          case LIR_addxovi:
          case LIR_subxovi:
          case LIR_mulxovi:
            ins = assemble_guard_xov();
            break;

          case LIR_addjovi:
          case LIR_subjovi:
          case LIR_muljovi:
          CASE64(LIR_addjovq:)
          CASE64(LIR_subjovq:)
            ins = assemble_jump_jov();
            break;

          case LIR_callv:
          case LIR_calli:
          CASESF(LIR_hcalli:)
          CASE64(LIR_callq:)
          case LIR_calld:
            ins = assemble_call(op);
            break;

          case LIR_reti:
            ins = assemble_ret(RT_INT);
            break;

#ifdef NANOJIT_64BIT
          case LIR_retq:
            ins = assemble_ret(RT_QUAD);
            break;
#endif

          case LIR_retd:
            ins = assemble_ret(RT_DOUBLE);
            break;

          case LIR_label:
            ins = mLir->ins0(LIR_label);
            add_jump_label(lab, ins);
            lab.clear();
            break;

          case LIR_file:
          case LIR_line:
          case LIR_jtbl:
            nyi(op);
            break;

          default:
            nyi(op);
            break;
        }

        assert(ins);
        if (!lab.empty())
            mLabels.insert(make_pair(lab, ins));

    }
    endFragment();
}

/* ------------------ Support for --random -------------------------- */

// Returns a positive integer in the range 0..(lim-1).
static inline size_t
rnd(size_t lim)
{
    size_t i = size_t(rand());
    return i % lim;
}

// Returns an int32_t in the range -RAND_MAX..RAND_MAX.
static inline int32_t
rndI32()
{
    return (rnd(2) ? 1 : -1) * rand();
}

// The maximum number of live values (per type, ie. B/I/Q/F) that are
// available to be used as operands.  If we make it too high we're prone to
// run out of stack space due to spilling.  Needs to be set in consideration
// with spillStackSzB.
const size_t maxLiveValuesPerType = 20;

// Returns a uint32_t in the range 0..(RAND_MAX*2).
static inline uint32_t
rndU32()
{
    return uint32_t(rnd(2) ? 0 : RAND_MAX) + uint32_t(rand());
}

template<typename t> t
rndPick(vector<t> &v)
{
    assert(!v.empty());
    return v[rnd(v.size())];
}

// Add the operand, and retire an old one if we have too many.
template<typename t> void
addOrReplace(vector<t> &v, t x)
{
    if (v.size() > maxLiveValuesPerType) {
        v[rnd(v.size())] = x;    // we're full:  overwrite an existing element
    } else {
        v.push_back(x);             // add to end
    }
}

// Returns a 4-aligned address within the given size.
static int32_t rndOffset32(size_t szB)
{
    return int32_t(rnd(szB)) & ~3;
}

// Returns an 8-aligned address within the give size.
static int32_t rndOffset64(size_t szB)
{
    return int32_t(rnd(szB)) & ~7;
}

static int32_t f_I_I1(int32_t a)
{
    return a;
}

static int32_t f_I_I6(int32_t a, int32_t b, int32_t c, int32_t d, int32_t e, int32_t f)
{
    return a + b + c + d + e + f;
}

#ifdef NANOJIT_64BIT
static uint64_t f_Q_Q2(uint64_t a, uint64_t b)
{
    return a + b;
}

static uint64_t f_Q_Q7(uint64_t a, uint64_t b, uint64_t c, uint64_t d,
                       uint64_t e, uint64_t f, uint64_t g)
{
    return a + b + c + d + e + f + g;
}
#endif

static double f_F_F3(double a, double b, double c)
{
    return a + b + c;
}

static double f_F_F8(double a, double b, double c, double d,
                     double e, double f, double g, double h)
{
    return a + b + c + d + e + f + g + h;
}

#ifdef NANOJIT_64BIT
static void f_V_IQF(int32_t, uint64_t, double)
{
    return;     // no need to do anything
}
#endif

const CallInfo ci_I_I1 = CI(f_I_I1, CallInfo::typeSig1(ARGTYPE_I, ARGTYPE_I));
const CallInfo ci_I_I6 = CI(f_I_I6, CallInfo::typeSig6(ARGTYPE_I, ARGTYPE_I, ARGTYPE_I, ARGTYPE_I,
                                                       ARGTYPE_I, ARGTYPE_I, ARGTYPE_I));

#ifdef NANOJIT_64BIT
const CallInfo ci_Q_Q2 = CI(f_Q_Q2, CallInfo::typeSig2(ARGTYPE_Q, ARGTYPE_Q, ARGTYPE_Q));
const CallInfo ci_Q_Q7 = CI(f_Q_Q7, CallInfo::typeSig7(ARGTYPE_Q, ARGTYPE_Q, ARGTYPE_Q, ARGTYPE_Q,
                                                       ARGTYPE_Q, ARGTYPE_Q, ARGTYPE_Q, ARGTYPE_Q));
#endif

const CallInfo ci_F_F3 = CI(f_F_F3, CallInfo::typeSig3(ARGTYPE_D, ARGTYPE_D, ARGTYPE_D, ARGTYPE_D));
const CallInfo ci_F_F8 = CI(f_F_F8, CallInfo::typeSig8(ARGTYPE_D, ARGTYPE_D, ARGTYPE_D, ARGTYPE_D,
                                                       ARGTYPE_D, ARGTYPE_D, ARGTYPE_D, ARGTYPE_D,
                                                       ARGTYPE_D));

#ifdef NANOJIT_64BIT
const CallInfo ci_V_IQF = CI(f_V_IQF, CallInfo::typeSig3(ARGTYPE_V, ARGTYPE_I, ARGTYPE_Q, ARGTYPE_D));
#endif

// Generate a random block containing nIns instructions, plus a few more
// setup/shutdown ones at the start and end.
//
// Basic operation:
// - We divide LIR into numerous classes, mostly according to their type.
//   (See LInsClasses.tbl for details.) Each time around the loop we choose
//   the class randomly, but there is weighting so that some classes are more
//   common than others, in an attempt to reflect the structure of real code.
// - Each instruction that produces a value is put in a buffer of the
//   appropriate type, for possible use as an operand of a later instruction.
//   This buffer is trimmed when its size exceeds 'maxLiveValuesPerType'.
// - If not enough operands are present in a buffer for the particular
//   instruction, we don't add it.
// - Skips aren't explicitly generated, but they do occcur if the fragment is
//   sufficiently big that it's spread across multiple chunks.
//
// The following instructions aren't generated yet:
// - LIR_parami/LIR_paramq (hard to test beyond what is auto-generated in fragment
//   prologues)
// - LIR_livei/LIR_liveq/LIR_lived
// - LIR_hcalli
// - LIR_x/LIR_xt/LIR_xf/LIR_addxovi/LIR_subxovi/LIR_mulxovi (hard to
//   test without having multiple fragments;  when we only have one fragment
//   we don't really want to leave it early)
// - LIR_reti/LIR_retq/LIR_retd (hard to test without having multiple fragments)
// - LIR_j/LIR_jt/LIR_jf/LIR_jtbl/LIR_label
// - LIR_file/LIR_line (#ifdef VTUNE only)
// - LIR_modd (not implemented in NJ backends)
//
// Other limitations:
// - Loads always use accSet==ACCSET_OTHER
// - Stores always use accSet==ACCSET_OTHER
//
void
FragmentAssembler::assembleRandomFragment(int nIns)
{
    vector<LIns*> Bs;       // boolean values, ie. 32-bit int values produced by tests
    vector<LIns*> Is;       // 32-bit int values
    vector<LIns*> Qs;       // 64-bit int values
    vector<LIns*> Ds;       // 64-bit double values
    vector<LIns*> M4s;      // 4 byte allocs
    vector<LIns*> M8ps;     // 8+ byte allocs

    vector<LOpcode> I_I_ops;
    I_I_ops.push_back(LIR_negi);
    I_I_ops.push_back(LIR_noti);

    // Nb: there are no Q_Q_ops.

    vector<LOpcode> D_D_ops;
    D_D_ops.push_back(LIR_negd);

    vector<LOpcode> I_II_ops;
    I_II_ops.push_back(LIR_addi);
    I_II_ops.push_back(LIR_subi);
    I_II_ops.push_back(LIR_muli);
#if defined NANOJIT_IA32 || defined NANOJIT_X64
    I_II_ops.push_back(LIR_divi);
    I_II_ops.push_back(LIR_modi);
#endif
    I_II_ops.push_back(LIR_andi);
    I_II_ops.push_back(LIR_ori);
    I_II_ops.push_back(LIR_xori);
    I_II_ops.push_back(LIR_lshi);
    I_II_ops.push_back(LIR_rshi);
    I_II_ops.push_back(LIR_rshui);

#ifdef NANOJIT_64BIT
    vector<LOpcode> Q_QQ_ops;
    Q_QQ_ops.push_back(LIR_addq);
    Q_QQ_ops.push_back(LIR_andq);
    Q_QQ_ops.push_back(LIR_orq);
    Q_QQ_ops.push_back(LIR_xorq);

    vector<LOpcode> Q_QI_ops;
    Q_QI_ops.push_back(LIR_lshq);
    Q_QI_ops.push_back(LIR_rshq);
    Q_QI_ops.push_back(LIR_rshuq);
#endif

    vector<LOpcode> D_DD_ops;
    D_DD_ops.push_back(LIR_addd);
    D_DD_ops.push_back(LIR_subd);
    D_DD_ops.push_back(LIR_muld);
    D_DD_ops.push_back(LIR_divd);

    vector<LOpcode> I_BII_ops;
    I_BII_ops.push_back(LIR_cmovi);

#ifdef NANOJIT_64BIT
    vector<LOpcode> Q_BQQ_ops;
    Q_BQQ_ops.push_back(LIR_cmovq);
#endif

    vector<LOpcode> D_BDD_ops;
    D_BDD_ops.push_back(LIR_cmovd);

    vector<LOpcode> B_II_ops;
    B_II_ops.push_back(LIR_eqi);
    B_II_ops.push_back(LIR_lti);
    B_II_ops.push_back(LIR_gti);
    B_II_ops.push_back(LIR_lei);
    B_II_ops.push_back(LIR_gei);
    B_II_ops.push_back(LIR_ltui);
    B_II_ops.push_back(LIR_gtui);
    B_II_ops.push_back(LIR_leui);
    B_II_ops.push_back(LIR_geui);

#ifdef NANOJIT_64BIT
    vector<LOpcode> B_QQ_ops;
    B_QQ_ops.push_back(LIR_eqq);
    B_QQ_ops.push_back(LIR_ltq);
    B_QQ_ops.push_back(LIR_gtq);
    B_QQ_ops.push_back(LIR_leq);
    B_QQ_ops.push_back(LIR_geq);
    B_QQ_ops.push_back(LIR_ltuq);
    B_QQ_ops.push_back(LIR_gtuq);
    B_QQ_ops.push_back(LIR_leuq);
    B_QQ_ops.push_back(LIR_geuq);
#endif

    vector<LOpcode> B_DD_ops;
    B_DD_ops.push_back(LIR_eqd);
    B_DD_ops.push_back(LIR_ltd);
    B_DD_ops.push_back(LIR_gtd);
    B_DD_ops.push_back(LIR_led);
    B_DD_ops.push_back(LIR_ged);

#ifdef NANOJIT_64BIT
    vector<LOpcode> Q_I_ops;
    Q_I_ops.push_back(LIR_i2q);
    Q_I_ops.push_back(LIR_ui2uq);

    vector<LOpcode> I_Q_ops;
    I_Q_ops.push_back(LIR_q2i);
#endif

    vector<LOpcode> D_I_ops;
#if !NJ_SOFTFLOAT_SUPPORTED
    // Don't emit LIR_{ui,i}2d for soft-float platforms because the soft-float filter removes them.
    D_I_ops.push_back(LIR_i2d);
    D_I_ops.push_back(LIR_ui2d);
#elif defined(NANOJIT_ARM)
    // The ARM back-end can detect FP support at run-time.
    if (mParent.mConfig.arm_vfp) {
        D_I_ops.push_back(LIR_i2d);
        D_I_ops.push_back(LIR_ui2d);
    }
#endif

    vector<LOpcode> I_D_ops;
#if NJ_SOFTFLOAT_SUPPORTED
    I_D_ops.push_back(LIR_dlo2i);
    I_D_ops.push_back(LIR_dhi2i);
#endif
#if !NJ_SOFTFLOAT_SUPPORTED
    // Don't emit LIR_d2i for soft-float platforms because the soft-float filter removes it.
    I_D_ops.push_back(LIR_d2i);
#elif defined(NANOJIT_ARM)
    // The ARM back-end can detect FP support at run-time.
    if (mParent.mConfig.arm_vfp) {
        I_D_ops.push_back(LIR_d2i);
    }
#endif

#ifdef NANOJIT_64BIT
    vector<LOpcode> Q_D_ops;
    Q_D_ops.push_back(LIR_dasq);

    vector<LOpcode> D_Q_ops;
    D_Q_ops.push_back(LIR_qasd);
#endif

    vector<LOpcode> D_II_ops;
#if NJ_SOFTFLOAT_SUPPORTED
    D_II_ops.push_back(LIR_ii2d);
#endif

    vector<LOpcode> I_loads;
    I_loads.push_back(LIR_ldi);          // weight LIR_ldi more heavily
    I_loads.push_back(LIR_ldi);
    I_loads.push_back(LIR_ldi);
    I_loads.push_back(LIR_lduc2ui);
    I_loads.push_back(LIR_ldus2ui);
#if NJ_EXPANDED_LOADSTORE_SUPPORTED 
    I_loads.push_back(LIR_ldc2i);
    I_loads.push_back(LIR_lds2i);
#endif

#ifdef NANOJIT_64BIT
    vector<LOpcode> Q_loads;
    Q_loads.push_back(LIR_ldq);
#endif

    vector<LOpcode> D_loads;
    D_loads.push_back(LIR_ldd);
#if NJ_EXPANDED_LOADSTORE_SUPPORTED
    // this loads a 32-bit float and expands it to 64-bit float
    D_loads.push_back(LIR_ldf2d);
#endif

    enum LInsClass {
#define CL___(name, relFreq)     name,
#include "LInsClasses.tbl"
#undef CL___
        LLAST
    };

    int relFreqs[LLAST];
    memset(relFreqs, 0, sizeof(relFreqs));
#define CL___(name, relFreq)     relFreqs[name] = relFreq;
#include "LInsClasses.tbl"
#undef CL___

    int relFreqsSum = 0;    // the sum of the individual relative frequencies
    for (int c = 0; c < LLAST; c++) {
        relFreqsSum += relFreqs[c];
    }

    // The number of times each LInsClass value appears in classGenerator[]
    // matches 'relFreqs' (see LInsClasses.tbl).  Eg. if relFreqs[LIMM_I] ==
    // 10, then LIMM_I appears in classGenerator[] 10 times.
    LInsClass* classGenerator = new LInsClass[relFreqsSum];
    int j = 0;
    for (int c = 0; c < LLAST; c++) {
        for (int i = 0; i < relFreqs[c]; i++) {
            classGenerator[j++] = LInsClass(c);
        }
    }

    // Used to keep track of how much stack we've explicitly used via
    // LIR_allocp.  We then need to keep some reserve for spills as well.
    const size_t stackSzB = NJ_MAX_STACK_ENTRY * 4;
    const size_t spillStackSzB = 1024;
    const size_t maxExplicitlyUsedStackSzB = stackSzB - spillStackSzB;
    size_t explicitlyUsedStackSzB = 0;

    // Do an 8-byte stack alloc right at the start so that loads and stores
    // can be done immediately.
    addOrReplace(M8ps, mLir->insAlloc(8));

    int n = 0;
    while (n < nIns) {

        LIns *ins;

        switch (classGenerator[rnd(relFreqsSum)]) {

        case LFENCE:
            if (rnd(2)) {
                mLir->ins0(LIR_regfence);
            } else {
                mLir->insGuard(LIR_xbarrier, NULL, createGuardRecord(createSideExit()));
            }
            n++;
            break;

        case LALLOC: {
            // The stack has a limited size, so we (a) don't want chunks to be
            // too big, and (b) have to stop allocating them after a while.
            size_t szB = 0;
            switch (rnd(3)) {
            case 0: szB = 4;                break;
            case 1: szB = 8;                break;
            case 2: szB = 4 * (rnd(6) + 3); break;  // 12, 16, ..., 32
            }
            if (explicitlyUsedStackSzB + szB <= maxExplicitlyUsedStackSzB) {
                ins = mLir->insAlloc(szB);
                // We add the result to Is/Qs so it can be used as an ordinary
                // operand, and to M4s/M8ps so that loads/stores can be done from
                // it.
#if defined NANOJIT_64BIT
                addOrReplace(Qs, ins);
#else
                addOrReplace(Is, ins);
#endif
                if (szB == 4)
                    addOrReplace(M4s, ins);
                else
                    addOrReplace(M8ps, ins);

                // It's possible that we will exceed maxExplicitlyUsedStackSzB
                // by up to 28 bytes.  Doesn't matter.
                explicitlyUsedStackSzB += szB;
                n++;
            }
            break;
        }

        // For the immediates, we bias towards smaller numbers, especially 0
        // and 1 and small multiples of 4 which are common due to memory
        // addressing.  This puts some realistic stress on CseFilter.
        case LIMM_I: {
            int32_t immI = 0;      // shut gcc up
            switch (rnd(5)) {
            case 0: immI = 0;                  break;
            case 1: immI = 1;                  break;
            case 2: immI = 4 * (rnd(256) + 1); break;  // 4, 8, ..., 1024
            case 3: immI = rnd(19999) - 9999;  break;  // -9999..9999
            case 4: immI = rndI32();           break;  // -RAND_MAX..RAND_MAX
            }
            ins = mLir->insImmI(immI);
            addOrReplace(Is, ins);
            n++;
            break;
        }

#ifdef NANOJIT_64BIT
        case LIMM_Q: {
            uint64_t imm64 = 0;
            switch (rnd(5)) {
            case 0: imm64 = 0;                                      break;
            case 1: imm64 = 1;                                      break;
            case 2: imm64 = 4 * (rnd(256) + 1);                     break;  // 4, 8, ..., 1024
            case 3: imm64 = rnd(19999) - 9999;                      break;  // -9999..9999
            case 4: imm64 = uint64_t(rndU32()) << 32 | rndU32();    break;  // possibly big!
            }
            ins = mLir->insImmQ(imm64);
            addOrReplace(Qs, ins);
            n++;
            break;
        }
#endif

        case LIMM_D: {
            // We don't explicitly generate infinities and NaNs here, but they
            // end up occurring due to ExprFilter evaluating expressions like
            // divd(1,0) and divd(Infinity,Infinity).
            double imm64f = 0;
            switch (rnd(5)) {
            case 0: imm64f = 0.0;                                           break;
            case 1: imm64f = 1.0;                                           break;
            case 2:
            case 3: imm64f = double(rnd(1000));                             break;  // 0.0..9999.0
            case 4:
                union {
                    double d;
                    uint64_t q;
                } u;
                u.q = uint64_t(rndU32()) << 32 | rndU32();
                imm64f = u.d;
                break;
            }
            ins = mLir->insImmD(imm64f);
            addOrReplace(Ds, ins);
            n++;
            break;
        }

        case LOP_I_I:
            if (!Is.empty()) {
                ins = mLir->ins1(rndPick(I_I_ops), rndPick(Is));
                addOrReplace(Is, ins);
                n++;
            }
            break;

        // case LOP_Q_Q:  no instruction in this category

        case LOP_D_D:
            if (!Ds.empty()) {
                ins = mLir->ins1(rndPick(D_D_ops), rndPick(Ds));
                addOrReplace(Ds, ins);
                n++;
            }
            break;

        case LOP_I_II:
            if (!Is.empty()) {
                LOpcode op = rndPick(I_II_ops);
                LIns* lhs = rndPick(Is);
                LIns* rhs = rndPick(Is);
#if defined NANOJIT_IA32 || defined NANOJIT_X64
                if (op == LIR_divi || op == LIR_modi) {
                    // XXX: ExprFilter can't fold a div/mod with constant
                    // args, due to the horrible semantics of LIR_modi.  So we
                    // just don't generate anything if we hit that case.
                    if (!lhs->isImmI() || !rhs->isImmI()) {
                        // If the divisor is positive, no problems.  If it's zero, we get an
                        // exception.  If it's -1 and the dividend is -2147483648 (-2^31) we get
                        // an exception (and this has been encountered in practice).  So we only
                        // allow positive divisors, ie. compute:  lhs / (rhs > 0 ? rhs : -k),
                        // where k is a random number in the range 2..100 (this ensures we have
                        // some negative divisors).
                        LIns* gt0  = mLir->ins2ImmI(LIR_gti, rhs, 0);
                        LIns* rhs2 = mLir->ins3(LIR_cmovi, gt0, rhs, mLir->insImmI(-((int32_t)rnd(99)) - 2));
                        LIns* div  = mLir->ins2(LIR_divi, lhs, rhs2);
                        if (op == LIR_divi) {
                            ins = div;
                            addOrReplace(Is, ins);
                            n += 5;
                        } else {
                            ins = mLir->ins1(LIR_modi, div);
                            // Add 'div' to the operands too so it might be used again, because
                            // the code generated is different as compared to the case where 'div'
                            // isn't used again.
                            addOrReplace(Is, div);
                            addOrReplace(Is, ins);
                            n += 6;
                        }
                    }
                } else
#endif
                {
                    ins = mLir->ins2(op, lhs, rhs);
                    addOrReplace(Is, ins);
                    n++;
                }
            }
            break;

#ifdef NANOJIT_64BIT
        case LOP_Q_QQ:
            if (!Qs.empty()) {
                ins = mLir->ins2(rndPick(Q_QQ_ops), rndPick(Qs), rndPick(Qs));
                addOrReplace(Qs, ins);
                n++;
            }
            break;

        case LOP_Q_QI:
            if (!Qs.empty() && !Is.empty()) {
                ins = mLir->ins2(rndPick(Q_QI_ops), rndPick(Qs), rndPick(Is));
                addOrReplace(Qs, ins);
                n++;
            }
            break;
#endif

        case LOP_D_DD:
            if (!Ds.empty()) {
                ins = mLir->ins2(rndPick(D_DD_ops), rndPick(Ds), rndPick(Ds));
                addOrReplace(Ds, ins);
                n++;
            }
            break;

        case LOP_I_BII:
            if (!Bs.empty() && !Is.empty()) {
                ins = mLir->ins3(rndPick(I_BII_ops), rndPick(Bs), rndPick(Is), rndPick(Is));
                addOrReplace(Is, ins);
                n++;
            }
            break;

#ifdef NANOJIT_64BIT
        case LOP_Q_BQQ:
            if (!Bs.empty() && !Qs.empty()) {
                ins = mLir->ins3(rndPick(Q_BQQ_ops), rndPick(Bs), rndPick(Qs), rndPick(Qs));
                addOrReplace(Qs, ins);
                n++;
            }
            break;
#endif

        case LOP_D_BDD:
            if (!Bs.empty() && !Ds.empty()) {
                ins = mLir->ins3(rndPick(D_BDD_ops), rndPick(Bs), rndPick(Ds), rndPick(Ds));
                addOrReplace(Ds, ins);
                n++;
            }
            break;

        case LOP_B_II:
           if (!Is.empty()) {
               ins = mLir->ins2(rndPick(B_II_ops), rndPick(Is), rndPick(Is));
               addOrReplace(Bs, ins);
               n++;
           }
            break;

#ifdef NANOJIT_64BIT
        case LOP_B_QQ:
            if (!Qs.empty()) {
                ins = mLir->ins2(rndPick(B_QQ_ops), rndPick(Qs), rndPick(Qs));
                addOrReplace(Bs, ins);
                n++;
            }
            break;
#endif

        case LOP_B_DD:
            if (!Ds.empty()) {
                ins = mLir->ins2(rndPick(B_DD_ops), rndPick(Ds), rndPick(Ds));
                // XXX: we don't push the result, because most (all?) of the
                // backends currently can't handle cmovs/qcmovs that take
                // float comparisons for the test (see bug 520944).  This means
                // that all B_DD values are dead, unfortunately.
                //addOrReplace(Bs, ins);
                n++;
            }
            break;

#ifdef NANOJIT_64BIT
        case LOP_Q_I:
            if (!Is.empty()) {
                ins = mLir->ins1(rndPick(Q_I_ops), rndPick(Is));
                addOrReplace(Qs, ins);
                n++;
            }
            break;
#endif

        case LOP_D_I:
            if (!Is.empty() && !D_I_ops.empty()) {
                ins = mLir->ins1(rndPick(D_I_ops), rndPick(Is));
                addOrReplace(Ds, ins);
                n++;
            }
            break;

#ifdef NANOJIT_64BIT
        case LOP_I_Q:
            if (!Qs.empty()) {
                ins = mLir->ins1(rndPick(I_Q_ops), rndPick(Qs));
                addOrReplace(Is, ins);
                n++;
            }
            break;
#endif

        case LOP_I_D:
// XXX: NativeX64 doesn't implement qhi yet (and it may not need to).
#if !defined NANOJIT_X64
            if (!Ds.empty()) {
                ins = mLir->ins1(rndPick(I_D_ops), rndPick(Ds));
                addOrReplace(Is, ins);
                n++;
            }
#endif
            break;

#if defined NANOJIT_X64
        case LOP_Q_D:
            if (!Ds.empty()) {
                ins = mLir->ins1(rndPick(Q_D_ops), rndPick(Ds));
                addOrReplace(Qs, ins);
                n++;
            }
            break;

        case LOP_D_Q:
            if (!Qs.empty()) {
                ins = mLir->ins1(rndPick(D_Q_ops), rndPick(Qs));
                addOrReplace(Ds, ins);
                n++;
            }
            break;
#endif

        case LOP_D_II:
            if (!Is.empty() && !D_II_ops.empty()) {
                ins = mLir->ins2(rndPick(D_II_ops), rndPick(Is), rndPick(Is));
                addOrReplace(Ds, ins);
                n++;
            }
            break;

        case LLD_I: {
            vector<LIns*> Ms = rnd(2) ? M4s : M8ps;
            if (!Ms.empty()) {
                LIns* base = rndPick(Ms);
                ins = mLir->insLoad(rndPick(I_loads), base, rndOffset32(base->size()), ACCSET_OTHER);
                addOrReplace(Is, ins);
                n++;
            }
            break;
        }

#ifdef NANOJIT_64BIT
        case LLD_Q:
            if (!M8ps.empty()) {
                LIns* base = rndPick(M8ps);
                ins = mLir->insLoad(rndPick(Q_loads), base, rndOffset64(base->size()), ACCSET_OTHER);
                addOrReplace(Qs, ins);
                n++;
            }
            break;
#endif

        case LLD_D:
            if (!M8ps.empty()) {
                LIns* base = rndPick(M8ps);
                ins = mLir->insLoad(rndPick(D_loads), base, rndOffset64(base->size()), ACCSET_OTHER);
                addOrReplace(Ds, ins);
                n++;
            }
            break;

        case LST_I: {
            vector<LIns*> Ms = rnd(2) ? M4s : M8ps;
            if (!Ms.empty() && !Is.empty()) {
                LIns* base = rndPick(Ms);
                mLir->insStore(rndPick(Is), base, rndOffset32(base->size()), ACCSET_OTHER);
                n++;
            }
            break;
        }

#ifdef NANOJIT_64BIT
        case LST_Q:
            if (!M8ps.empty() && !Qs.empty()) {
                LIns* base = rndPick(M8ps);
                mLir->insStore(rndPick(Qs), base, rndOffset64(base->size()), ACCSET_OTHER);
                n++;
            }
            break;
#endif

        case LST_D:
            if (!M8ps.empty() && !Ds.empty()) {
                LIns* base = rndPick(M8ps);
                mLir->insStore(rndPick(Ds), base, rndOffset64(base->size()), ACCSET_OTHER);
                n++;
            }
            break;

        case LCALL_I_I1:
            if (!Is.empty()) {
                LIns* args[1] = { rndPick(Is) };
                ins = mLir->insCall(&ci_I_I1, args);
                addOrReplace(Is, ins);
                n++;
            }
            break;

        case LCALL_I_I6:
            if (!Is.empty()) {
                LIns* args[6] = { rndPick(Is), rndPick(Is), rndPick(Is),
                                  rndPick(Is), rndPick(Is), rndPick(Is) };
                ins = mLir->insCall(&ci_I_I6, args);
                addOrReplace(Is, ins);
                n++;
            }
            break;

#ifdef NANOJIT_64BIT
        case LCALL_Q_Q2:
            if (!Qs.empty()) {
                LIns* args[2] = { rndPick(Qs), rndPick(Qs) };
                ins = mLir->insCall(&ci_Q_Q2, args);
                addOrReplace(Qs, ins);
                n++;
            }
            break;

        case LCALL_Q_Q7:
            if (!Qs.empty()) {
                LIns* args[7] = { rndPick(Qs), rndPick(Qs), rndPick(Qs), rndPick(Qs),
                                  rndPick(Qs), rndPick(Qs), rndPick(Qs) };
                ins = mLir->insCall(&ci_Q_Q7, args);
                addOrReplace(Qs, ins);
                n++;
            }
            break;
#endif

        case LCALL_D_D3:
            if (!Ds.empty()) {
                LIns* args[3] = { rndPick(Ds), rndPick(Ds), rndPick(Ds) };
                ins = mLir->insCall(&ci_F_F3, args);
                addOrReplace(Ds, ins);
                n++;
            }
            break;

        case LCALL_D_D8:
            if (!Ds.empty()) {
                LIns* args[8] = { rndPick(Ds), rndPick(Ds), rndPick(Ds), rndPick(Ds),
                                  rndPick(Ds), rndPick(Ds), rndPick(Ds), rndPick(Ds) };
                ins = mLir->insCall(&ci_F_F8, args);
                addOrReplace(Ds, ins);
                n++;
            }
            break;

#ifdef NANOJIT_64BIT
        case LCALL_V_IQD:
            if (!Is.empty() && !Qs.empty() && !Ds.empty()) {
                // Nb: args[] holds the args in reverse order... sigh.
                LIns* args[3] = { rndPick(Ds), rndPick(Qs), rndPick(Is) };
                ins = mLir->insCall(&ci_V_IQF, args);
                n++;
            }
            break;
#endif

        case LLABEL:
            // Although no jumps are generated yet, labels are important
            // because they delimit areas where CSE can be applied.  Without
            // them, CSE can be applied over very long regions, which leads to
            // values that have very large live ranges, which leads to stack
            // overflows.
            mLir->ins0(LIR_label);
            n++;
            break;

        default:
            NanoAssert(0);
            break;
        }
    }

    delete[] classGenerator;

    // Return 0.
    mReturnTypeBits |= RT_INT;
    mLir->ins1(LIR_reti, mLir->insImmI(0));

    endFragment();
}

Lirasm::Lirasm(bool verbose, Config& config) :
    mConfig(config),
    mAssm(mCodeAlloc, mAlloc, mAlloc, &mLogc, mConfig)
{
    mVerbose = verbose;
    mLogc.lcbits = 0;

    mLirbuf = new (mAlloc) LirBuffer(mAlloc);
#ifdef DEBUG
    if (mVerbose) {
        mLogc.lcbits = LC_ReadLIR | LC_AfterDCE | LC_Native | LC_RegAlloc | LC_Activation;
        mLirbuf->printer = new (mAlloc) LInsPrinter(mAlloc, LIRASM_NUM_USED_ACCS);
    }
#endif

    // Populate the mOpMap table.
#define OP___(op, number, repKind, retType, isCse) \
    mOpMap[#op] = LIR_##op;
#include "nanojit/LIRopcode.tbl"
#undef OP___

    // XXX: could add more pointer-sized synonyms here
    mOpMap["paramp"] = mOpMap[PTR_SIZE("parami", "paramq")];
    mOpMap["livep"]  = mOpMap[PTR_SIZE("livei", "liveq")];
}

Lirasm::~Lirasm()
{
    Fragments::iterator i;
    for (i = mFragments.begin(); i != mFragments.end(); ++i) {
        delete i->second.fragptr;
    }
}


bool
Lirasm::lookupFunction(const string &name, CallInfo *&ci)
{
    const size_t nfuns = sizeof(functions) / sizeof(functions[0]);
    for (size_t i = 0; i < nfuns; i++) {
        if (name == functions[i].name) {
            *ci = functions[i].callInfo;
            return true;
        }
    }

    Fragments::const_iterator func = mFragments.find(name);
    if (func != mFragments.end()) {
        // The ABI, arg types and ret type will be overridden by the caller.
        if (func->second.mReturnType == RT_DOUBLE) {
            CallInfo target = {(uintptr_t) func->second.rdouble,
                               0, ABI_FASTCALL, /*isPure*/0, ACCSET_STORE_ANY
                               verbose_only(, func->first.c_str()) };
            *ci = target;

        } else {
            CallInfo target = {(uintptr_t) func->second.rint,
                               0, ABI_FASTCALL, /*isPure*/0, ACCSET_STORE_ANY
                               verbose_only(, func->first.c_str()) };
            *ci = target;
        }
        return false;

    } else {
        bad("invalid function reference " + name);
        return false;
    }
}

void
Lirasm::assemble(istream &in, bool optimize)
{
    LirTokenStream ts(in);
    bool first = true;

    LirToken token;
    while (ts.get(token)) {

        if (token.type == NEWLINE)
            continue;
        if (token.type != NAME)
            bad("unexpected token '" + token.data + "'");

        const string &op = token.data;
        if (op == ".patch") {
            handlePatch(ts);
        } else if (op == ".begin") {
            string name;
            if (!ts.getName(name))
                bad("expected fragment name after .begin");
            if (!ts.eat(NEWLINE))
                bad("extra junk after .begin " + name);

            FragmentAssembler assembler(*this, name, optimize);
            assembler.assembleFragment(ts, false, NULL);
            first = false;
        } else if (op == ".end") {
            bad(".end without .begin");
        } else if (first) {
            FragmentAssembler assembler(*this, "main", optimize);
            assembler.assembleFragment(ts, true, &token);
            break;
        } else {
            bad("unexpected stray opcode '" + op + "'");
        }
    }
}

void
Lirasm::assembleRandom(int nIns, bool optimize)
{
    string name = "main";
    FragmentAssembler assembler(*this, name, optimize);
    assembler.assembleRandomFragment(nIns);
}

void
Lirasm::handlePatch(LirTokenStream &in)
{
    string src, fragName, guardName, destName;

    if (!in.getName(src) || !in.eat(PUNCT, "->") || !in.getName(destName))
        bad("incorrect syntax");

    // Break the src at '.'. This is awkward but the syntax looks nice.
    size_t j = src.find('.');
    if (j == string::npos || j == 0 || j == src.size() - 1)
        bad("incorrect syntax");
    fragName = src.substr(0, j);
    guardName = src.substr(j + 1);

    Fragments::iterator i;
    if ((i=mFragments.find(fragName)) == mFragments.end())
        bad("invalid fragment reference");
    LirasmFragment *frag = &i->second;
    if (frag->mLabels.find(guardName) == frag->mLabels.end())
        bad("invalid guard reference");
    LIns *ins = frag->mLabels.find(guardName)->second;
    if ((i=mFragments.find(destName)) == mFragments.end())
        bad("invalid guard reference");
    ins->record()->exit->target = i->second.fragptr;

    mAssm.patch(ins->record()->exit);
}

void
usageAndQuit(const string& progname)
{
    cout <<
        "usage: " << progname << " [options] [filename]\n"
        "Options:\n"
        "  -h --help         print this message\n"
        "  -v --verbose      print LIR and assembly code\n"
        "  --execute         execute LIR\n"
        "  --[no-]optimize   enable or disable optimization of the LIR (default=off)\n"
        "  --random [N]      generate a random LIR block of size N (default=100)\n"
        "  --stkskip [N]     push approximately N Kbytes of stack before execution (default=100)\n"
        "\n"
        "Build query options (these print a value for this build of lirasm and exit)\n"
        "  --show-arch       show the architecture ('i386', 'X64', 'arm', 'ppc',\n"
        "                    'sparc', 'mips', or 'sh4')\n"
        "  --show-word-size  show the word size ('32' or '64')\n"
        "  --show-endianness show the endianness ('little-endian' or 'big-endian')\n"
        "\n"
        "i386-specific options:\n"
        "  --[no]sse         use SSE2 instructions (default=on)\n"
        "\n"
        "ARM-specific options:\n"
        "  --arch N          use ARM architecture version N instructions (default=7)\n"
        "  --[no]vfp         use ARM VFP instructions (default=on)\n"
        "\n"
        ;
    exit(0);
}

void
errMsgAndQuit(const string& progname, const string& msg)
{
    cerr << progname << ": " << msg << endl;
    exit(1);
}

struct CmdLineOptions {
    string  progname;
    bool    verbose;
    bool    execute;
    bool    optimize;
    int     random;
    int     stkskip;
    string  filename;
    Config  config;
};


bool parseOptionalInt(int argc, char** argv, int* i, int* value, int defaultValue)
{
    if (*i == argc - 1) {
        *value = defaultValue;      // no numeric argument, use default
    } else {
        char* endptr;
        int res = strtol(argv[*i+1], &endptr, 10);
        if ('\0' == *endptr) {
            // We don't bother checking for overflow.
            if (res <= 0) {
                return false;
            }
            *value = res;           // next arg is a number, use that for the value
            (*i)++;
        } else {
            *value = defaultValue;  // next arg is not a number
        }
    }
    return true;
}

static void
processCmdLine(int argc, char **argv, CmdLineOptions& opts)
{
    opts.progname = argv[0];
    opts.verbose  = false;
    opts.execute  = false;
    opts.random   = 0;
    opts.optimize = false;
    opts.stkskip  = 0;

    // Architecture-specific options.
#if defined NANOJIT_IA32
    bool            i386_sse = true;
#elif defined NANOJIT_ARM
    unsigned int    arm_arch = 7;
    bool            arm_vfp = true;
#endif

    for (int i = 1; i < argc; i++) {
        string arg = argv[i];

        // Common flags for every architecture.
        if (arg == "-h" || arg == "--help")
            usageAndQuit(opts.progname);
        else if (arg == "-v" || arg == "--verbose")
            opts.verbose = true;
        else if (arg == "--execute")
            opts.execute = true;
        else if (arg == "--optimize")
            opts.optimize = true;
        else if (arg == "--no-optimize")
            opts.optimize = false;
        else if (arg == "--random") {
            if (!parseOptionalInt(argc, argv, &i, &opts.random, 100))
                errMsgAndQuit(opts.progname, "--random argument must be greater than zero");
        }
        else if (arg == "--stkskip") {
            if (!parseOptionalInt(argc, argv, &i, &opts.stkskip, 100))
                errMsgAndQuit(opts.progname, "--stkskip argument must be greater than zero");
        }
        else if (arg == "--show-arch") {
            const char* str = 
#if defined NANOJIT_IA32
                "i386";
#elif defined NANOJIT_X64
                "X64";
#elif defined NANOJIT_ARM
                "arm";
#elif defined NANOJIT_PPC
                "ppc";
#elif defined NANOJIT_SPARC
                "sparc";
#elif defined NANOJIT_MIPS
                "mips";
#elif defined NANOJIT_SH4
                "sh4";
#else
#               error "unknown arch"
#endif
            cout << str << "\n";
            exit(0);
        }
        else if (arg == "--show-word-size") {
            cout << sizeof(void*) * 8 << "\n";
            exit(0);
        }
        else if (arg == "--show-endianness") {
            int32_t x = 0x01020304;
            if (*(char*)&x == 0x1) {
              cout << "big-endian" << "\n";
            } else {
              cout << "little-endian" << "\n";
            }
            exit(0);
        }

        // Architecture-specific flags.
#if defined NANOJIT_IA32
        else if (arg == "--sse") {
            i386_sse = true;
        }
        else if (arg == "--nosse") {
            i386_sse = false;
        }
#elif defined NANOJIT_ARM
        else if ((arg == "--arch") && (i < argc-1)) {
            char* endptr;
            arm_arch = strtoul(argv[i+1], &endptr, 10);
            // Check that the argument was a number.
            if ('\0' == *endptr) {
                if ((arm_arch < 4) || (arm_arch > 7)) {
                    errMsgAndQuit(opts.progname, "Unsupported argument to --arch.\n");
                }
            } else {
                errMsgAndQuit(opts.progname, "Unrecognized argument to --arch.\n");
            }
            i++;
        } else if (arg == "--vfp") {
            arm_vfp = true;
        } else if (arg == "--novfp") {
            arm_vfp = false;
        }
#endif
        // Input file names.
        else if (arg[0] != '-') {
            if (opts.filename.empty())
                opts.filename = arg;
            else
                errMsgAndQuit(opts.progname, "you can only specify one filename");
        }
        // No matching flag found, so report the error.
        else
            errMsgAndQuit(opts.progname, "bad option: " + arg);
    }

    if ((!opts.random && opts.filename.empty()) || (opts.random && !opts.filename.empty()))
        errMsgAndQuit(opts.progname,
                      "you must specify either a filename or --random (but not both)");

    // Handle the architecture-specific options.
#if defined NANOJIT_IA32
    opts.config.i386_use_cmov = opts.config.i386_sse2 = i386_sse;
    opts.config.i386_fixed_esp = true;
#elif defined NANOJIT_ARM
    // Warn about untested configurations.
    if ( ((arm_arch == 5) && (arm_vfp)) || ((arm_arch >= 6) && (!arm_vfp)) ) {
        char const * vfp_string = (arm_vfp) ? ("VFP") : ("no VFP");
        cerr << "Warning: This configuration (ARMv" << arm_arch << ", " << vfp_string << ") " <<
                "is not regularly tested." << endl;
    }

    opts.config.arm_arch = arm_arch;
    opts.config.arm_vfp = arm_vfp;
    opts.config.soft_float = !arm_vfp;
#endif
}

int32_t* dummy;

void
executeFragment(const LirasmFragment& fragment, int skip)
{
    // Allocate a large frame, and make sure we don't optimize it away.
    int32_t space[512];
    dummy = space;

    if (skip > 0) {
        executeFragment(fragment, skip-1);
    } else {
        switch (fragment.mReturnType) {
          case RT_INT: {
            int res = fragment.rint();
            cout << "Output is: " << res << endl;
            break;
          }
#ifdef NANOJIT_64BIT
          case RT_QUAD: {
            int res = fragment.rquad();
            cout << "Output is: " << res << endl;
            break;
          }
#endif
          case RT_DOUBLE: {
            double res = fragment.rdouble();
            cout << "Output is: " << res << endl;
            break;
          }
          case RT_GUARD: {
            LasmSideExit *ls = (LasmSideExit*) fragment.rguard()->exit;
            cout << "Exited block on line: " << ls->line << endl;
            break;
          }
        }
    }
}

int
main(int argc, char **argv)
{
    CmdLineOptions opts;
    processCmdLine(argc, argv, opts);

    Lirasm lasm(opts.verbose, opts.config);
    if (opts.random) {
        lasm.assembleRandom(opts.random, opts.optimize);
    } else {
        ifstream in(opts.filename.c_str());
        if (!in)
            errMsgAndQuit(opts.progname, "unable to open file " + opts.filename);
        lasm.assemble(in, opts.optimize);
    }

    Fragments::const_iterator i;
    if (opts.execute) {
        i = lasm.mFragments.find("main");
        if (i == lasm.mFragments.end())
            errMsgAndQuit(opts.progname, "error: at least one fragment must be named 'main'");
        executeFragment(i->second, opts.stkskip);
    } else {
        for (i = lasm.mFragments.begin(); i != lasm.mFragments.end(); i++)
            dump_srecords(cout, i->second.fragptr);
    }
}
