import java.io.*;
import java.util.*;
import java.math.*;

class IR {
    
  static abstract class Operand { }

  static class RegTemp extends Operand {   // Temporary register %ti
    int i;
    RegTemp(int i) {
      this.i = i;
    }
    public boolean equals(Object l) {
      return (l instanceof RegTemp) && (((RegTemp) l).i == i);
    }
    public int hashCode() {
      return i;
    }
    public String toString () {
      return "%t" + i;
    }
  }
  static class RegO0 extends Operand {  // Register %o0 (place to get function return value)
    public boolean equals(Object l) {
      return (l instanceof RegO0);
    }
    public int hashCode() {
      return 1001;
    }
    public String toString () {
      return"%o0";
    }
  }
  static RegO0 REGO0 = new RegO0();
  static class RegI0 extends Operand {  // Register %i0 (place to set function return value)
    public boolean equals(Object l) {
      return (l instanceof RegI0);
    }
    public int hashCode() {
      return 1002;
    }
    public String toString () {
      return "%i0";
    }
  }
  static RegI0 REGI0 = new RegI0();
  static class IntLit extends Operand { // Integer literal
    int i;
    IntLit(int i) {
      this.i = i;
    }
    public boolean equals(Object l) {
      return (l instanceof IntLit) && (((IntLit) l).i == i);
    }
    public int hashCode() {
      return i;
    }
    public String toString () {
      return "" + i;
    }
  }
  static class AddrString extends Operand { // Address of string literal
    String s;
    AddrString(String s) {
      this.s = s;
    }
    public boolean equals(Object l) {
      return (l instanceof AddrString) && (((AddrString) l).s.equals(s));
    }
    public int hashCode() {
      return s.hashCode();
    }
    public String toString () {
      return "\"" + s + "\"";
    }
  }
  static class AddrArg extends Operand { // Address of call argument $ai
    int i;
    AddrArg(int i) {
      this.i = i;
    }
    public boolean equals(Object l) {
      return (l instanceof AddrArg) && (((AddrArg) l).i == i);
    }
    public int hashCode() {
      return i;
    }
    public String toString () {
      return "$a" + i;
    }
  }
  static class AddrName extends Operand { // Named address
    String s;
    AddrName(String s) {
      this.s = s;
    }
    public boolean equals(Object l) {
      boolean b = (l instanceof AddrName) && (((AddrName) l).s.equals(s));
      return b;
    }
    public int hashCode() {
      return s.hashCode();
    }
    public String toString () {
      return s;
    }
  }
  static class AddrNull extends Operand { // Null address
    public boolean equals(Object l) {
      return (l instanceof AddrNull);
    }
    public int hashCode() {
      return 1003;
    }
    public String toString () {
      return "$null";
    }
  }
  static class Label extends Operand { // Numeric label
    int i;
    Label(int i) {
      this.i = i;
    }
    public boolean equals(Object l) {
      return (l instanceof Label) && (((Label) l).i == i);
    }
    public int hashCode() {
      return i;
    }
    public String toString () {
      return "L" + i; 
    }
  }

  // Instruction op codes
  static final int 
    NO_OP = 0,
    LD_OP = 1,
    ST_OP = 2,
    RET_OP = 3,
    CALL_OP = 4,
    SCALL_OP = 5,
    BA_OP = 6,
    BG_OP = 7,
    BL_OP = 8,
    BE_OP = 9,
    BGE_OP = 10,
    BLE_OP = 11,
    BNE_OP = 12,
    BGU_OP = 13,
    BLU_OP = 14,
    BGEU_OP = 15,
    BLEU_OP = 16,
    CMP_OP = 17,
    MOV_OP = 18,
    ADD_OP = 19,
    SUB_OP = 20,
    SMUL_OP = 21,
    SDIV_OP = 22,
    ADDA_OP = 23;


