1 // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
8 // FEATURE: progress indicator
9 // FEATURE: emit bytecode rather than .java code (for on-the-fly classloading without javac present in "real" JVMs
11 public class Compiler implements Registers {
13 /** Stores the "case r XXX: ... run_YYYY();" blocks generated by the emitText method/ */
14 private StringBuffer runs = new StringBuffer();
15 /** Stores the "initData" and "cleadData" calls generated by the emitData and emitBSS methods */
16 private StringBuffer inits = new StringBuffer();
17 /** Stores lines to go in the class scope */
18 private StringBuffer classLevel = new StringBuffer();
20 /** The stream to write the compiled output to */
21 private PrintWriter out;
23 /** The ELF binary being read */
26 /** The name of the class beging generated */
27 private String fullClassName;
29 /** The name of the binary this class is begin generated from */
30 private String mipsBinary;
32 /** Used by the p() method to add indentation */
35 /** Prints a blank line to the output stream */
36 private void p() { out.println(); }
37 /** prints the given string (indented by <i>indent</i>*4 spaces) to the output stream */
38 private void p(String s) { out.println(indents[indent] + s); }
39 private void pblock(StringBuffer sb) { out.print(sb.toString()); }
41 private static String indents[] = new String[16];
42 static { String s=""; for(int i=0;i<indents.length;i++,s=s+" ") indents[i] = s; }
44 /** Thrown when the compilation fails for some reason */
45 private static class CompilationException extends Exception { public CompilationException(String s) { super(s); } }
47 // Set this to true to enable fast memory access
48 // When this is enabled a Java RuntimeException will be thrown when a page fault occures. When it is disabled
49 // a FaultException will be throw which is easier to catch and deal with, however. as the name implies, this is slower
50 private boolean fastMem = true;
52 // This MUST be a power of two. If it is not horrible things will happen
53 // NOTE: This value can be much higher without breaking the classfile
54 // specs (around 1024) but Hotstop seems to do much better with smaller
56 private int maxInsnPerMethod = 128;
59 private int maxBytesPerMethod;
60 private int methodMask;
61 private int methodShift;
62 private void maxInsnPerMethodInit() throws CompilationException {
63 if((maxInsnPerMethod&(maxInsnPerMethod-1)) != 0) throw new CompilationException("maxBytesPerMethod is not a power of two");
64 maxBytesPerMethod = maxInsnPerMethod*4;
65 methodMask = ~(maxBytesPerMethod-1);
66 while(maxBytesPerMethod>>>methodShift != 1) methodShift++;
69 // Store frequently used registers in local variables
70 // Unfortunately this doesn't seem to speed things up much
71 private String[] freqRegs = { /*"r2", "r29", "r3", "r16", "r5", "r17", "r6", "r18", "r4", "r31", "r19"*/ };
73 // True to try to determine which case statement are needed and only include them
74 private boolean pruneCases = true;
76 private boolean assumeTailCalls = true;
78 private boolean optimizedMemcpy = true;
80 // True to insert some code in the output to help diagnore compiler problems
81 private boolean debugCompiler = false;
83 // True to print various statistics about the compilation
84 private boolean printStats = false;
86 // True to generate runtime statistics that slow execution down significantly
87 private boolean runtimeStats = false;
89 private boolean supportCall = true;
91 private boolean nullPointerCheck = false;
93 private String runtimeClass = "org.xwt.mips.Runtime";
95 private String hashClass = "java.util.HashMap";
97 private boolean unixRuntime;
99 private boolean lessConstants = true;
101 private int pageSize = 4096;
102 private int totalPages = 65536;
103 private int pageShift;
104 private boolean onePage;
106 private void pageSizeInit() throws CompilationException {
108 Runtime.checkPageSize(pageSize,totalPages);
109 } catch(IllegalArgumentException e) {
110 throw new CompilationException(e.getMessage());
112 while(pageSize>>>pageShift != 1) pageShift++;
115 /** The address of the memcpy function in the binary (used for optimizedMemcpy) */
118 /** The address of the memset function in the binary (used for optimizedMemcpy) */
121 private static void usage() {
122 System.err.println("Usage: java Compiler [-outfile output.java] [-o options] [-dumpoptions] <classname> <binary.mips>");
123 System.err.println("-o takes mount(8) like options and can be specified multiple times");
124 System.err.println("Available options:");
125 for(int i=0;i<options.length;i+=2)
126 System.err.print(options[i] + ": " + wrapAndIndent(options[i+1],18-2-options[i].length(),18,62));
130 public static void main(String[] args) throws Exception {
131 String outfile = null;
133 String className = null;
134 String mipsBinary = null;
135 boolean dumpOptions = false;
137 while(args.length-arg > 0) {
138 if(args[arg].equals("-outfile")) {
140 if(arg==args.length) usage();
142 } else if(args[arg].equals("-o")) {
144 if(arg==args.length) usage();
145 if(o==null || o.length() == 0)
147 else if(args[arg].length() != 0)
148 o += "," + args[arg];
149 } else if(args[arg].equals("-dumpoptions")) {
151 } else if(className == null) {
152 className = args[arg];
153 } else if(mipsBinary == null) {
154 mipsBinary = args[arg];
160 if(className == null || mipsBinary == null) usage();
162 Writer w = outfile == null ? new OutputStreamWriter(System.out): new FileWriter(outfile);
165 Compiler comp = new Compiler(mipsBinary,className,w,o);
167 System.err.println("== Options ==");
168 for(int i=0;i<options.length;i+=2)
169 System.err.println(options[i] + ": " + comp.getOption(options[i]).get());
170 System.err.println("== End Options ==");
173 } catch(CompilationException e) {
174 System.err.println("Compiler Error: " + e.getMessage());
181 public Compiler(String mipsBinary, String fullClassName, Writer w, String options) throws CompilationException, IOException {
182 this.fullClassName = fullClassName;
183 this.mipsBinary = mipsBinary;
184 out = new PrintWriter(w);
186 parseOptions(options);
188 if(onePage && pageSize <= 4096) pageSize = 4*1024*1024;
189 if(nullPointerCheck && !fastMem) throw new CompilationException("fastMem must be enabled for nullPointerCheck to be of any use");
190 if(onePage && !fastMem) throw new CompilationException("fastMem must be enabled for onePage to be of any use");
191 if(totalPages == 1 && !onePage) throw new CompilationException("totalPages == 1 and onePage is not set");
192 if(onePage) totalPages = 1;
194 maxInsnPerMethodInit();
197 elf = new ELF(mipsBinary);
198 if(elf.header.type != ELF.ELFHeader.ET_EXEC) throw new CompilationException("Binary is not an executable");
199 if(elf.header.machine != ELF.ELFHeader.EM_MIPS) throw new CompilationException("Binary is not for the MIPS I Architecture");
200 if(elf.ident.data != ELF.ELFIdent.ELFDATA2MSB) throw new CompilationException("Binary is not big endian");
203 private boolean used;
204 public void go() throws CompilationException, IOException {
205 if(used) throw new Error("Compiler instances are good for one shot only");
210 if (fullClassName.indexOf('.') != -1) {
211 packageName = fullClassName.substring(0, fullClassName.lastIndexOf('.'));
212 className = fullClassName.substring(fullClassName.lastIndexOf('.') + 1);
214 className = fullClassName;
218 // Get a copy of the symbol table in the elf binary
219 ELF.Symtab symtab = elf.getSymtab();
220 if(symtab == null) throw new CompilationException("Binary has no symtab (did you strip it?)");
223 // Check for some functions we can override
224 sym = symtab.getGlobalSymbol("memcpy");
225 memcpy = sym == null ? -1 : sym.addr;
227 sym = symtab.getGlobalSymbol("memset");
228 memset = sym == null ? -1 : sym.addr;
230 ELF.Symbol userInfo = symtab.getGlobalSymbol("user_info");
231 ELF.Symbol gp = symtab.getGlobalSymbol("_gp");
232 if(gp == null) throw new CompilationException("no _gp symbol (did you strip the binary?)");
234 p("/* This file was generated from " + mipsBinary + " by Mips2Java on " + dateTime() + " */");
235 if (packageName != null) p("package " + packageName + ";");
236 if(runtimeStats) p("import java.util.*;");
238 if(unixRuntime && runtimeClass.startsWith("org.xwt.mips.")) runtimeClass = "org.xwt.mips.UnixRuntime";
239 p("public class " + className + " extends " + runtimeClass + " {");
242 p("/* program counter */");
243 p("private int pc = 0;");
245 p("private int lastPC = 0;");
247 p("/* General Purpose registers */");
248 p("private final static int r0 = 0;");
249 p("private int r1, r2, r3, r4, r5, r6, r7,");
250 p(" r8, r9, r10, r11, r12, r13, r14, r15,");
251 p(" r16, r17, r18, r19, r20, r21, r22, r23,");
252 p(" r24, r25, r26, r27, r28, r29, r30, r31,");
253 p(" hi = 0, lo = 0;");
254 p("/* FP registers */");
255 p("private int f0, f1, f2, f3, f4, f5, f6, f7,");
256 p(" f8, f9, f10, f11, f12, f13, f14, f15,");
257 p(" f16, f17, f18, f19, f20, f21, f22, f23,");
258 p(" f24, f25, f26, f27, f28, f29, f30, f31;");
259 p("/* FP Control Register */");
260 p("private int fcsr = 0;");
263 if(onePage) p("int[] page = readPages[0];");
265 Set jumpableAddresses = null;
267 // Find all possible branches
268 jumpableAddresses = new HashSet();
270 jumpableAddresses.add(new Integer(elf.header.entry));
272 ELF.SHeader text = elf.sectionWithName(".text");
273 if(text == null) throw new CompilationException("No .text segment");
274 findBranchesInText(text.addr,new DataInputStream(text.getInputStream()),text.size,jumpableAddresses);
276 findBranchesInSymtab(symtab,jumpableAddresses);
278 for(int i=0;i<elf.sheaders.length;i++) {
279 ELF.SHeader sheader = elf.sheaders[i];
280 String name = sheader.name;
281 // if this section doesn't get loaded into our address space don't worry about it
282 if(sheader.addr == 0x0) continue;
283 if(name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors"))
284 findBranchesInData(new DataInputStream(sheader.getInputStream()),sheader.size,jumpableAddresses,text.addr,text.addr+text.size);
288 // Generate main body functions (run_XXXX() blocks, _data[] arrays, etc)
291 for(int i=0;i<elf.sheaders.length;i++) {
292 ELF.SHeader sheader = elf.sheaders[i];
293 String name = sheader.name;
294 // if this section doesn't get loaded into our address space don't worry about it
295 if(sheader.addr == 0x0) continue;
297 highestAddr = Math.max(highestAddr, sheader.addr + sheader.size);
299 if(name.equals(".text"))
300 emitText(sheader.addr, new DataInputStream(sheader.getInputStream()),sheader.size,jumpableAddresses);
301 else if(name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors"))
302 emitData(sheader.addr, new DataInputStream(sheader.getInputStream()), sheader.size,name.equals(".rodata"));
303 else if(name.equals(".bss") || name.equals(".sbss"))
304 emitBSS(sheader.addr,sheader.size);
306 throw new CompilationException("Unknown segment: " + name);
313 // Trampoline (dispatch calls to the appropriate run_XXX() methods
314 p("private final void trampoline() throws ExecutionException {");
316 p("while(state == RUNNING) {");
318 p("switch(pc>>>" + methodShift+ ") {");
321 p("default: throw new ExecutionException(\"invalid address 0x\" + Long.toString(this.pc&0xffffffffL,16));");
328 p("public " + className + "() {");
330 p("super(" + pageSize + "," + totalPages + "," + (fastMem?"false":"true") + ");");
331 p("entryPoint = " + toHex(elf.header.entry) + ";");
332 if(userInfo != null) {
333 p("userInfoBase=" + toHex(userInfo.addr) + ";");
334 p("userInfoSize=" + userInfo.size + ";");
336 p("gp = " + toHex(gp.addr) + ";");
338 p("brkAddr = " + toHex((highestAddr+4095)&~4095) + ";");
340 p("brkAddr = " + toHex((highestAddr+pageSize-1)&~(pageSize-1)) + ";");
342 p("state = INITIALIZED;");
348 p("public static void main(String[] javaArgs) throws Exception {");
350 p("String[] args = new String[javaArgs.length+1];");
351 p("System.arraycopy(javaArgs,0,args,1,javaArgs.length);");
352 p("args[0] = \"" + className + "\";");
353 p(className + " me = new " + className + "();");
354 p("int status = me.run(args);");
355 p("System.err.println(\"Exit status: \" + status);");
356 if(runtimeStats) p("me.printStats();");
357 p("System.exit(status);");
362 // Runtime abstract methods
363 p("protected void _execute() throws ExecutionException { trampoline(); }");
366 p("protected void setCPUState(CPUState state) {");
368 for(int i=1;i<32;i++) p("r" + i + "=state.r[" + i + "];");
369 for(int i=0;i<32;i++) p("f" + i + "=state.f[" + i + "];");
370 p("hi=state.hi; lo=state.lo; fcsr=state.fcsr;");
374 p("protected CPUState getCPUState() {");
376 p("CPUState state = new CPUState();");
377 for(int i=1;i<32;i++) p("state.r[" + i + "]=r" + i+ ";");
378 for(int i=0;i<32;i++) p("state.f[" + i + "]=f" + i +";");
379 p("state.hi=hi; state.lo=lo; state.fcsr=fcsr;");
387 p("private static " + hashClass + " symbols = new " + hashClass + "();");
390 ELF.Symbol[] symbols = symtab.symbols;
391 for(int i=0;i<symbols.length;i++) {
392 ELF.Symbol s = symbols[i];
393 if(s.type == ELF.Symbol.STT_FUNC && s.binding == ELF.Symbol.STB_GLOBAL && (s.name.equals("_call_helper") || !s.name.startsWith("_")))
394 p("symbols.put(\"" + s.name + "\",new Integer(" + toHex(s.addr) + "));");
398 p("public int lookupSymbol(String symbol) { Integer i = (Integer) symbols.get(symbol); return i==null ? -1 : i.intValue(); }");
404 p("private HashMap counters = new HashMap();");
405 p("private void inc(String k) { Long i = (Long)counters.get(k); counters.put(k,new Long(i==null ? 1 : i.longValue() + 1)); }");
406 p("private void printStats() {");
407 p(" Iterator i = new TreeSet(counters.keySet()).iterator();");
408 p(" while(i.hasNext()) { Object o = i.next(); System.err.println(\"\" + o + \": \" + counters.get(o)); }");
413 // Null pointer check helper function
414 if(nullPointerCheck) {
415 p("private static void nullPointerCheck(int addr) throws ExecutionException {");
417 if(onePage) p("if(addr < 65536)");
418 else p(" if((addr>>>" + pageShift + ") < 16) ");
420 p("throw new ExecutionException(\"Attempted to dereference a null pointer (0x\" + Long.toString(addr&0xffffffffL,16) + \")\");");
429 private int startOfMethod = 0;
430 private int endOfMethod = 0;
432 private void startMethod(int addr) {
433 addr &= ~(maxBytesPerMethod-1);
434 startOfMethod = addr;
435 endOfMethod = addr + maxBytesPerMethod;
436 String methodName = "run_" + Long.toString(addr & 0xffffffffL, 16);
437 runs.append(indents[4] + "case " + toHex(addr>>>methodShift) + ": " + methodName + "(); break; \n");
438 p("private final void " + methodName + "() throws ExecutionException { /"+"* " + toHex(addr) + " - " + toHex(endOfMethod) + " *" + "/");
441 for(int i=0;i<freqRegs.length;i++)
442 p("int " + freqRegs[i] + " = this." + freqRegs[i] + ";");
449 private void endMethod() { endMethod(endOfMethod); }
450 private void endMethod(int lastAddr) {
451 if(startOfMethod == 0) return;
452 // This isn't strictly necessary; its just here to work around unreachable code errors
453 p("case " + toHex(lastAddr) + ":");
455 p("pc=" + constant(lastAddr) + ";");
459 p("default: throw new ExecutionException(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16) + \" (got here from 0x\" + Long.toString(lastPC&0xffffffffL,16)+\")\");");
461 p("default: throw new ExecutionException(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16));");
463 p("}"); // end switch
464 p("/* NOT REACHED */");
468 p("}"); // end method
469 endOfMethod = startOfMethod = 0;
472 private HashMap relativeAddrs = new HashMap();
473 private String constant(int target) {
474 if(target >= 4096 && lessConstants) {
475 int n = target & ~1023;
476 String var = "N_" + toHex8(n);
477 if(relativeAddrs.get(new Integer(n)) == null) {
478 relativeAddrs.put(new Integer(n),Boolean.TRUE);
479 classLevel.append(indents[1] + "private static int " + var + " = " + toHex(n) + ";\n");
481 return "(" + var + " + " + toHex(target - n) + ")";
483 return toHex(target);
487 private void branch(int pc, int target) {
488 if(debugCompiler) p("lastPC = " + toHex(pc) + ";");
489 p("pc=" + constant(target) + ";");
491 p("throw new ExecutionException(\"Branch to addr 0x0\");");
492 else if((pc&methodMask) == (target&methodMask))
494 else if(assumeTailCalls)
495 p("run_" + Long.toString((target&methodMask)&0xffffffffL, 16) + "(); return;");
500 private void leaveMethod() {
501 for(int i=0;i<freqRegs.length;i++)
502 p("this." + freqRegs[i] + " = " + freqRegs[i] + ";");
506 private boolean textDone;
507 private void emitText(int addr, DataInputStream dis, int size, Set jumpableAddresses) throws CompilationException,IOException {
508 if(textDone) throw new CompilationException("Multiple text segments");
511 if((addr&3)!=0 || (size&3)!=0) throw new CompilationException("Section on weird boundaries");
513 int nextInsn = dis.readInt();
514 if(nextInsn == -1) throw new Error("Actually read -1 at " + toHex(addr));
517 for(int i=0;i<count;i++,addr+=4) {
519 nextInsn = (i == count-1) ? -1 : dis.readInt();
520 if(addr >= endOfMethod) { endMethod(); startMethod(addr); }
521 if(jumpableAddresses==null || addr == startOfMethod || jumpableAddresses.contains(new Integer(addr))) {
522 p("case " + toHex(addr) + ":");
524 } else if(unreachable) {
526 } else if(debugCompiler) {
527 p("/" + "* pc = " + toHex(addr) + "*" + "/");
530 emitInstruction(addr,insn,nextInsn);
538 private int initDataCount = 0;
539 private void emitData(int addr, DataInputStream dis, int size, boolean readOnly) throws CompilationException,IOException {
540 if((addr&3)!=0 || (size&3)!=0) throw new CompilationException("Data section on weird boundaries");
541 int last = addr + size;
543 StringBuffer sb = new StringBuffer();
544 int segSize = Math.min(size,28000); // must be a multiple of 56
545 for(int i=0;i<segSize;i+=7) {
547 for(int j=0;j<7;j++) {
549 byte b = (i+j < size) ? dis.readByte() : 1;
552 for(int j=0;j<8;j++) {
553 char c = (char) ((l>>>(7*(7-j)))&0x7f);
554 if(c=='\n') sb.append("\\n");
555 else if(c=='\r') sb.append("\\r");
556 else if(c=='\\') sb.append("\\\\");
557 else if(c=='"') sb.append("\\\"");
558 else if(c >= 32 && c <= 126) sb.append(c);
559 else sb.append("\\" + toOctal3(c));
562 String varname = "_data" + (++initDataCount);
563 p("private static final int[] " + varname + " = decodeData(\"" + sb.toString() + "\"," + toHex(segSize/4) + ");");
564 inits.append(indents[2] + "initPages(" + varname +"," + toHex(addr) + "," + (readOnly?"true":"false") + ");\n");
571 private void emitBSS(int addr, int size) throws CompilationException {
572 if((addr&3)!=0) throw new CompilationException("BSS section on weird boundaries");
575 inits.append(indents[2] + "clearPages(" + toHex(addr) + "," + toHex(count) + ");\n");
579 private void findBranchesInSymtab(ELF.Symtab symtab, Set jumps) {
580 ELF.Symbol[] symbols = symtab.symbols;
582 for(int i=0;i<symbols.length;i++) {
583 ELF.Symbol s = symbols[i];
584 if(s.type == ELF.Symbol.STT_FUNC) {
585 //System.err.println("Adding symbol: " + s.name + " at " + toHex(s.addr));
586 if(jumps.add(new Integer(s.addr))) n++;
589 if(printStats) System.err.println("Found " + n + " additional possible branch targets in Symtab");
592 private void findBranchesInText(int addr, DataInputStream dis, int size, Set jumps) throws IOException {
597 for(int i=0;i<count;i++,pc+=4) {
598 int insn = dis.readInt();
599 int op = (insn >>> 26) & 0xff;
600 int rs = (insn >>> 21) & 0x1f;
601 int rt = (insn >>> 16) & 0x1f;
602 int signedImmediate = (insn << 16) >> 16;
603 int branchTarget = signedImmediate;
604 int jumpTarget = (insn & 0x03ffffff);
605 int subcode = insn & 0x3f;
611 if(jumps.add(new Integer(pc+8))) n++; // return address
614 if(jumps.add(new Integer(pc+4))) n++;
622 if(jumps.add(new Integer(pc+8))) n++; // return address
626 if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
631 if(jumps.add(new Integer(pc+8))) n++; // return address
634 if(jumps.add(new Integer((pc&0xf0000000)|(jumpTarget << 2)))) n++;
640 if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
642 case 17: // FPU Instructions
644 case 8: // BC1F, BC1T
645 if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
652 if(printStats) System.err.println("Found " + n + " additional possible branch targets in Text segment");
655 private void findBranchesInData(DataInputStream dis, int size, Set jumps, int textStart, int textEnd) throws IOException {
658 for(int i=0;i<count;i++) {
659 int word = dis.readInt();
660 if((word&3)==0 && word >= textStart && word < textEnd) {
661 if(jumps.add(new Integer(word))) n++;
665 if(n>0 && printStats) System.err.println("Found " + n + " additional possible branch targets in Data segment");
668 // True if the current code path is unreachable (any instruction with a case statement is reachable)
669 private boolean unreachable = false;
671 private void emitInstruction(int pc, int insn, int nextInsn) throws IOException,CompilationException {
672 if(insn == -1) throw new Error("insn is -1");
674 int op = (insn >>> 26) & 0xff; // bits 26-31
675 int rs = (insn >>> 21) & 0x1f; // bits 21-25
676 int rt = (insn >>> 16) & 0x1f; // bits 16-20
677 int ft = (insn >>> 16) & 0x1f;
678 int rd = (insn >>> 11) & 0x1f; // bits 11-15
679 int fs = (insn >>> 11) & 0x1f;
680 int shamt = (insn >>> 6) & 0x1f; // bits 6-10
681 int fd = (insn >>> 6) & 0x1f;
682 int subcode = insn & 0x3f; // bits 0-5
684 int jumpTarget = (insn & 0x03ffffff); // bits 0-25
685 int unsignedImmediate = insn & 0xffff;
686 int signedImmediate = (insn << 16) >> 16;
687 int branchTarget = signedImmediate;
689 int tmp; // temporaries
691 //if(pc%64==0) p("System.err.println(\"Executing: " + toHex(pc) + "\");");
692 //p("/" + "*" + (pc == -1 ? "Delay Slot" : toHex(pc)) + " *" + "/ ");
693 if(pc==-1) p("/" + "* Next insn is delay slot *" + "/ ");
700 p( "r"+rd+" = r"+rt+" << "+shamt+";");
703 p( "r"+rd+" = r"+rt+" >>> "+shamt+";");
706 p( "r"+rd+" = r"+rt+" >> "+shamt+";");
709 p( "r"+rd+" = r"+rt+" << (r"+rs+"&0x1f);");
712 p( "r"+rd+" = r"+rt+" >>> (r"+rs+"&0x1f);");
715 p( "r"+rd+" = r"+rt+" >> (r"+rs+"&0x1f);");
718 if(pc == -1) throw new Error("pc modifying insn in delay slot");
719 emitInstruction(-1,nextInsn,-1);
720 if(debugCompiler) p("lastPC = " + toHex(pc) + ";");
721 p("pc=r" + rs + ";");
726 if(pc == -1) throw new Error("pc modifying insn in delay slot");
727 emitInstruction(-1,nextInsn,-1);
728 if(debugCompiler) p("lastPC = " + toHex(pc) + ";");
729 p("pc=r" + rs + ";");
730 p("r" + RA + "=" + constant(pc+8 /*skip this insn and delay slot*/) + ";");
735 p("pc = " + toHex(pc) + ";");
736 p( "r"+V0+" = syscall(r"+V0+",r"+A0+",r"+A1+",r"+A2+",r"+A3+");");
737 p("if (state != RUNNING) {");
739 p("pc = " + toHex(pc+4) + ";");
745 p( "throw new ExecutionException(\"Break\");");
760 p( "{ long hilo = (long)(r"+rs+") * ((long)r"+rt+"); " +
761 "hi = (int) (hilo >>> 32); " +
762 "lo = (int) hilo; }");
765 p( "{ long hilo = (r"+rs+" & 0xffffffffL) * (r"+rt+" & 0xffffffffL); " +
766 "hi = (int) (hilo >>> 32); " +
767 "lo = (int) hilo; } ");
770 p( "hi = r"+rs+"%r"+rt+"; lo = r"+rs+"/r"+rt+";");
773 p( "hi = (int)((r"+rs+" & 0xffffffffL) % (r"+rt+" & 0xffffffffL)); " +
774 "lo = (int)((r"+rs+" & 0xffffffffL) / (r"+rt+" & 0xffffffffL));");
777 throw new CompilationException("ADD (add with oveflow trap) not suported");
778 /*This must trap on overflow
779 p( "r"+rd+" = r"+rs+" + r"+rt+";");
782 p( "r"+rd+" = r"+rs+" + r"+rt+";");
785 throw new CompilationException("SUB (add with oveflow trap) not suported");
786 /*This must trap on overflow
787 p( "r"+rd+" = r"+rs+" - r"+rt+";");
790 p( "r"+rd+" = r"+rs+" - r"+rt+";");
793 p( "r"+rd+" = r"+rs+" & r"+rt+";");
796 p( "r"+rd+" = r"+rs+" | r"+rt+";");
799 p( "r"+rd+" = r"+rs+" ^ r"+rt+";");
802 p( "r"+rd+" = ~(r"+rs+" | r"+rt+");");
805 p( "r"+rd+" = r"+rs+" < r"+rt+" ? 1 : 0;");
808 p( "r"+rd+" = ((r"+rs+" & 0xffffffffL) < (r"+rt+" & 0xffffffffL)) ? 1 : 0;");
811 throw new RuntimeException("Illegal instruction 0/" + subcode);
818 if(pc == -1) throw new Error("pc modifying insn in delay slot");
819 p("if(r" + rs + " < 0) {");
821 emitInstruction(-1,nextInsn,-1);
822 branch(pc,pc+branchTarget*4+4);
827 if(pc == -1) throw new Error("pc modifying insn in delay slot");
828 p("if(r" + rs + " >= 0) {");
830 emitInstruction(-1,nextInsn,-1);
831 branch(pc,pc+branchTarget*4+4);
836 if(pc == -1) throw new Error("pc modifying insn in delay slot");
837 p("if(r" + rs + " < 0) {");
839 emitInstruction(-1,nextInsn,-1);
840 p("r" + RA + "=" + constant(pc+8 /*skip this insn and delay slot*/) + ";");
841 branch(pc,pc+branchTarget*4+4);
846 if(pc == -1) throw new Error("pc modifying insn in delay slot");
847 p("if(r" + rs + " >= 0) {");
849 emitInstruction(-1,nextInsn,-1);
850 p("r" + RA + "=" + constant(pc+8 /*skip this insn and delay slot*/) + ";");
851 branch(pc,pc+branchTarget*4+4);
856 throw new RuntimeException("Illegal Instruction 1/" + rt);
861 if(pc == -1) throw new Error("pc modifying insn in delay slot");
862 emitInstruction(-1,nextInsn,-1);
863 branch(pc,(pc&0xf0000000)|(jumpTarget << 2));
868 if(pc == -1) throw new Error("pc modifying insn in delay slot");
869 int target = (pc&0xf0000000)|(jumpTarget << 2);
870 emitInstruction(-1,nextInsn,-1);
871 if(optimizedMemcpy && (target == memcpy || target == memset)) {
873 p("memcpy(r4,r5,r6);");
874 else if(target == memset)
875 p("memset(r4,r5,r6);");
879 p("r" + RA + "=" + constant(pc+8 /*skip this insn and delay slot*/) + ";");
886 if(pc == -1) throw new Error("pc modifying insn in delay slot");
887 p("if(r" + rs + " == r" + rt + ") {");
889 emitInstruction(-1,nextInsn,-1);
890 branch(pc,pc+branchTarget*4+4);
895 if(pc == -1) throw new Error("pc modifying insn in delay slot");
896 p("if(r" + rs + " != r" + rt + ") {");
898 emitInstruction(-1,nextInsn,-1);
899 branch(pc,pc+branchTarget*4+4);
904 if(pc == -1) throw new Error("pc modifying insn in delay slot");
905 p("if(r" + rs + " <= 0) {");
907 emitInstruction(-1,nextInsn,-1);
908 branch(pc,pc+branchTarget*4+4);
913 if(pc == -1) throw new Error("pc modifying insn in delay slot");
914 p("if(r" + rs + " > 0) {");
916 emitInstruction(-1,nextInsn,-1);
917 branch(pc,pc+branchTarget*4+4);
922 p( "r"+rt+" = r"+rs+" + "+signedImmediate +";");
925 p( "r"+rt+" = r"+rs+" + "+signedImmediate+";");
928 p( "r"+rt+" = r"+rs+" < "+signedImmediate+" ? 1 : 0;");
931 p( "r"+rt+" = (r"+rs+"&0xffffffffL) < ("+unsignedImmediate+"&0xffffffffL) ? 1 : 0;");
934 p( "r"+rt+" = r"+rs+" & "+unsignedImmediate+";");
937 p( "r"+rt+" = r"+rs+" | "+unsignedImmediate+";");
940 p( "r"+rt+" = r"+rs+" ^ "+unsignedImmediate+";");
943 p( "r"+rt+" = "+unsignedImmediate+" << 16;");
946 throw new CompilationException("TLB/Exception support not implemented");
950 p( "r"+rt+" = f"+rd+";");
953 if(fs != 31) throw new CompilationException("FCR " + fs + " unavailable");
954 p( "r"+rt+" = fcsr;");
957 p( "f"+rd+" = r"+rt+";");
960 if(fs != 31) throw new CompilationException("FCR " + fs + " unavailable");
961 p( "fcsr = r"+rt+";");
963 case 8: {// BC1F, BC1T
965 p("if(((fcsr&0x800000)!=0) == (" + tmp + "!=0)) {");
967 emitInstruction(-1,nextInsn,-1);
968 branch(pc,pc+branchTarget*4+4);
976 p(setFloat(fd,getFloat(fs)+"+"+getFloat(ft)));
979 p(setFloat(fd,getFloat(fs)+"-"+getFloat(ft)));
982 p(setFloat(fd,getFloat(fs)+"*"+getFloat(ft)));
985 p(setFloat(fd,getFloat(fs)+"/"+getFloat(ft)));
988 p(setFloat(fd,"Math.abs("+getFloat(fs)+")"));
991 p("f"+fd+" = f"+fs+"; // MOV.S");
994 p(setFloat(fd,"-"+getFloat(fs)));
997 p(setDouble(fd,"(float)"+getFloat(fs)));
1000 p("switch(fcsr & 3) {");
1002 p("case 0: f"+fd+" = (int)Math.floor("+getFloat(fs)+"+0.5); break; // Round to nearest");
1003 p("case 1: f"+fd+" = (int)"+getFloat(fs)+"; break; // Round towards zero");
1004 p("case 2: f"+fd+" = (int)Math.ceil("+getFloat(fs)+"); break; // Round towards plus infinity");
1005 p("case 3: f"+fd+" = (int)Math.floor("+getFloat(fs)+"); break; // Round towards minus infinity");
1010 p("fcsr = (fcsr&~0x800000) | (("+getFloat(fs)+"=="+getFloat(ft)+") ? 0x800000 : 0x000000);");
1013 p("fcsr = (fcsr&~0x800000) | (("+getFloat(fs)+"<"+getFloat(ft)+") ? 0x800000 : 0x000000);");
1016 p("fcsr = (fcsr&~0x800000) | (("+getFloat(fs)+"<="+getFloat(ft)+") ? 0x800000 : 0x000000);");
1018 default: throw new CompilationException("Invalid Instruction 17/" + rs + "/" + subcode);
1022 case 17: { // Double
1025 p(setDouble(fd,getDouble(fs)+"+"+getDouble(ft)));
1028 p(setDouble(fd,getDouble(fs)+"-"+getDouble(ft)));
1031 p(setDouble(fd,getDouble(fs)+"*"+getDouble(ft)));
1034 p(setDouble(fd,getDouble(fs)+"/"+getDouble(ft)));
1037 p(setDouble(fd,"Math.abs("+getDouble(fs)+")"));
1040 p("f"+fd+" = f"+fs+";");
1041 p("f"+(fd+1)+" = f"+(fs+1)+";");
1044 p(setDouble(fd,"-"+getDouble(fs)));
1047 p(setFloat(fd,"(float)"+getDouble(fs)));
1050 p("switch(fcsr & 3) {");
1052 p("case 0: f"+fd+" = (int)Math.floor("+getDouble(fs)+"+0.5); break; // Round to nearest");
1053 p("case 1: f"+fd+" = (int)"+getDouble(fs)+"; break; // Round towards zero");
1054 p("case 2: f"+fd+" = (int)Math.ceil("+getDouble(fs)+"); break; // Round towards plus infinity");
1055 p("case 3: f"+fd+" = (int)Math.floor("+getDouble(fs)+"); break; // Round towards minus infinity");
1060 p("fcsr = (fcsr&~0x800000) | (("+getDouble(fs)+"=="+getDouble(ft)+") ? 0x800000 : 0x000000);");
1063 p("fcsr = (fcsr&~0x800000) | (("+getDouble(fs)+"<"+getDouble(ft)+") ? 0x800000 : 0x000000);");
1066 p("fcsr = (fcsr&~0x800000) | (("+getDouble(fs)+"<="+getDouble(ft)+") ? 0x800000 : 0x000000);");
1068 default: throw new CompilationException("Invalid Instruction 17/" + rs + "/" + subcode);
1072 case 20: { // Integer
1076 p(setFloat(fd,"((float)f"+fs+")"));
1079 p(setDouble(fd,"((double)f"+fs+")"));
1081 default: throw new CompilationException("Invalid Instruction 17/" + rs + "/" + subcode);
1086 throw new CompilationException("Invalid Instruction 17/" + rs);
1091 throw new CompilationException("coprocessor 2 and 3 instructions not available");
1093 if(runtimeStats) p("inc(\"LB\");");
1094 p("addr=r" + rs +"+"+signedImmediate + ";");
1095 memRead("addr&~3","tmp");
1096 p("tmp = (tmp>>>(((~addr)&3)<<3)) & 0xff;");
1097 p("if((tmp&0x80)!=0) tmp |= 0xffffff00; /* sign extend */");
1098 p("r"+rt+" = tmp;");
1102 if(runtimeStats) p("inc(\"LH\");");
1103 p("addr=r" + rs +"+"+signedImmediate + ";");
1104 memRead("addr&~3","tmp");
1105 p("tmp = (tmp>>>(((~addr)&2)<<3)) & 0xffff;");
1106 p("if((tmp&0x8000)!=0) tmp |= 0xffff0000; /* sign extend */");
1107 p("r"+rt+" = tmp;");
1111 p("addr=r" + rs +"+"+signedImmediate + ";");
1112 memRead("addr&~3","tmp");
1113 p("switch(addr&3) {");
1115 p("case 0: r"+rt+" = (r"+rt+"&0x00000000)|(tmp<< 0); break;");
1116 p("case 1: r"+rt+" = (r"+rt+"&0x000000ff)|(tmp<< 8); break;");
1117 p("case 2: r"+rt+" = (r"+rt+"&0x0000ffff)|(tmp<<16); break;");
1118 p("case 3: r"+rt+" = (r"+rt+"&0x00ffffff)|(tmp<<24); break;");
1124 if(runtimeStats) p("inc(\"LW\");");
1125 memRead("r" + rs +"+"+signedImmediate,"r"+rt);
1128 p("addr=r" + rs +"+"+signedImmediate + ";");
1129 memRead("addr&~3","tmp");
1130 p("tmp = (tmp>>>(((~addr)&3)<<3)) & 0xff;");
1131 p("r"+rt+" = tmp;");
1135 p("addr=r" + rs +"+"+signedImmediate + ";");
1136 memRead("addr&~3","tmp");
1137 p("tmp = (tmp>>>(((~addr)&2)<<3)) & 0xffff;");
1138 p("r"+rt+" = tmp;");
1142 p("addr=r" + rs +"+"+signedImmediate + ";");
1143 memRead("addr&~3","tmp");
1144 p("switch(addr&3) {");
1146 p("case 0: r"+rt+" = (r"+rt+"&0xffffff00)|(tmp>>>24); break;");
1147 p("case 1: r"+rt+" = (r"+rt+"&0xffff0000)|(tmp>>>16); break;");
1148 p("case 2: r"+rt+" = (r"+rt+"&0xff000000)|(tmp>>> 8); break;");
1149 p("case 3: r"+rt+" = (r"+rt+"&0x00000000)|(tmp>>> 0); break;");
1155 if(runtimeStats) p("inc(\"SB\");");
1156 p("addr=r" + rs +"+"+signedImmediate + ";");
1157 memRead("addr&~3","tmp");
1158 p("tmp = (tmp&~(0xff000000>>>((addr&3)<<3)))|((r"+rt+"&0xff)<<(((~addr)&3)<<3));");
1159 memWrite("addr&~3","tmp");
1163 if(runtimeStats) p("inc(\"SH\");");
1164 p("addr=r" + rs +"+"+signedImmediate + ";");
1165 memRead("addr&~3","tmp");
1166 p("tmp = (tmp&(0xffff<<((addr&2)<<3)))|((r" + rt + "&0xffff)<<(((~addr)&2)<<3));");
1167 memWrite("addr&~3","tmp");
1172 p("addr=r" + rs +"+"+signedImmediate + ";");
1173 memRead("addr&~3","tmp");
1174 p("switch(addr&3) {");
1176 p("case 0: tmp=(tmp&0x00000000)|(r"+rt+">>> 0); break;");
1177 p("case 1: tmp=(tmp&0xff000000)|(r"+rt+">>> 8); break;");
1178 p("case 2: tmp=(tmp&0xffff0000)|(r"+rt+">>>16); break;");
1179 p("case 3: tmp=(tmp&0xffffff00)|(r"+rt+">>>24); break;");
1182 memWrite("addr&~3","tmp");
1186 if(runtimeStats) p("inc(\"SW\");");
1187 memWrite("r"+rs+"+"+signedImmediate,"r" + rt);
1191 p("addr=r" + rs +"+"+signedImmediate + ";");
1192 memRead("addr&~3","tmp");
1193 p("switch(addr&3) {");
1195 p("case 0: tmp=(tmp&0x00ffffff)|(r"+rt+"<<24); break;");
1196 p("case 1: tmp=(tmp&0x0000ffff)|(r"+rt+"<<16); break;");
1197 p("case 2: tmp=(tmp&0x000000ff)|(r"+rt+"<< 8); break;");
1198 p("case 3: tmp=(tmp&0x00000000)|(r"+rt+"<< 0); break;");
1201 memWrite("addr&~3","tmp");
1205 memRead("r"+rs+"+"+signedImmediate,"f"+rt);
1208 memWrite("r"+rs+"+"+signedImmediate,"f"+rt);
1211 throw new CompilationException("Invalid Instruction: " + op + " at " + toHex(pc));
1215 // Helper functions for emitText
1216 private void memWrite(String addr, String target) {
1217 if(nullPointerCheck) p("nullPointerCheck(" + addr + ");");
1219 p("page[(" + addr + ")>>>2] = " + target + ";");
1221 p("writePages[("+addr+")>>>"+pageShift+"][(("+addr+")>>>2)&"+toHex((pageSize>>2)-1)+"] = " + target + ";");
1223 p("memWrite(" + addr + "," + target + ");");
1225 private void memRead(String addr, String target) {
1226 if(nullPointerCheck) p("nullPointerCheck(" + addr + ");");
1228 p(target + "= page[(" + addr + ")>>>2];");
1230 p(target + " = readPages[("+addr+")>>>"+pageShift+"][(("+addr+")>>>2)&"+toHex((pageSize>>2)-1)+"];");
1232 p(target + " = memRead(" + addr + ");");
1234 private static String getFloat(int r) { return "(Float.intBitsToFloat(f"+r+"))"; }
1235 private static String getDouble(int r) {
1236 return "(Double.longBitsToDouble(((f"+(r+1)+"&0xffffffffL) << 32) | (f"+r+"&0xffffffffL)))";
1238 private static String setFloat(int r, String expr) { return "f"+r+"=Float.floatToRawIntBits("+expr+");"; }
1239 private static String setDouble(int r, String expr) {
1240 return "{ long l = Double.doubleToLongBits("+expr+"); "+
1241 "f"+(r+1)+" = (int)(l >>> 32); f"+r+" = (int)l; }";
1244 // Helper functions for pretty output
1245 private final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
1246 private final static String toHex8(int n) {
1247 String s = Long.toString(n & 0xffffffffL, 16);
1248 StringBuffer sb = new StringBuffer("0x");
1249 for(int i=8-s.length();i>0;i--) sb.append('0');
1251 return sb.toString();
1254 private final static String toOctal3(int n) {
1255 char[] buf = new char[3];
1256 for(int i=2;i>=0;i--) {
1257 buf[i] = (char) ('0' + (n & 7));
1260 return new String(buf);
1264 private class Option {
1265 private java.lang.reflect.Field field;
1266 public Option(String name) throws NoSuchFieldException { field = name==null ? null : Compiler.class.getDeclaredField(name); }
1267 public void set(Object val) {
1268 if(field == null) return;
1270 field.setAccessible(true);
1271 field.set(Compiler.this,val);
1272 } catch(IllegalAccessException e) {
1273 System.err.println(e);
1276 public Object get() {
1277 if(field == null) return null;
1279 field.setAccessible(true);
1280 return field.get(Compiler.this);
1281 } catch(IllegalAccessException e) {
1282 System.err.println(e); return null;
1285 public Class getType() { return field == null ? null : field.getType(); }
1288 private static String[] options = {
1289 "fastMem", "Enable fast memory access - RuntimeExceptions will be thrown on faults",
1290 "nullPointerCheck", "Enables checking at runtime for null pointer accessses (slows things down a bit, only applicable with fastMem)",
1291 "maxInsnPerMethod", "Maximum number of MIPS instructions per java method (128 is optimal with Hotspot)",
1292 "pruneCases", "Remove unnecessary case 0xAABCCDD blocks from methods - may break some weird code",
1293 "assumeTailCalls", "Assume the JIT optimizes tail calls",
1294 "optimizedMemcpy", "Use an optimized java version of memcpy where possible",
1295 "debugCompiler", "Output information in the generated code for debugging the compiler - will slow down generated code significantly",
1296 "printStats", "Output some useful statistics about the compilation",
1297 "runtimeStats", "Keep track of some statistics at runtime in the generated code - will slow down generated code significantly",
1298 "supportCall", "Keep a stripped down version of the symbol table in the generated code to support the call() method",
1299 "runtimeClass", "Full classname of the Runtime class (default: Runtime) - use this is you put Runtime in a package",
1300 "hashClass", "Full classname of a Hashtable class (default: java.util.HashMap) - this must support get() and put()",
1301 "unixRuntime", "Use the UnixRuntime (has support for fork, wai, du, pipe, etc)",
1302 "pageSize", "The page size (must be a power of two)",
1303 "totalPages", "Total number of pages (total mem = pageSize*totalPages, must be a power of two)",
1304 "onePage", "One page hack (FIXME: document this better)",
1305 "lessConstants", "Use less constants at the cost of speed (FIXME: document this better)"
1308 private Option getOption(String name) {
1309 name = name.toLowerCase();
1311 for(int i=0;i<options.length;i+=2)
1312 if(options[i].toLowerCase().equals(name))
1313 return new Option(options[i]);
1315 } catch(NoSuchFieldException e) {
1320 private void parseOptions(String opts) {
1321 if(opts == null || opts.length() == 0) return;
1322 StringTokenizer st = new StringTokenizer(opts,",");
1323 while(st.hasMoreElements()) {
1324 String tok = st.nextToken();
1327 if(tok.indexOf("=") != -1) {
1328 key = tok.substring(0,tok.indexOf("="));
1329 val = tok.substring(tok.indexOf("=")+1);
1330 } else if(tok.startsWith("no")) {
1331 key = tok.substring(2);
1337 Option opt = getOption(key);
1339 System.err.println("WARNING: No such option: " + key);
1343 if(opt.getType() == String.class)
1345 else if(opt.getType() == Integer.TYPE)
1347 opt.set(parseInt(val));
1348 } catch(NumberFormatException e) {
1349 System.err.println("WARNING: " + val + " is not an integer");
1351 else if(opt.getType() == Boolean.TYPE)
1352 opt.set(new Boolean(val.toLowerCase().equals("true")||val.toLowerCase().equals("yes")));
1354 throw new Error("Unknown type: " + opt.getType());
1358 private static Integer parseInt(String s) {
1360 s = s.toLowerCase();
1361 if(!s.startsWith("0x") && s.endsWith("m")) { s = s.substring(0,s.length()-1); mult = 1024*1024; }
1362 else if(!s.startsWith("0x") && s.endsWith("k")) { s = s.substring(0,s.length()-1); mult = 1024; }
1364 if(s.length() > 2 && s.startsWith("0x")) n = Integer.parseInt(s.substring(2),16);
1365 else n = Integer.parseInt(s);
1366 return new Integer(n*mult);
1369 private static String wrapAndIndent(String s, int firstindent, int indent, int width) {
1370 StringTokenizer st = new StringTokenizer(s," ");
1371 StringBuffer sb = new StringBuffer();
1372 for(int i=0;i<firstindent;i++)
1375 while(st.hasMoreTokens()) {
1376 String tok = st.nextToken();
1377 if(tok.length() + sofar + 1 > width && sofar > 0) {
1379 for(int i=0;i<indent;i++) sb.append(' ');
1381 } else if(sofar > 0) {
1386 sofar += tok.length();
1389 return sb.toString();
1392 // This ugliness is to work around a gcj static linking bug (Bug 12908)
1393 // The best solution is to force gnu.java.locale.Calendar to be linked in but this'll do
1394 private static String dateTime() {
1396 return new Date().toString();
1397 } catch(RuntimeException e) {