/**
     * Create an EmitterDescriptor for the given methodName. This conmstructor creates a descriptor
     * that represents explicitly the types and size of the operands of the given emit* method. This
     * constructor encapsulate the logic to parse the given method name into the appropriate
     * explicit representation.
     */
    EmitterDescriptor(String methodName, Class<?>[] argTypes) {
      StringTokenizer toks = new StringTokenizer(methodName, "_");
      toks.nextElement(); // first element is emitXXX;
      args = new ArgumentType[toks.countTokens()];
      ArgumentType size = null;
      int count = 0;
      int argTypeNum = 0;
      for (int i = 0; i < args.length; i++) {
        String cs = toks.nextToken();
        ArgumentType code;
        if (argTypeNum < argTypes.length) {
          code = getEncoding(cs, argTypes[argTypeNum]);
        } else {
          code = getEncoding(cs, null);
        }
        argTypeNum += code.getParameters();
        if (DEBUG) {
          System.err.println(methodName + "[" + i + "] is " + code + " for " + cs);
        }

        args[count] = code;
        count++;
        if (code.isSize()) {
          size = code;
          count--;
        }
      }
      this.size = size;
      this.count = count;
    }
    /**
     * Write the Java code required for error checking and calling the emit method represented by a
     * singleton EmitterSet. A singleton EmiiterSet will typically be the result of a series of
     * splits of bigger sets, where the splits represent emitted queries of operand types and sizes.
     * (See emitSet) However, there may be cases when some operand has only one possible options, so
     * the splitting will not have generated any tests for it. In this case, we will emit assertions
     * that guarantee the operand is of the expected type. Note that the answers to queries
     * alrrready performed by splitting are known to be fine, so no additional error checking is
     * needed for cases they cover.
     *
     * @see #emitSet
     * @param opcode the IA32 opcode to generate
     * @param testsPerformed the set of queries already performed by splitting.
     * @param level level of indentation for prett printing
     */
    private void emitSingleton(String opcode, boolean[][] testsPerformed, int level) {
      EmitterDescriptor ed = (EmitterDescriptor) emitters.iterator().next();

      ArgumentType[] args = ed.getArgs();
      int count = ed.getCount();
      for (int i = 0; i < count; i++)
        if (!testsPerformed[i][args[i].ordinal()]) emitVerify(i, args[i], level);

      ArgumentType size = ed.getSize();
      if (size != null) {
        boolean needed = true;

        for (int i = 0; i < count; i++) if (testsPerformed[i][size.ordinal()]) needed = false;

        if (needed) emitVerify(0, size, level);

        if (size == ArgumentType.Byte)
          for (int i = 0; i < count; i++)
            if (args[i] == ArgumentType.GPRegister)
              if (currentOpcode.indexOf("MOVZX") == -1 && currentOpcode.indexOf("MOVSX") == -1) {
                emitTab(level);
                emit("if (VM.VerifyAssertions) opt_assert(");
                emitArgs(i, ArgumentType.GPRegister);
                emit(".isValidAs8bitRegister());\n");
              }
      }

      emitEmitCall(opcode, args, count, level, ed.getSize());
    }
    /**
     * Emit the Java code to call a particular emit method for a particular opcode. This method
     * takes representations of the opcode and operands of a given emit method, and generates the
     * appropriate Java source code to call it. It synthesizes the encoded emit method name, and
     * uses emitArgs to pass all the required arguments.
     *
     * @see #emitArgs
     * @param opcode the IA32 opcode of the emit method
     * @param args the encoding of each operand to the emit method
     * @param count the number of operands
     * @param level the level of tabbing for pretty output
     */
    private void emitEmitCall(
        String opcode, ArgumentType[] args, int count, int level, ArgumentType size) {
      if (DEBUG) {
        System.err.print("Emitting call for " + opcode + " with args: ");
        for (ArgumentType arg : args) {
          System.err.print(arg + " ");
        }
        System.err.println(" count=" + count + " level=" + level + " size=" + size);
      }
      emitTab(level);
      emit("emit" + opcode);
      for (int i = 0; i < count; i++) emit("_" + args[i].getAssemblerName());
      if (size != null) emit("_" + size.getAssemblerName());

      if (count == 0) emit("();\n");
      else {
        emit("(");
        for (int i = 0; i < count; i++) {
          emit("\n");
          emitTab(level + 1);
          emitArgs(i, args[i]);
          if (i == count - 1) emit(");\n");
          else emit(",");
        }
      }
    }
 /**
  * Generate code to fetch all the arguments needed for a given operand number and encoding. The
  * different argument encodings of the Assembler need different arguments to be passed to the
  * emitter function. For instance, a register-displacement mode operand needs to be given a base
  * register and an immediate displacement. This function generates the appropriate arguments given
  * the operand number and encoding; that is, it generates reads of the appropriate Instruction
  * argument and fetches of the appropriate pieces of information from the operand.
  *
  * @param argNumber The argument being generated.
  * @param argEcoding The encoding to use.
  */
 private static void emitArgs(int argNumber, ArgumentType argEncoding) {
   String op = getOperand(argNumber);
   switch (argEncoding) {
     case LabelOrImmediate:
       emit("getImm(" + op + "), getLabel(" + op + ")");
       break;
     case RegisterDisplacement:
       emit("getBase(" + op + "), getDisp(" + op + ")");
       break;
     case Absolute:
       emit("getDisp(" + op + ").toWord().toAddress()");
       break;
     case RegisterOffset:
       emit("getIndex(" + op + "), getScale(" + op + "), getDisp(" + op + ")");
       break;
     case RegisterIndexed:
       emit(
           "getBase(" + op + "), getIndex(" + op + "), getScale(" + op + "), getDisp(" + op + ")");
       break;
     case RegisterIndirect:
       emit("getBase(" + op + ")");
       break;
     default:
       emit("get" + argEncoding.getOptName() + "(" + op + ")");
   }
 }
 /**
  * For a given string representing a valid operand encoding for the Assembler, return the
  * corresponding Assembler constant. This function only looks for encodings of operand types, and
  * will not accept strings that correspond to size encodings.
  *
  * @param str A valid Assembler encoding of operand type
  * @return The Assembler constant corresponding to str, or -1 if none
  */
 private static ArgumentType getEncoding(String str, Class<?> type) {
   if (str.equals("Reg")) {
     if (type == null) {
       throw new Error("Unable to encode Reg with no type information");
     }
     String typeName = type.getName();
     str = typeName.substring(typeName.lastIndexOf('$') + 1) + "_Reg";
   }
   for (ArgumentType arg : ArgumentType.values()) {
     if (arg.getOptName().equals(str)) {
       return arg;
     }
   }
   throw new Error(
       "Unable to encode the argument " + str + " of type " + type + " as a valid argument type");
 }
    /**
     * Find the best operand type or size and operand number to partition this EmitterSet. This
     * method searches across all possible ways of splitting this set--all possible operand types
     * and sizes, and all possible operands--to determine which one splits the set most evenly.
     *
     * @return a SplitRecord representing the most-even split
     */
    SplitRecord split() {
      int splitArg = -1;
      ArgumentType splitTest = null;
      int splitDiff = 1000;
      for (int arg = 0; arg < 4; arg++) {
        for (ArgumentType test : ArgumentType.values()) {
          int c = getEncodingSplit(arg, test);
          if (c == 0) return new SplitRecord(arg, test);
          else if (c < splitDiff) {
            splitArg = arg;
            splitTest = test;
            splitDiff = c;
          }
        }
      }

      return new SplitRecord(splitArg, splitTest);
    }
 /**
  * This method checks whether the emit* method represented by this EmitterDescriptor expects the
  * argument type represented by enc as its argument'th operand. If enc is an operand type
  * encoding, this method checks wether the given argument is of the appropriate type. If enc is
  * an operand size encoding, the argument parameter is ignored, and this method checks whether
  * the emit* method represented operates upon data of the desired size.
  *
  * <p><EM>See the descriptions of the GenerateAssembler constants:</EM>
  *
  * <DL>
  *   <DT><EM>Operand types</EM> <DI>
  *       <UL>
  *         <LI>{@link #Immediate}
  *         <LI>{@link #Label}
  *         <LI>{@link #LabelOrImmediate}
  *         <LI>{@link #Absolute}
  *         <LI>{@link #Register}
  *         <LI>{@link #RegisterIndirect}
  *         <LI>{@link #RegisterOffset}
  *         <LI>{@link #RegisterIndexed}
  *       </UL>
  *   <DT><EM>Data size</EM>
  *       <UL>
  *         <LI>{@link #Byte}
  *         <LI>{@link #Word}
  *         <LI>{@link #Quad}
  *       </UL>
  * </DL>
  *
  * <p>
  *
  * @param argument The operand number examined
  * @param enc The argument type queried, as encoded as one of the operand type constants used
  *     throughout GenerateAssembler.
  * @return True if this method expects an argument type encoded by enc as its argument'th
  *     operand, and false otherwise.
  */
 boolean argMatchesEncoding(int argument, ArgumentType enc) {
   if (!enc.isSize()) return (count > argument) && args[argument] == enc;
   else return size == enc;
 }
 /**
  * Given an operand number and an encoding, generate a test to determine whether the given operand
  * matches the encoding. That is, generate code to the Assembler that examines a given operand of
  * the current Instruction, and determines whether it is of the type encoded by the given
  * encoding. This is used to generate the if statements of the dispatch functions for each opt
  * compiler opcode.
  *
  * @param argNumber The argument to examine
  * @param argEncoding The encoding for which to check
  */
 private static void emitTest(int argNumber, ArgumentType argEncoding) {
   if (argEncoding.isSize()) emit("is" + argEncoding.getOptName() + "(inst)");
   else emit("is" + argEncoding.getOptName() + "(" + getOperand(argNumber) + ")");
 }
  /** Generate an assembler for the opt compiler */
  public static void main(String[] args) {
    try {
      out = new FileWriter(System.getProperty("generateToDir") + "/AssemblerOpt.java");
    } catch (IOException e) {
      throw new Error(e);
    }

    emit("package org.jikesrvm.compilers.opt.mir2mc.ia32;\n\n");
    emit("import org.jikesrvm.*;\n\n");
    emit("import org.jikesrvm.compilers.opt.*;\n\n");
    emit("import org.jikesrvm.compilers.opt.ir.*;\n\n");
    emit("import org.jikesrvm.compilers.opt.ir.ia32.*;\n\n");
    emit("import static org.jikesrvm.compilers.opt.ir.ia32.ArchOperators.*;\n\n");
    emit("import static org.jikesrvm.compilers.opt.OptimizingCompilerException.opt_assert;\n\n");
    emit("\n\n");

    emit("/**\n");
    emit(" *  This class is the automatically-generated assembler for\n");
    emit(" * the optimizing compiler.  It consists of methods that\n");
    emit(" * understand the possible operand combinations of each\n");
    emit(" * instruction type, and how to translate those operands to\n");
    emit(" * calls to the Assember low-level emit method\n");
    emit(" *\n");
    emit(" * It is generated by GenerateAssembler.java\n");
    emit(" *\n");
    emit(" */\n");
    emit("public class AssemblerOpt extends AssemblerBase {\n\n");

    emitTab(1);
    emit("/**\n");
    emitTab(1);
    emit(" * @see org.jikesrvm.ArchitectureSpecific.Assembler\n");
    emitTab(1);
    emit(" */\n");
    emitTab(1);
    emit("public AssemblerOpt(int bcSize, boolean print, IR ir) {\n");
    emitTab(2);
    emit("super(bcSize, print, ir);\n");
    emitTab(1);
    emit("}");
    emit("\n\n");

    Method[] emitters = lowLevelAsm.getDeclaredMethods();
    Set<String> opcodes = getOpcodes(emitters);

    Iterator<String> i = opcodes.iterator();
    while (i.hasNext()) {
      String opcode = (String) i.next();
      setCurrentOpcode(opcode);
      emitTab(1);
      emit("/**\n");
      emitTab(1);
      emit(" *  Emit the given instruction, assuming that\n");
      emitTab(1);
      emit(" * it is a " + currentFormat + " instruction\n");
      emitTab(1);
      emit(" * and has a " + currentOpcode + " operator\n");
      emitTab(1);
      emit(" *\n");
      emitTab(1);
      emit(" * @param inst the instruction to assemble\n");
      emitTab(1);
      emit(" */\n");
      emitTab(1);
      emit("private void do" + opcode + "(Instruction inst) {\n");
      EmitterSet emitter = buildSetForOpcode(emitters, opcode);
      boolean[][] tp = new boolean[4][ArgumentType.values().length];
      emitter.emitSet(opcode, tp, 2);
      emitTab(1);
      emit("}\n\n");
    }

    emitTab(1);
    emit("/**\n");
    emitTab(1);
    emit(" *  The number of instructions emitted so far\n");
    emitTab(1);
    emit(" */\n");
    emitTab(1);
    emit("private int instructionCount = 0;\n\n");

    emitTab(1);
    emit("/**\n");
    emitTab(1);
    emit(" *  Assemble the given instruction\n");
    emitTab(1);
    emit(" *\n");
    emitTab(1);
    emit(" * @param inst the instruction to assemble\n");
    emitTab(1);
    emit(" */\n");
    emitTab(1);
    emit("public void doInst(Instruction inst) {\n");
    emitTab(2);
    emit("instructionCount++;\n");
    emitTab(2);
    emit("resolveForwardReferences(instructionCount);\n");
    emitTab(2);
    emit("switch (inst.getOpcode()) {\n");

    Set<String> emittedOpcodes = new HashSet<String>();

    i = opcodes.iterator();
    while (i.hasNext()) {
      String opcode = i.next();
      Iterator<String> operators = getMatchingOperators(opcode).iterator();
      while (operators.hasNext()) {
        String operator = operators.next();
        emitTab(3);
        emittedOpcodes.add(operator);
        emit("case IA32_" + operator + "_opcode:\n");
      }
      emitTab(4);
      emit("do" + opcode + "(inst);\n");
      emitTab(4);
      emit("break;\n");
    }

    // Special case because doJCC is handwritten to add
    // logic for short-forward branches
    emittedOpcodes.add("JCC");
    emitTab(3);
    emit("case IA32_JCC_opcode:\n");
    emitTab(4);
    emit("doJCC(inst);\n");
    emitTab(4);
    emit("break;\n");

    // Special case because doJMP is handwritten to add
    // logic for short-forward branches
    emittedOpcodes.add("JMP");
    emitTab(3);
    emit("case IA32_JMP_opcode:\n");
    emitTab(4);
    emit("doJMP(inst);\n");
    emitTab(4);
    emit("break;\n");

    // Kludge for IA32_LOCK which needs to call emitLockNextInstruction
    emittedOpcodes.add("LOCK");
    emitTab(3);
    emit("case IA32_LOCK_opcode:\n");
    emitTab(4);
    emit("emitLockNextInstruction();\n");
    emitTab(4);
    emit("break;\n");

    // Kludge for PATCH_POINT
    emitTab(3);
    emit("case IG_PATCH_POINT_opcode:\n");
    emitTab(4);
    emit("emitPatchPoint();\n");
    emitTab(4);
    emit("break;\n");

    // Kludge for LOWTABLESWITCH
    emitTab(3);
    emit("case MIR_LOWTABLESWITCH_opcode:\n");
    emitTab(4);
    emit("doLOWTABLESWITCH(inst);\n");
    emitTab(4);
    emit("// kludge table switches that are unusually long instructions\n");
    emitTab(4);
    emit("instructionCount += MIR_LowTableSwitch.getNumberOfTargets(inst);\n");
    emitTab(4);
    emit("break;\n");

    Set<String> errorOpcodes = getErrorOpcodes(emittedOpcodes);
    if (!errorOpcodes.isEmpty()) {
      i = errorOpcodes.iterator();
      while (i.hasNext()) {
        emitTab(3);
        emit("case IA32_" + i.next() + "_opcode:\n");
      }
      emitTab(4);
      emit(
          "throw new OptimizingCompilerException(inst + \" has unimplemented IA32 opcode (check excludedOpcodes)\");\n");
    }

    emitTab(2);
    emit("}\n");
    emitTab(2);
    emit("inst.setmcOffset( mi );\n");
    emitTab(1);
    emit("}\n\n");

    emit("\n}\n");

    try {
      out.close();
    } catch (IOException e) {
      throw new Error(e);
    }
  }