  public static String opToString(int op) {
    switch (op) {
    case NO_OP: return "nop";
    case LD_OP: return "ld";
    case ST_OP: return "st";
    case RET_OP: return "return";
    case CALL_OP: return "call";
    case SCALL_OP: return "scall";
    case BA_OP: return "ba";
    case BG_OP: return "bg";
    case BL_OP: return "bl";
    case BE_OP: return "be";
    case BGE_OP: return "bge";
    case BLE_OP: return "ble";
    case BNE_OP: return "bne";
    case BGU_OP: return "bgu";
    case BLU_OP: return "blu";
    case BGEU_OP: return "bgeu";
    case BLEU_OP: return "bleu";
    case CMP_OP: return "cmp";
    case MOV_OP: return "mov";
    case ADD_OP: return "add";
    case SUB_OP: return "sub";
    case SMUL_OP: return "smul";
    case SDIV_OP: return "sdiv";
    case ADDA_OP: return "adda";
    default: throw new Error("undefined op code");
    }
  }      

  public static int opCount(int op) {
    switch (op) {
    case NO_OP: return 0;
    case LD_OP: return 2;
    case ST_OP: return 2;
    case RET_OP: return 0;
    case CALL_OP: return 1;
    case SCALL_OP: return 2;
    case BA_OP: return 1;
    case BG_OP: return 1;
    case BL_OP: return 1;
    case BE_OP: return 1;
    case BGE_OP: return 1;
    case BLE_OP: return 1;
    case BNE_OP: return 1;
    case BGU_OP: return 1;
    case BLU_OP: return 1;
    case BGEU_OP: return 1;
    case BLEU_OP: return 1;
    case CMP_OP: return 2;
    case MOV_OP: return 2;
    case ADD_OP: return 3;
    case SUB_OP: return 3;
    case SMUL_OP: return 3;
    case SDIV_OP: return 3;
    case ADDA_OP: return 3;
    default: throw new Error("undefined op code");
    }
  }
    
  static abstract class CodeLine {
    abstract public String pretty();
  }

  static class Inst extends CodeLine {
    int op;
    Operand rands[];
    Inst(int op, Operand[] rands) {
      this.op = op; this.rands = rands; 
    }
    Inst(int op) {
      this(op,new Operand[0]);
    }
    public String toString() {
      String r = opToString(op);
      switch (op) {
      case LD_OP:
	r += " [" + rands[0]  + "]," + rands[1];
	break;
      case ST_OP:
	r += " " + rands[0] + ",[" + rands[1] + "]";
	break;
      default:
	if (rands.length > 0) 
	  r += " " + rands[0];
	if (rands.length > 1)
	  r += "," + rands[1];
	if (rands.length > 2)
	  r += "," + rands[2];
	break;
      }
      return r;
    }
    public String pretty() {
      return "\t" + this.toString();
    }
  }
    
  static class LabelDec extends CodeLine {
    Label lab;
    LabelDec(Label lab) {
      this.lab = lab;
    }
    public String toString() {
      return lab.toString();
    }
    public String pretty() {
      return this.toString() + ":";
    }
  }

  static interface Decl {
    abstract String pretty();
  }

  static abstract class Binding {
    String id;
  }

  static class Var extends Binding implements Decl {
    Code initializer;
    Var(String id, Code initializer) {
      this.id = id; this.initializer = initializer;
    }
    public String toString() {
      return "VAR " + id + " " + initializer + "\n";
    }
    public String pretty() {
      return "VAR " + id + "\n" + initializer.pretty();
    }
  }

  static class Code {
    CodeLine[] codeLines;   // actual code may be a subset
    int low;    // index into codeLines 
    int high;   // index into codeLines 
    Code (CodeLine[] codeLines, int low, int high) {
      this.codeLines = codeLines; this.low = low; this.high = high;
    }
    Code (List codelist) {  // used by parser to construct raw code segment
      this.codeLines = (CodeLine[]) codelist.toArray(new CodeLine[0]);
      this.low = 0;
      this.high = codeLines.length - 1;
    }
    public String toString() {
      return "[" + low + "," + high + "]";
    }
    public String pretty() {
      String r = "CODE\n";
      for (int i = low; i <= high; i++)
	r += codeLines[i].pretty() + "\n";	
      r += "ENDCODE\n";
      return r;
    }
  }      

  static class Procs implements Decl {
    Proc procs[];
    Procs(List procs) {
      this.procs = (Proc[]) procs.toArray(new Proc[0]);
    }
    public String toString() {
      String r = "PROCS\n";
      for (int i = 0; i < procs.length; i++)
	r += procs[i];
      return r;
    }
    public String pretty() {
      String r = "PROCS\n";
      for (int i = 0; i < procs.length; i++)
	r += procs[i].pretty();
      return r;
    }
  }


