/*
 * JavaScript Assembler.
 * Copyright (c) 1998 New Generation Software (NGS) Oy
 *
 * Author: Markku Rossi <mtr@ngs.fi>
 */

/*
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 * MA 02111-1307, USA
 */

/*
 * $Source: /usr/local/cvsroot/ngs/js/jsc/asm.js,v $
 * $Id: asm.js,v 1.15 1998/10/26 15:25:21 mtr Exp $
 */

/* Byte-code file definitions. */

JSC$BC_MAGIC 		= 0xc0014a53;

JSC$BC_SECT_CODE 	= 0;
JSC$BC_SECT_CONSTANTS	= 1;
JSC$BC_SECT_SYMTAB 	= 2;
JSC$BC_SECT_DEBUG	= 3;

JSC$CONST_INT		= 3;
JSC$CONST_STRING	= 4;
JSC$CONST_FLOAT		= 5;
JSC$CONST_SYMBOL	= 10;
JSC$CONST_REGEXP	= 11;
JSC$CONST_NAN		= 13;

JSC$CONST_REGEXP_FLAG_G	= 0x01;
JSC$CONST_REGEXP_FLAG_I	= 0x02;

JSC$DEBUG_FILENAME	= 1;
JSC$DEBUG_LINENUMBER	= 2;

/* Opcode definitions. */

JSC$OP_HALT		= 0;
JSC$OP_DONE		= 1;
JSC$OP_NOP		= 2;
JSC$OP_DUP		= 3;
JSC$OP_POP		= 4;
JSC$OP_POP_N		= 5;
JSC$OP_APOP		= 6;
JSC$OP_SWAP		= 7;
JSC$OP_ROLL		= 8;
JSC$OP_CONST		= 9;
JSC$OP_CONST_NULL	= 10;
JSC$OP_CONST_TRUE	= 11;
JSC$OP_CONST_FALSE	= 12;
JSC$OP_CONST_UNDEFINED	= 13;
JSC$OP_CONST_I0		= 14;
JSC$OP_CONST_I1		= 15;
JSC$OP_CONST_I2		= 16;
JSC$OP_CONST_I3		= 17;
JSC$OP_CONST_I		= 18;
JSC$OP_LOAD_GLOBAL	= 19;
JSC$OP_STORE_GLOBAL	= 20;
JSC$OP_LOAD_ARG		= 21;
JSC$OP_STORE_ARG	= 22;
JSC$OP_LOAD_LOCAL	= 23;
JSC$OP_STORE_LOCAL	= 24;
JSC$OP_LOAD_PROPERTY	= 25;
JSC$OP_STORE_PROPERTY	= 26;
JSC$OP_LOAD_ARRAY	= 27;
JSC$OP_STORE_ARRAY	= 28;
JSC$OP_NTH		= 29;
JSC$OP_CMP_EQ		= 30;
JSC$OP_CMP_NE		= 31;
JSC$OP_CMP_LT		= 32;
JSC$OP_CMP_GT		= 33;
JSC$OP_CMP_LE		= 34;
JSC$OP_CMP_GE		= 35;
JSC$OP_CMP_SEQ		= 36;
JSC$OP_CMP_SNE		= 37;
JSC$OP_SUB		= 38;
JSC$OP_ADD		= 39;
JSC$OP_MUL		= 40;
JSC$OP_DIV		= 41;
JSC$OP_MOD		= 42;
JSC$OP_NEG		= 43;
JSC$OP_AND		= 44;
JSC$OP_NOT		= 45;
JSC$OP_OR		= 46;
JSC$OP_XOR		= 47;
JSC$OP_SHIFT_LEFT	= 48;
JSC$OP_SHIFT_RIGHT	= 49;
JSC$OP_SHIFT_RRIGHT	= 50;
JSC$OP_IFFALSE		= 51;
JSC$OP_IFTRUE		= 52;
JSC$OP_CALL_METHOD	= 53;
JSC$OP_JMP		= 54;
JSC$OP_JSR		= 55;
JSC$OP_RETURN		= 56;
JSC$OP_TYPEOF		= 57;
JSC$OP_NEW		= 58;
JSC$OP_DELETE_PROPERTY	= 59;
JSC$OP_DELETE_ARRAY	= 60;
JSC$OP_LOCALS		= 61;
JSC$OP_MIN_ARGS		= 62;
JSC$OP_LOAD_NTH_ARG	= 63;
JSC$OP_WITH_PUSH	= 64;
JSC$OP_WITH_POP		= 65;
JSC$OP_TRY_PUSH		= 66;
JSC$OP_TRY_POP		= 67;
JSC$OP_THROW		= 68;

/* Type aware operands. */
JSC$OP_IFFALSE_B	= 69;
JSC$OP_IFTRUE_B		= 70;
JSC$OP_ADD_1_I		= 71;
JSC$OP_ADD_2_I		= 72;
JSC$OP_LOAD_GLOBAL_W	= 73;
JSC$OP_JSR_W		= 74;

/* Internal values. */
JSC$ASM_SYMBOL		= 1000;
JSC$ASM_LABEL		= 1001;

/*
 * General helpers.
 */

/* Generate byte-code for operands with Int8 value. */
function JSC$ASM_bytecode_int8 ()
{
  return String.pack ("C", this.value);
}

/* Generate byte-code for operands with Int16 value. */
function JSC$ASM_bytecode_int16 ()
{
  return String.pack ("n", this.value);
}

/* Generate byte-code for operands with Int32 value. */
function JSC$ASM_bytecode_int32 ()
{
  return String.pack ("N", this.value);
}

/* Generate byte-code for operands with Symbol value. */
function JSC$ASM_bytecode_symbol ()
{
  var cid = JSC$asm_genconstant (String.pack ("C", JSC$CONST_SYMBOL)
				 + this.value + String.pack ("C", 0));
  return String.pack ("N", cid);
}

/* Generate byte-code for local jump operands. */
function JSC$ASM_bytecode_local_jump ()
{
  var delta = this.value.offset - (this.offset + this.size);
  return String.pack ("N", delta);
}


/*
 * Assembler operands.
 */

/* Symbol. */

