/** * 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); } }