  static class Body {
    Decl[] decls;
    Code bodyCode;            // main body (excluding VAR initializers)
    CodeLine[] codeLines;     // all codeLines: filled by constructor
    int[] labels;             // points into codeLines; filled by constructor
    Body (List decls, Code bodyCode) {
      this.decls = (Decl[]) decls.toArray(new Decl[0]);
      this.bodyCode = bodyCode;
      codeLines = flatten();  
      labels = bindLabels();
    }
    public String toString() {
      String r = "";
      for (int i = 0; i < decls.length; i++)
	r += decls[i];
      r += "Body:" + bodyCode + "\n";
      r += "Code:\n";
      for (int i = 1; i < codeLines.length; i++) 
	r += i + "\t" + codeLines[i].pretty() + "\n";	
      r += "Labels:\n";
      for (int i = 0; i < labels.length; i++)
	r += i + " -> " + labels[i] + "\n";
      return r;
    }
    public String pretty() {
      String r = "";
      for (int i = 0; i < decls.length; i++)
	r += decls[i].pretty();
      r += bodyCode.pretty();
      return r;
    }
    CodeLine[] flatten() {
      List allCodeLines = new ArrayList();
      allCodeLines.add(null);  // placeholder for entry point
      int low[] = new int[decls.length];
      int high[] = new int[decls.length];
      for (int i = 0; i < decls.length; i++) {
	Decl d = decls[i];
	if (d instanceof Var) {
	  Var v = (Var) d;
	  Code c = v.initializer;
	  low[i] = allCodeLines.size();
	  for (int j = c.low; j <= c.high; j++)
	    allCodeLines.add(c.codeLines[j]);
	  high[i] = allCodeLines.size() - 1;
	}
      }	
      int bodylow = allCodeLines.size();
      for (int j = bodyCode.low; j <= bodyCode.high; j++) 
	allCodeLines.add(bodyCode.codeLines[j]);
      int bodyhigh = allCodeLines.size() - 1; 
      CodeLine[] result = (CodeLine[]) allCodeLines.toArray(new CodeLine[0]);
      for (int i = 0; i < decls.length; i++) {
	Decl d = decls[i];
	if (d instanceof Var) {
	  Var v = (Var) d;
	  Code c = v.initializer;
	  c.codeLines = result;
	  c.low = low[i];
	  c.high = high[i];
	}
      }
      bodyCode.codeLines = result;
      bodyCode.low = bodylow;
      bodyCode.high = bodyhigh;
      return result;
    }
    int[] bindLabels() {
      SortedMap labelMap = new TreeMap();
      for (int i = 1; i < codeLines.length; i++) {
	CodeLine c = codeLines[i];
	if (c instanceof LabelDec)
	  labelMap.put(new Integer(((LabelDec) c).lab.i), new Integer(i));
      }
      int maxLabel = labelMap.isEmpty() ? -1 : ((Integer) (labelMap.lastKey())).intValue();
      labels = new int[maxLabel + 1];
      for (int j = 0; j < labels.length; j++) {
	Integer k = (Integer) (labelMap.get(new Integer(j)));
	labels[j] = (k == null) ? -1 : k.intValue();
      }
      return labels;
    }
  }

  static class Formal extends Binding {
    Formal (String id) {
      this.id = id;
    }
    public String toString() {
      return id;
    }
  }

  static class Proc extends Binding {
    Formal formals[];
    Body body;
    Proc (String id, List formals, Body body) {
      this.id = id; this.formals = (Formal[]) formals.toArray(new Formal[0]); this.body = body;
    }
    public String toString() {
      String r = "PROC " + id + " ( ";
      for (int i = 0; i < formals.length; i++)
	r += formals[i] + " ";
      r += ")\n" + body;
      return r;
    }
    public String pretty() {
      String r = "PROC " + id + " ( ";
      for (int i = 0; i < formals.length; i++)
	r += formals[i] + " ";
      r += ")\n" + body.pretty();
      return r;
    }
  }
}