function JSC$ASM_symbol (ln, value)
{
  this.type = JSC$ASM_SYMBOL;
  this.linenum = ln;
  this.value = value;
  this.size = 0;
  this.print = JSC$ASM_symbol_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_symbol_print (stream)
{
  stream.write ("\n" + this.value + ":\n");
}

/* Label */

function JSC$ASM_label ()
{
  this.type = JSC$ASM_LABEL;
  this.linenum = 0;
  this.size = 0;
  this.value = JSC$asm_label_count++;
  this.referenced = false;
  this.next = null;
  this.print = JSC$ASM_label_print;
  this.link = JSC$asm_link;
  this.format = JSC$ASM_label_format;
}

function JSC$ASM_label_print (stream)
{
  stream.write (this.format () + ":\n");
}

function JSC$ASM_label_format ()
{
  return ".L" + this.value.toString ();
}

/* halt */

function JSC$ASM_halt (ln)
{
  this.type = JSC$OP_HALT;
  this.linenum = ln;
  this.size = 1;
  this.print = JSC$ASM_halt_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_halt_print (stream)
{
  stream.write ("\thalt\n");
}

/* done */

function JSC$ASM_done (ln)
{
  this.type = JSC$OP_DONE;
  this.linenum = ln;
  this.size = 1;
  this.print = JSC$ASM_done_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_done_print (stream)
{
  stream.write ("\tdone\n");
}

/* nop */

function JSC$ASM_nop (ln)
{
  this.type = JSC$OP_NOP;
  this.linenum = ln;
  this.size = 1;
  this.print = JSC$ASM_nop_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_nop_print (stream)
{
  stream.write ("\tnop\n");
}

/* dup */

function JSC$ASM_dup (ln)
{
  this.type = JSC$OP_DUP;
  this.linenum = ln;
  this.stack_delta = 1;
  this.size = 1;
  this.print = JSC$ASM_dup_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_dup_print (stream)
{
  stream.write ("\tdup\n");
}

/* pop */

function JSC$ASM_pop (ln)
{
  this.type = JSC$OP_POP;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_pop_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_pop_print (stream)
{
  stream.write ("\tpop\n");
}

/* pop_n */

function JSC$ASM_pop_n (ln, value)
{
  this.type = JSC$OP_POP_N;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = -value;
  this.size = 2;
  this.print = JSC$ASM_pop_n_print;
  this.link = JSC$asm_link;
  this.bytecode = JSC$ASM_bytecode_int8;
}

function JSC$ASM_pop_n_print (stream)
{
  stream.write ("\tpop_n\t\t" + this.value.toString () + "\n");
}

/* apop */

function JSC$ASM_apop (ln, value)
{
  this.type = JSC$OP_APOP;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = -value;
  this.size = 2;
  this.print = JSC$ASM_apop_print;
  this.link = JSC$asm_link;
  this.bytecode = JSC$ASM_bytecode_int8;
}

function JSC$ASM_apop_print (stream)
{
  stream.write ("\tapop\t\t" + this.value.toString () + "\n");
}

/* swap */

function JSC$ASM_swap (ln)
{
  this.type = JSC$OP_SWAP;
  this.linenum = ln;
  this.size = 1;
  this.print = JSC$ASM_swap_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_swap_print (stream)
{
  stream.write ("\tswap\n");
}

/* roll */
function JSC$ASM_roll (ln, value)
{
  this.type = JSC$OP_ROLL;
  this.linenum = ln;
  this.value = value;
  this.size = 2;
  this.print = JSC$ASM_roll_print;
  this.link = JSC$asm_link;
  this.bytecode = JSC$ASM_bytecode_int8;
}

function JSC$ASM_roll_print (stream)
{
  stream.write ("\troll\t\t" + this.value.toString () + "\n");
}

/* const */

function JSC$ASM_const (ln, value)
{
  this.type = JSC$OP_CONST;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = 1;
  this.size = 5;
  this.print = JSC$ASM_const_print;
  this.link = JSC$asm_link;
  this.bytecode = JSC$ASM_const_bytecode;
}

function JSC$ASM_const_print (stream)
{
  if (typeof this.value == "number")
    stream.write ("\tconst\t\t" + this.value.toString () + "\n");
  else if (typeof this.value == "string"
	   || typeof this.value == "#builtin")
    {
      var i, c;
      var ender, src;
      var stringp = (typeof this.value == "string");

      if (stringp)
	{
	  ender = "\"";
	  src = this.value;
	}
      else
	{
	  ender = "/";
	  src = this.value.source;
	}

      stream.write ("\tconst\t\t" + ender);
      for (i = 0; i < src.length; i++)
	{
	  c = src.charCodeAt (i);
	  if (c == ender[0] || c == #'\\')
	    stream.write ("\\" + src.charAt (i));
	  else if (c == #'\n')
	    stream.write ("\\n");
	  else if (c == #'\r')
	    stream.write ("\\r");
	  else if (c == #'\t')
	    stream.write ("\\t");
	  else if (c == #'\f')
	    stream.write ("\\f");
	  else
	    stream.write (src.charAt (i));
	}
      stream.write (ender);

      if (!stringp)
	{
	  if (this.value.global)
	    stream.write ("g");
	  if (this.value.ignoreCase)
	    stream.write ("i");
	}

      stream.write ("\n");
    }
}

function JSC$ASM_const_bytecode ()
{
  var cid;

  if (typeof this.value == "number")
    {
      if (isInt (this.value))
	cid = JSC$asm_genconstant (String.pack ("CN", JSC$CONST_INT,
						this.value));
      else if (isFloat (this.value))
	cid = JSC$asm_genconstant (String.pack ("Cd", JSC$CONST_FLOAT,
						this.value));
      else
	cid = JSC$asm_genconstant (String.pack ("C", JSC$CONST_NAN));
    }
  else if (typeof this.value == "string")
    cid = JSC$asm_genconstant (String.pack ("CN", JSC$CONST_STRING,
					    this.value.length)
			       + this.value);
  else if (typeof this.value == "#builtin")
    {
      /* Regular expression. */
      var flags = 0;

      if (this.value.global)
	flags |= JSC$CONST_REGEXP_FLAG_G;
      if (this.value.ignoreCase)
	flags |= JSC$CONST_REGEXP_FLAG_I;

      cid = JSC$asm_genconstant (String.pack ("CCN", JSC$CONST_REGEXP, flags,
					      this.value.source.length)
				 + this.value.source);
    }
  else
    error ("ASM_const_bytecode(): unknown type: " + typeof this.value);

  return String.pack ("N", cid);
}

/* const_null */

function JSC$ASM_const_null (ln)
{
  this.type = JSC$OP_CONST_NULL;
  this.linenum = ln;
  this.stack_delta = 1;
  this.size = 1;
  this.print = JSC$ASM_const_null_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_const_null_print (stream)
{
  stream.write ("\tconst_null\n");
}

/* const_true */

function JSC$ASM_const_true (ln)
{
  this.type = JSC$OP_CONST_TRUE;
  this.linenum = ln;
  this.stack_delta = 1;
  this.size = 1;
  this.print = JSC$ASM_const_true_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_const_true_print (stream)
{
  stream.write ("\tconst_true\n");
}

/* const_false */

function JSC$ASM_const_false (ln)
{
  this.type = JSC$OP_CONST_FALSE;
  this.linenum = ln;
  this.stack_delta = 1;
  this.size = 1;
  this.print = JSC$ASM_const_false_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_const_false_print (stream)
{
  stream.write ("\tconst_false\n");
}

/* const_undefined */

function JSC$ASM_const_undefined (ln)
{
  this.type = JSC$OP_CONST_UNDEFINED;
  this.linenum = ln;
  this.stack_delta = 1;
  this.size = 1;
  this.print = JSC$ASM_const_undefined_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_const_undefined_print (stream)
{
  stream.write ("\tconst_undefined\n");
}

/* const_i0 */

function JSC$ASM_const_i0 (ln)
{
  this.type = JSC$OP_CONST_I0;
  this.linenum = ln;
  this.stack_delta = 1;
  this.size = 1;
  this.print = JSC$ASM_const_i0_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_const_i0_print (stream)
{
  stream.write ("\tconst_i0\n");
}

/* const_i1 */

function JSC$ASM_const_i1 (ln)
{
  this.type = JSC$OP_CONST_I1;
  this.linenum = ln;
  this.stack_delta = 1;
  this.size = 1;
  this.print = JSC$ASM_const_i1_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_const_i1_print (stream)
{
  stream.write ("\tconst_i1\n");
}

/* const_i2 */

function JSC$ASM_const_i2 (ln)
{
  this.type = JSC$OP_CONST_I2;
  this.linenum = ln;
  this.stack_delta = 1;
  this.size = 1;
  this.print = JSC$ASM_const_i2_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_const_i2_print (stream)
{
  stream.write ("\tconst_i2\n");
}

/* const_i3 */

function JSC$ASM_const_i3 (ln)
{
  this.type = JSC$OP_CONST_I3;
  this.linenum = ln;
  this.stack_delta = 1;
  this.size = 1;
  this.print = JSC$ASM_const_i3_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_const_i3_print (stream)
{
  stream.write ("\tconst_i3\n");
}

/* const_i */

function JSC$ASM_const_i (ln, value)
{
  this.type = JSC$OP_CONST_I;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = 1;
  this.size = 5;
  this.print = JSC$ASM_const_i_print;
  this.bytecode = JSC$ASM_bytecode_int32;
  this.link = JSC$asm_link;
}

function JSC$ASM_const_i_print (stream)
{
  stream.write ("\tconst_i\t\t" + this.value.toString () + "\n");
}

/* load_global */

function JSC$ASM_load_global (ln, value)
{
  this.type = JSC$OP_LOAD_GLOBAL;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = 1;
  this.size = 5;
  this.print = JSC$ASM_load_global_print;
  this.bytecode = JSC$ASM_bytecode_symbol;
  this.link = JSC$asm_link;
}

function JSC$ASM_load_global_print (stream)
{
  stream.write ("\tload_global\t" + this.value + "\n");
}

/* store_global */

function JSC$ASM_store_global (ln, value)
{
  this.type = JSC$OP_STORE_GLOBAL;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = -1;
  this.size = 5;
  this.print = JSC$ASM_store_global_print;
  this.bytecode = JSC$ASM_bytecode_symbol;
  this.link = JSC$asm_link;
}

function JSC$ASM_store_global_print (stream)
{
  stream.write ("\tstore_global\t" + this.value + "\n");
}

/* load_arg */

function JSC$ASM_load_arg (ln, value)
{
  this.type = JSC$OP_LOAD_ARG;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = 1;
  this.size = 2;
  this.print = JSC$ASM_load_arg_print;
  this.bytecode = JSC$ASM_bytecode_int8;
  this.link = JSC$asm_link;
}

function JSC$ASM_load_arg_print (stream)
{
  stream.write ("\tload_arg\t" + this.value.toString () + "\n");
}

/* store_arg */

function JSC$ASM_store_arg (ln, value)
{
  this.type = JSC$OP_STORE_ARG;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = -1;
  this.size = 2;
  this.print = JSC$ASM_store_arg_print;
  this.bytecode = JSC$ASM_bytecode_int8;
  this.link = JSC$asm_link;
}

function JSC$ASM_store_arg_print (stream)
{
  stream.write ("\tstore_arg\t" + this.value.toString () + "\n");
}

/* load_local */

function JSC$ASM_load_local (ln, value)
{
  this.type = JSC$OP_LOAD_LOCAL;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = 1;
  this.size = 3;
  this.print = JSC$ASM_load_local_print;
  this.bytecode = JSC$ASM_bytecode_int16;
  this.link = JSC$asm_link;
}

function JSC$ASM_load_local_print (stream)
{
  stream.write ("\tload_local\t" + this.value.toString () + "\n");
}

/* store_local */

function JSC$ASM_store_local (ln, value)
{
  this.type = JSC$OP_STORE_LOCAL;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = -1;
  this.size = 3;
  this.print = JSC$ASM_store_local_print;
  this.bytecode = JSC$ASM_bytecode_int16;
  this.link = JSC$asm_link;
}

function JSC$ASM_store_local_print (stream)
{
  stream.write ("\tstore_local\t" + this.value.toString () + "\n");
}

/* load_property */

function JSC$ASM_load_property (ln, value)
{
  this.type = JSC$OP_LOAD_PROPERTY;
  this.linenum = ln;
  this.value = value;
  this.size = 5;
  this.print = JSC$ASM_load_property_print;
  this.bytecode = JSC$ASM_bytecode_symbol;
  this.link = JSC$asm_link;
}

function JSC$ASM_load_property_print (stream)
{
  stream.write ("\tload_property\t" + this.value + "\n");
}

/* store_property */

function JSC$ASM_store_property (ln, value)
{
  this.type = JSC$OP_STORE_PROPERTY;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = -2;
  this.size = 5;
  this.print = JSC$ASM_store_property_print;
  this.bytecode = JSC$ASM_bytecode_symbol;
  this.link = JSC$asm_link;
}

function JSC$ASM_store_property_print (stream)
{
  stream.write ("\tstore_property\t" + this.value + "\n");
}

/* load_array */

function JSC$ASM_load_array (ln)
{
  this.type = JSC$OP_LOAD_ARRAY;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_load_array_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_load_array_print (stream)
{
  stream.write ("\tload_array\n");
}

/* store_array */

function JSC$ASM_store_array (ln)
{
  this.type = JSC$OP_STORE_ARRAY;
  this.linenum = ln;
  this.stack_delta = -3;
  this.size = 1;
  this.print = JSC$ASM_store_array_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_store_array_print (stream)
{
  stream.write ("\tstore_array\n");
}

/* nth */

function JSC$ASM_nth (ln)
{
  this.type = JSC$OP_NTH;
  this.linenum = ln;
  this.size = 1;
  this.print = JSC$ASM_nth_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_nth_print (stream)
{
  stream.write ("\tnth\n");
}

/* cmp_eq */

function JSC$ASM_cmp_eq (ln)
{
  this.type = JSC$OP_CMP_EQ;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_cmp_eq_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_cmp_eq_print (stream)
{
  stream.write ("\tcmp_eq\n");
}

/* cmp_ne */

function JSC$ASM_cmp_ne (ln)
{
  this.type = JSC$OP_CMP_NE;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_cmp_ne_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_cmp_ne_print (stream)
{
  stream.write ("\tcmp_ne\n");
}

/* cmp_lt */

function JSC$ASM_cmp_lt (ln)
{
  this.type = JSC$OP_CMP_LT;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_cmp_lt_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_cmp_lt_print (stream)
{
  stream.write ("\tcmp_lt\n");
}

/* cmp_gt */

function JSC$ASM_cmp_gt (ln)
{
  this.type = JSC$OP_CMP_GT;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_cmp_gt_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_cmp_gt_print (stream)
{
  stream.write ("\tcmp_gt\n");
}

/* cmp_le */

function JSC$ASM_cmp_le (ln)
{
  this.type = JSC$OP_CMP_LE;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_cmp_le_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_cmp_le_print (stream)
{
  stream.write ("\tcmp_le\n");
}

/* cmp_ge */

function JSC$ASM_cmp_ge (ln)
{
  this.type = JSC$OP_CMP_GE;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_cmp_ge_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_cmp_ge_print (stream)
{
  stream.write ("\tcmp_ge\n");
}

/* cmp_seq */

function JSC$ASM_cmp_seq (ln)
{
  this.type = JSC$OP_CMP_SEQ;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_cmp_seq_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_cmp_seq_print (stream)
{
  stream.write ("\tcmp_seq\n");
}

/* cmp_sne */

function JSC$ASM_cmp_sne (ln)
{
  this.type = JSC$OP_CMP_SNE;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_cmp_sne_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_cmp_sne_print (stream)
{
  stream.write ("\tcmp_sne\n");
}

/* sub */

function JSC$ASM_sub (ln)
{
  this.type = JSC$OP_SUB;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_sub_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_sub_print (stream)
{
  stream.write ("\tsub\n");
}

/* add */

function JSC$ASM_add (ln)
{
  this.type = JSC$OP_ADD;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_add_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_add_print (stream)
{
  stream.write ("\tadd\n");
}

/* mul */

function JSC$ASM_mul (ln)
{
  this.type = JSC$OP_MUL;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_mul_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_mul_print (stream)
{
  stream.write ("\tmul\n");
}

/* div */

function JSC$ASM_div (ln)
{
  this.type = JSC$OP_DIV;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_div_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_div_print (stream)
{
  stream.write ("\tdiv\n");
}

/* mod */

function JSC$ASM_mod (ln)
{
  this.type = JSC$OP_MOD;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_mod_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_mod_print (stream)
{
  stream.write ("\tmod\n");
}

/* neg */

function JSC$ASM_neg (ln)
{
  this.type = JSC$OP_NEG;
  this.linenum = ln;
  this.size = 1;
  this.print = JSC$ASM_neg_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_neg_print (stream)
{
  stream.write ("\tneg\n");
}

/* and */

function JSC$ASM_and (ln)
{
  this.type = JSC$OP_AND;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_and_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_and_print (stream)
{
  stream.write ("\tand\n");
}

/* not */

function JSC$ASM_not (ln)
{
  this.type = JSC$OP_NOT;
  this.linenum = ln;
  this.size = 1;
  this.print = JSC$ASM_not_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_not_print (stream)
{
  stream.write ("\tnot\n");
}

/* or */

function JSC$ASM_or (ln)
{
  this.type = JSC$OP_OR;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_or_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_or_print (stream)
{
  stream.write ("\tor\n");
}

/* xor */

function JSC$ASM_xor (ln)
{
  this.type = JSC$OP_XOR;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_xor_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_xor_print (stream)
{
  stream.write ("\txor\n");
}

/* shift_left */

function JSC$ASM_shift_left (ln)
{
  this.type = JSC$OP_SHIFT_LEFT;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_shift_left_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_shift_left_print (stream)
{
  stream.write ("\tshift_left\n");
}

/* shift_right */

function JSC$ASM_shift_right (ln)
{
  this.type = JSC$OP_SHIFT_RIGHT;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_shift_right_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_shift_right_print (stream)
{
  stream.write ("\tshift_right\n");
}

/* shift_rright */

function JSC$ASM_shift_rright (ln)
{
  this.type = JSC$OP_SHIFT_RRIGHT;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_shift_rright_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_shift_rright_print (stream)
{
  stream.write ("\tshift_rright\n");
}

/* iffalse */

function JSC$ASM_iffalse (ln, value)
{
  this.type = JSC$OP_IFFALSE;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = -1;
  this.size = 5;
  this.print = JSC$ASM_iffalse_print;
  this.bytecode = JSC$ASM_bytecode_local_jump;
  this.link = JSC$asm_link;
}

function JSC$ASM_iffalse_print (stream)
{
  stream.write ("\tiffalse\t\t" + this.value.format () + "\n");
}

/* iftrue */

function JSC$ASM_iftrue (ln, value)
{
  this.type = JSC$OP_IFTRUE;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = -1;
  this.size = 5;
  this.print = JSC$ASM_iftrue_print;
  this.bytecode = JSC$ASM_bytecode_local_jump;
  this.link = JSC$asm_link;
}

function JSC$ASM_iftrue_print (stream)
{
  stream.write ("\tiftrue\t\t" + this.value.format () + "\n");
}

/* call_method */

function JSC$ASM_call_method (ln, value)
{
  this.type = JSC$OP_CALL_METHOD;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = 1;
  this.size = 5;
  this.print = JSC$ASM_call_method_print;
  this.bytecode = JSC$ASM_bytecode_symbol;
  this.link = JSC$asm_link;
}

function JSC$ASM_call_method_print (stream)
{
  stream.write ("\tcall_method\t" + this.value + "\n");
}

/* jmp */

function JSC$ASM_jmp (ln, value)
{
  this.type = JSC$OP_JMP;
  this.linenum = ln;
  this.value = value;
  this.size = 5;
  this.print = JSC$ASM_jmp_print;
  this.bytecode = JSC$ASM_bytecode_local_jump;
  this.link = JSC$asm_link;
}

function JSC$ASM_jmp_print (stream)
{
  stream.write ("\tjmp\t\t" + this.value.format () + "\n");
}

/* jsr */

function JSC$ASM_jsr (ln)
{
  this.type = JSC$OP_JSR;
  this.linenum = ln;
  this.stack_delta = 1;
  this.size = 1;
  this.print = JSC$ASM_jsr_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_jsr_print (stream)
{
  stream.write ("\tjsr\n");
}

/* return */

function JSC$ASM_return (ln)
{
  this.type = JSC$OP_RETURN;
  this.linenum = ln;
  this.size = 1;
  this.print = JSC$ASM_return_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_return_print (stream)
{
  stream.write ("\treturn\n");
}

/* typeof */

function JSC$ASM_typeof (ln)
{
  this.type = JSC$OP_TYPEOF;
  this.linenum = ln;
  this.size = 1;
  this.print = JSC$ASM_typeof_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_typeof_print (stream)
{
  stream.write ("\ttypeof\n");
}

/* new */

function JSC$ASM_new (ln)
{
  this.type = JSC$OP_NEW;
  this.linenum = ln;
  this.stack_delta = 1;
  this.size = 1;
  this.print = JSC$ASM_new_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_new_print (stream)
{
  stream.write ("\tnew\n");
}

/* delete_property */

function JSC$ASM_delete_property (ln, value)
{
  this.type = JSC$OP_DELETE_PROPERTY;
  this.linenum = ln;
  this.value = value;
  this.size = 5;
  this.print = JSC$ASM_delete_property_print;
  this.bytecode = JSC$ASM_bytecode_symbol;
  this.link = JSC$asm_link;
}

function JSC$ASM_delete_property_print (stream)
{
  stream.write ("\tdelete_property\t" + this.value + "\n");
}

/* delete_array */

function JSC$ASM_delete_array (ln)
{
  this.type = JSC$OP_DELETE_ARRAY;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_delete_array_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_delete_array_print (stream)
{
  stream.write ("\tdelete_array\n");
}

/* locals */

function JSC$ASM_locals (ln, value)
{
  this.type = JSC$OP_LOCALS;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = value;
  this.size = 3;
  this.print = JSC$ASM_locals_print;
  this.bytecode = JSC$ASM_bytecode_int16;
  this.link = JSC$asm_link;
}

function JSC$ASM_locals_print (stream)
{
  stream.write ("\tlocals\t\t" + this.value.toString () + "\n");
}

/* min_args */

function JSC$ASM_min_args (ln, value)
{
  this.type = JSC$OP_MIN_ARGS;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = -1;
  this.size = 2;
  this.print = JSC$ASM_min_args_print;
  this.bytecode = JSC$ASM_bytecode_int8;
  this.link = JSC$asm_link;
}

function JSC$ASM_min_args_print (stream)
{
  stream.write ("\tmin_args\t" + this.value.toString () + "\n");
}

/* load_nth_arg */

function JSC$ASM_load_nth_arg (ln)
{
  this.type = JSC$OP_LOAD_NTH_ARG;
  this.linenum = ln;
  this.size = 1;
  this.print = JSC$ASM_load_nth_arg_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_load_nth_arg_print (stream)
{
  stream.write ("\tload_nth_arg\n");
}

/* with_push */

function JSC$ASM_with_push (ln)
{
  this.type = JSC$OP_WITH_PUSH;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_with_push_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_with_push_print (stream)
{
  stream.write ("\twith_push\n");
}

/* with_pop */

function JSC$ASM_with_pop (ln, value)
{
  this.type = JSC$OP_WITH_POP;
  this.linenum = ln;
  this.value = value;
  this.size = 2;
  this.print = JSC$ASM_with_pop_print;
  this.bytecode = JSC$ASM_bytecode_int8;
  this.link = JSC$asm_link;
}

function JSC$ASM_with_pop_print (stream)
{
  stream.write ("\twith_pop\t" + this.value.toString () + "\n");
}

/* try_push */

function JSC$ASM_try_push (ln, value)
{
  this.type = JSC$OP_TRY_PUSH;
  this.linenum = ln;
  this.value = value;
  this.size = 5;
  this.print = JSC$ASM_try_push_print;
  this.bytecode = JSC$ASM_bytecode_local_jump;
  this.link = JSC$asm_link;
}

function JSC$ASM_try_push_print (stream)
{
  stream.write ("\ttry_push\t" + this.value.format () + "\n");
}

/* try_pop */

function JSC$ASM_try_pop (ln, value)
{
  this.type = JSC$OP_TRY_POP;
  this.linenum = ln;
  this.value = value;
  this.size = 2;
  this.print = JSC$ASM_try_pop_print;
  this.bytecode = JSC$ASM_bytecode_int8;
  this.link = JSC$asm_link;
}

function JSC$ASM_try_pop_print (stream)
{
  stream.write ("\ttry_pop\t\t" + this.value.toString () + "\n");
}

/* throw */

function JSC$ASM_throw (ln)
{
  this.type = JSC$OP_THROW;
  this.linenum = ln;
  this.stack_delta = -1;
  this.size = 1;
  this.print = JSC$ASM_throw_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_throw_print (stream)
{
  stream.write ("\tthrow\n");
}

/* iffalse_b */

function JSC$ASM_iffalse_b (ln, value)
{
  this.type = JSC$OP_IFFALSE_B;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = -1;
  this.size = 5;
  this.print = JSC$ASM_iffalse_b_print;
  this.bytecode = JSC$ASM_bytecode_local_jump;
  this.link = JSC$asm_link;
}

function JSC$ASM_iffalse_b_print (stream)
{
  stream.write ("\tiffalse_b\t" + this.value.format () + "\n");
}

/* iftrue */

function JSC$ASM_iftrue_b (ln, value)
{
  this.type = JSC$OP_IFTRUE_B;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = -1;
  this.size = 5;
  this.print = JSC$ASM_iftrue_b_print;
  this.bytecode = JSC$ASM_bytecode_local_jump;
  this.link = JSC$asm_link;
}

function JSC$ASM_iftrue_b_print (stream)
{
  stream.write ("\tiftrue_b\t" + this.value.format () + "\n");
}

/* add_1_i */

function JSC$ASM_add_1_i (ln)
{
  this.type = JSC$OP_ADD_1_I;
  this.linenum = ln;
  this.size = 1;
  this.print = JSC$ASM_add_1_i_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_add_1_i_print (stream)
{
  stream.write ("\tadd_1_i\n");
}

/* add_2_i */

function JSC$ASM_add_2_i (ln)
{
  this.type = JSC$OP_ADD_2_I;
  this.linenum = ln;
  this.size = 1;
  this.print = JSC$ASM_add_2_i_print;
  this.link = JSC$asm_link;
}

function JSC$ASM_add_2_i_print (stream)
{
  stream.write ("\tadd_2_i\n");
}

/* load_global_w */

function JSC$ASM_load_global_w (ln, value)
{
  this.type = JSC$OP_LOAD_GLOBAL_W;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = 1;
  this.size = 5;
  this.print = JSC$ASM_load_global_w_print;
  this.bytecode = JSC$ASM_bytecode_symbol;
  this.link = JSC$asm_link;
}

function JSC$ASM_load_global_w_print (stream)
{
  stream.write ("\tload_global_w\t" + this.value + "\n");
}

/* jsr_w */

function JSC$ASM_jsr_w (ln, value)
{
  this.type = JSC$OP_JSR_W;
  this.linenum = ln;
  this.value = value;
  this.stack_delta = 1;
  this.size = 5;
  this.print = JSC$ASM_jsr_w_print;
  this.bytecode = JSC$ASM_bytecode_symbol;
  this.link = JSC$asm_link;
}

function JSC$ASM_jsr_w_print (stream)
{
  stream.write ("\tjsr_w\t\t" + this.value + "\n");
}

/*
 * General helpers.
 */

function JSC$asm_link ()
{
  this.next = null;

  if (JSC$asm_tail != null)
    {
      JSC$asm_tail_prev = JSC$asm_tail;
      JSC$asm_tail.next = this;
    }
  else
    JSC$asm_head = this;

  JSC$asm_tail = this;
}


/*
 * The phases of the assembler.
 */

/* This is called from the compiler initialization code. */
function JSC$asm_reset ()
{
  JSC$asm_label_count = 1;
  JSC$asm_head = JSC$asm_tail = JSC$asm_tail_prev = null;
  JSC$asm_constcount = 0;
  JSC$asm_constants = null;
  JSC$asm_known_constants = null;
}


function JSC$asm_generate ()
{
  var i;

  if (JSC$verbose)
    JSC$message ("jsc: generating assembler");

  JSC$ns = new JSC$NameSpace ();

  /* Functions. */
  for (i = 0; i < JSC$functions.length; i++)
    JSC$functions[i].asm ();

  /* Global statements. */
  if (JSC$global_stmts.length > 0)
    {
      /* Define the `.global' symbol. */
      new JSC$ASM_symbol (JSC$global_stmts[0].linenum, ".global").link ();

      /* Handle local variables. */
      var num_locals = JSC$count_locals_from_stmt_list (JSC$global_stmts);
      if (num_locals > 0)
	new JSC$ASM_locals (JSC$global_stmts[0].linenum, num_locals).link ();

      /* Generate assembler. */
      for (i = 0; i < JSC$global_stmts.length; i++)
	JSC$global_stmts[i].asm ();

      /*
       * Fix things so that also the global statement returns something
       * (this is required when we use eval() in JavaScript).
       */
      if (JSC$asm_tail_prev == null)
	{
	  /* This is probably illegal, but we don't panic. */
	  new JSC$ASM_const_undefined (0).link ();
	}
      else
	{
	  /*
	   * If the latest op is `pop', remove it.  Otherwise, append
	   * a `const_undefined'.
	   */
	  if (JSC$asm_tail.type == JSC$OP_POP)
	    {
	      JSC$asm_tail = JSC$asm_tail_prev;
	      JSC$asm_tail.next = null;
	      JSC$asm_tail_prev = null;
	    }
	  else
	    new JSC$ASM_const_undefined (JSC$asm_tail.linenum).link ();
	}
    }

  JSC$ns = null;
}


function JSC$asm_print (src_stream, stream)
{
  var i;
  var last_ln;
  var annotate = src_stream ? true : false;

  if (annotate)
    {
      stream.write ("; -*- asm -*-\n");

      /* Set the prev properties. */
      var prev = null;
      for (i = JSC$asm_head; i != null; prev = i, i = i.next)
	i.prev = prev;

      /*
       * Fix the label line numbers to be the same that the next
       * assembler operand has.
       */
      last_ln = 0;
      for (i = JSC$asm_tail; i != null; i = i.prev)
	{
	  if (i.type == JSC$ASM_LABEL)
	    i.linenum = last_ln;
	  else if (typeof i.linenum != "undefined")
	    last_ln = i.linenum;
	}
    }

  last_ln = 0;
  for (i = JSC$asm_head; i != null; i = i.next)
    {
      if (typeof i.linenum == "undefined")
	{
	  if (annotate)
	    stream.write ("; undefined linenum\n");
	}
      else
	while (annotate && i.linenum > last_ln)
	  {
	    var line = src_stream.readln ();
	    stream.write ("; " + line + "\n");
	    last_ln++;
	  }

      i.print (stream);
    }
}


function JSC$asm_is_load_op (op)
{
  return (op.type == JSC$OP_LOAD_GLOBAL
	  || op.type == JSC$OP_LOAD_ARG
	  || op.type == JSC$OP_LOAD_LOCAL);
}


function JSC$asm_is_store_op (op)
{
  return (op.type == JSC$OP_STORE_GLOBAL
	  || op.type == JSC$OP_STORE_ARG
	  || op.type == JSC$OP_STORE_LOCAL);
}


function JSC$asm_is_local_jump (op)
{
  return (op.type == JSC$OP_JMP
	  || op.type == JSC$OP_IFFALSE
	  || op.type == JSC$OP_IFTRUE
	  || op.type == JSC$OP_IFFALSE_B
	  || op.type == JSC$OP_IFTRUE_B
	  || op.type == JSC$OP_TRY_PUSH);
}


function JSC$asm_is_const_op (op)
{
  return (JSC$OP_CONST <= op.type && op.type <= JSC$OP_CONST_I3);
}


function JSC$asm_lookup_next_op (item)
{
  while (item != null &&
	 (item.type == JSC$ASM_LABEL || item.type == JSC$ASM_SYMBOL))
    item = item.next;

  return item;
}


function JSC$asm_optimize (flags)
{
  var item;

  /* Simple peephole optimization. */
  if ((flags & JSC$FLAG_OPTIMIZE_PEEPHOLE) != 0)
    {
      if (JSC$verbose)
	JSC$message ("jsc: optimize: peephole");

      for (item = JSC$asm_head; item != null; item = item.next)
	{
	  /*
	   * Optimization for dup ... pop cases where pop removes the
	   * item duplicated by dup.
	   */
	  if (item.next != null && item.next.type == JSC$OP_DUP)
	    {
	      var balance = 2;
	      var found = false;
	      var i1;

	      for (i1 = item.next.next;
		   i1 != null && i1.next != null;
		   i1 = i1.next)
		{
		  var i2 = i1.next;

		  /*
		   * The lookup ends on branches, and on dup, throw,
		   * and try_pop operands.  We optimize on a basic
		   * block and we match the closest dup-pop pairs.
		   */
		  if (JSC$asm_is_local_jump (i1)
		      || i1.type == JSC$OP_JSR
		      || i1.type == JSC$OP_NEW
		      || i1.type == JSC$OP_CALL_METHOD
		      || i1.type == JSC$OP_RETURN
		      || i1.type == JSC$ASM_SYMBOL
		      || i1.type == JSC$ASM_LABEL
		      || i1.type == JSC$OP_DUP
		      || i1.type == JSC$OP_TRY_POP
		      || i1.type == JSC$OP_THROW)
		    break;

		  if (i1.stack_delta)
		    {
		      balance += i1.stack_delta;
		      if (balance <= 0)
			/* Going to negative.  Stop here. */
			break;
		    }

		  if (i2.type == JSC$OP_POP && balance == 1)
		    {
		      /* Found a matching pop. */
		      found = true;
		      i1.next = i2.next;
		      break;
		    }
		}

	      if (found)
		{
		  /* The dup can be removed. */
		  item.next = item.next.next;
		}
	    }

	  /* Two instruction optimization (starting from item.next). */
	  if (item.next != null && item.next.next != null)
	    {
	      var i1 = item.next;
	      var i2 = i1.next;

	      if (i1.type == JSC$OP_APOP
		  && i2.type == JSC$OP_POP)
		{
		  /*
		   * i1:	apop n
		   * i2:	pop		->	pop_n n + 1
		   */
		  var i = new JSC$ASM_pop_n (i1.linenum, i1.value + 1);
		  item.next = i;
		  i.next = i2.next;
		}
	    }
	  if (item.next != null && item.next.next != null)
	    {
	      var i1 = item.next;
	      var i2 = i1.next;

	      if (i1.type == JSC$OP_CONST_TRUE
		  && (i2.type == JSC$OP_IFFALSE
		      || i2.type == JSC$OP_IFFALSE_B))
		{
		  /*
		   * i1:	const_true
		   * i2:	iffalse{,_b}	.LX	=> ---
		   */
		  item.next = i2.next;
		}
	    }
	  if (item.next != null && item.next.next != null)
	    {
	      var i1 = item.next;
	      var i2 = i1.next;

	      if (i1.type == JSC$OP_CONST_FALSE
		  && (i2.type == JSC$OP_IFTRUE
		      || i2.type == JSC$OP_IFTRUE_B))
		{
		  /*
		   * i1:	const_false
		   * i2:	iftrue{,_b}	.LX	=> ---
		   */
		  item.next = i2.next;
		}
	    }
	  if (item.next != null && item.next.next != null)
	    {
	      var i1 = item.next;
	      var i2 = i1.next;

	      if ((i1.type == JSC$OP_CONST_FALSE
		   && (i2.type == JSC$OP_IFFALSE
		       || i2.type == JSC$OP_IFFALSE_B))
		  || (i1.type == JSC$OP_CONST_TRUE
		      && (i2.type == JSC$OP_IFTRUE
			  || i2.type == JSC$OP_IFTRUE_B)))
		{
		  /*
		   * i1:	const_false
		   * i2:	iffalse{,_b}	.LX	=> jmp .LX
		   *
		   * i1:	const_true
		   * i2:	iftrue{,_b}	.LX	=> jmp .LX
		   */
		  var i = new JSC$ASM_jmp (i1.linenum, i2.value);
		  item.next = i;
		  i.next = i2.next;
		}
	    }
	}
    }

  /* Jumps to jumps. */
  if ((flags & JSC$FLAG_OPTIMIZE_JUMPS) != 0)
    {
      if (JSC$verbose)
	JSC$message ("jsc: optimize: jumps to jumps");
      for (item = JSC$asm_head; item != null; item = item.next)
	if (JSC$asm_is_local_jump (item))
	  {
	    var i2;

	    /* Operand's value is a label */
	    i2 = JSC$asm_lookup_next_op (item.value);

	    if (i2 != null && i2.type == JSC$OP_JMP)
	      /* Ok, we can jump there directly. */
	      item.value = i2.value;
	  }
    }

  if ((flags & JSC$FLAG_OPTIMIZE_HEAVY) != 0)
    JSC$optimize_heavy ();

  /*
   * Optimizations for the size of the generated byte-code.  It isn't
   * probably worth of doing these optimization for interactive
   * scripts since these won't affect the speed of the execution.
   * However, these optimizations make the byte-code files smaller so
   * these are nice for batch-compiled files.
   */
  if ((flags & JSC$FLAG_OPTIMIZE_BC_SIZE) != 0)
    {
      var delta = true;

      while (delta)
	{
	  delta = false;

	  /* Remove un-referenced labels. */

	  if (JSC$verbose)
	    JSC$message ("jsc: optimize: removing un-referenced labels");

	  /* First, make all labels unreferenced. */
	  for (item = JSC$asm_head; item != null; item = item.next)
	    if (item.type == JSC$ASM_LABEL)
	      item.referenced = false;

	  /* Second, mark all referenced labels. */
	  for (item = JSC$asm_head; item != null; item = item.next)
	    if (JSC$asm_is_local_jump (item))
	      item.value.referenced = true;

	  /* Third, remove all un-referenced labels. */
	  for (item = JSC$asm_head; item != null; item = item.next)
	    while (item.next != null && item.next.type == JSC$ASM_LABEL
		   && !item.next.referenced
		   && item.next.next != null)
	      {
		delta = true;
		item.next = item.next.next;
	      }

	  /* Dead code elimination. */
	  if (JSC$verbose)
	    JSC$message ("jsc: optimize: dead code elimination");
	  for (item = JSC$asm_head; item != null; item = item.next)
	    if (item.type == JSC$OP_RETURN || item.type == JSC$OP_JMP)
	      while (item.next != null && item.next.type != JSC$ASM_SYMBOL
		     && item.next.type != JSC$ASM_LABEL)
		{
		  delta = true;
		  item.next = item.next.next;
		}


	  /* Simple peephole optimization. */
	  if (JSC$verbose)
	    JSC$message ("jsc: optimize: peephole");
	  for (item = JSC$asm_head; item != null; item = item.next)
	    {
	      /* Two instruction optimization (starting from item.next). */
	      if (item.next != null && item.next.next != null)
		{
		  var i1 = item.next;
		  var i2 = i1.next;

		  if (i1.type == JSC$OP_JMP
		      && i2.type == JSC$ASM_LABEL
		      && i1.value == i2)
		    {
		      /*
		       * i1:	jmp	.LX
		       * i2:	.LX		=> .LX
		       */
		      item.next = i2;
		      delta = true;
		    }
		}
	    }
	}
    }
}


function JSC$optimize_heavy ()
{
  if (JSC$verbose)
    JSC$message ("jsc: optimize: liveness analyzing");

  /* First, set the prev pointers and zero usage flags. */
  var item, prev = null;

  for (item = JSC$asm_head; item != null; prev = item, item = item.next)
    {
      item.prev = prev;
      item.live_args = 0;
      item.live_locals = 0;
      item.live_used = false;
    }

  /* For each function. */
  var ftail, fhead;
  for (ftail = JSC$asm_tail; ftail != null; ftail = fhead.prev)
    {
      var change = true;

      /* While there is change in the liveness. */
      while (change)
	{
	  change = false;

	  for (fhead = ftail;
	       fhead.type != JSC$ASM_SYMBOL;
	       fhead = fhead.prev)
	    {
	      var floc, farg;

	      if (fhead.next != null)
		{
		  floc = fhead.next.live_locals;
		  farg = fhead.next.live_args;
		}
	      else
		floc = farg = 0;

	      if (fhead.type == JSC$OP_LOAD_LOCAL && fhead.value < 32)
		floc |= (1 << fhead.value);

	      if (fhead.type == JSC$OP_STORE_LOCAL && fhead.value < 32)
		floc &= ~(1 << fhead.value);

	      if (fhead.type == JSC$OP_LOAD_ARG && fhead.value < 32)
		farg |= (1 << fhead.value);

	      if (fhead.type == JSC$OP_STORE_ARG && fhead.value < 32)
		farg &= ~(1 << fhead.value);

	      if (JSC$asm_is_local_jump (fhead))
		{
		  floc |= fhead.value.live_locals;
		  fhead.value.live_used = true;
		}

	      if (fhead.live_used && (fhead.live_locals != floc
				      || fhead.live_args != farg))
		change = true;

	      fhead.live_used = false;
	      fhead.live_locals = floc;
	      fhead.live_args = farg;
	    }
	}
    }

  /*
   * When we have the liveness analyzing performed, we can do some
   * fancy optimizations.
   */

  if (JSC$verbose)
    JSC$message ("jsc: optimize: peephole");

  for (item = JSC$asm_head; item != null; item = item.next)
    {
      /* Three instruction optimization. */
      if (item.next != null && item.next.next != null
	  && item.next.next.next != null)
	{
	  var i1 = item.next;
	  var i2 = i1.next;
	  var i3 = i2.next;

	  if (i1.type == JSC$OP_STORE_LOCAL
	      && i2.type == JSC$OP_LOAD_LOCAL
	      && i1.value == i2.value
	      && (i3.live_locals & (1 << i1.value)) == 0)
	    {
	      /*
	       * i1:	store_local 	n
	       * i2:	load_local	n
	       * i3:	nnn (n not live)	=> nnn
	       */

	      item.next = i3;
	    }
	}
    }
}


function JSC$asm_finalize ()
{
  var item;
  var offset = 0;

  for (item = JSC$asm_head; item != null; item = item.next)
    {
      item.offset = offset;
      offset += item.size;
    }
}


function JSC$ConstantReg ()
{
}

function JSC$asm_genconstant (val)
{
  if (JSC$asm_known_constants == null)
    JSC$asm_known_constants = new JSC$ConstantReg ();

  /* Lookup <val> from a list of known constants. */
  var id = JSC$asm_known_constants[val];
  if (typeof id == "number")
    return id;

  /* This is a new constant. */
  JSC$asm_constants.append (val);
  JSC$asm_known_constants[val] = JSC$asm_constcount;

  return JSC$asm_constcount++;
}

function JSC$asm_bytecode ()
{
  var item;
  var symtab = new String ("");
  var nsymtab_entries = 0;
  var code = new String ("");
  var debug = new String ("");
  var debug_last_linenum = 0;

  if (JSC$verbose)
    JSC$message ("jsc: generating byte-code");

  if (JSC$generate_debug_info)
    /* Source file name. */
    debug.append (String.pack ("CN", JSC$DEBUG_FILENAME, JSC$filename.length)
		  + JSC$filename);

  JSC$asm_constants = new String ("");

  for (item = JSC$asm_head; item != null; item = item.next)
    {
      if (item.type == JSC$ASM_SYMBOL)
	{
	  symtab.append (item.value + String.pack ("CN", 0, item.offset));
	  nsymtab_entries++;
	}
      else if (item.type == JSC$ASM_LABEL)
	;
      else
	{
	  /* Real assembler operands. */

	  if (JSC$generate_debug_info)
	    if (item.linenum != debug_last_linenum)
	      {
		debug.append (String.pack ("CNN", JSC$DEBUG_LINENUMBER,
					   item.offset + item.size,
					   item.linenum));
		debug_last_linenum = item.linenum;
	      }

	  if (item.size == 1)
	    /* We handle these. */
	    code.append (String.pack ("C", item.type));
	  else
	    {
	      /*
	       * All operands which take an argument, have a method to create
	       * the byte code entry for their argument.
	       */
	      code.append (String.pack ("C", item.type) + item.bytecode ());
	    }
	}
    }

  symtab = String.pack ("N", nsymtab_entries) + symtab;

  if (JSC$verbose)
    {
      var msg = ("jsc: code=" + code.length.toString ()
		 + ", constants=" + JSC$asm_constants.length.toString ()
		 + ", symtab=" + symtab.length.toString ());

      if (JSC$generate_debug_info)
	msg += ", debug=" + debug.length.toString ();

      msg += (", headers="
	      + (32 + (JSC$generate_debug_info ? 8 : 0)).toString ()
	      + ", total="
	      + (code.length + JSC$asm_constants.length + symtab.length
		 + debug.length + 32
		 + (JSC$generate_debug_info ? 8 : 0)).toString ());
      JSC$message (msg);
    }

  return (String.pack ("NN", JSC$BC_MAGIC,
		       3 + (JSC$generate_debug_info ? 1 : 0))
	  + String.pack ("NN", JSC$BC_SECT_CODE, code.length) + code

	  + String.pack ("NN", JSC$BC_SECT_CONSTANTS,
			 JSC$asm_constants.length)
	  + JSC$asm_constants

	  + String.pack ("NN", JSC$BC_SECT_SYMTAB, symtab.length) + symtab

	  + (JSC$generate_debug_info
	     ? String.pack ("NN", JSC$BC_SECT_DEBUG, debug.length) + debug
	     : ""));
}


/*
Local variables:
mode: c
End:
*/


syntax highlighted by Code2HTML, v. 0.9.1