import java.util.*;
import java.io.*;
-// FIXME: lb/sb/sh/lh need not be word aligned
-// FIXME: memory accesses aren't handling sign-extending properly
-// FIXME: probably have to implement nonaligned access
-// FIXME: implement malloc()
-
-// FIXME: implement an ELF parser based on RandomAccessFile
-
// FEATURE: progress indicator
-// FEATURE: support n32 abi (passes more arguments in registers)
-// FEATURE: trap on arithmetic overflows
-// FEATURE: FPU
-// FEATURE: we always know the value of the pc register; we should emit it as a literal when it appears in computations
-// FEATURE: emit bytecode rather than .java code (for on-the-fly classloading without javac present in "real" JVMs)
-
-/** reads a fully linked MIPS ELF binary image on stdin; writes a .java file on stdout */
-public class Compiler {
-
- static String runs = "";
- static int last_emit = -1;
- static DataInputStream dis;
- public static void main(String[] s) throws IOException {
+// FEATURE: emit bytecode rather than .java code (for on-the-fly classloading without javac present in "real" JVMs
+
+public class Compiler implements Registers {
+
+ private static StringBuffer runs = new StringBuffer();
+ private static StringBuffer inits = new StringBuffer();
+
+ private static PrintStream out = System.out;
+
+ private static int indent;
+ private static String indents[] = new String[16];
+ static { String s=""; for(int i=0;i<indents.length;i++,s=s+" ") indents[i] = s; }
+ private static final void p() { out.println(); }
+ private static final void p(String s) { out.println(indents[indent] + s); }
+
+ // FEATURE: This should probably provide some detail about where the excpetion happened (address, line numbers, etc)
+ private static class CompilationException extends Exception { public CompilationException(String s) { super(s); } }
+
+ // Set this to true to enable fast memory access
+ // When this is enabled a Java RuntimeException will be thrown when a page fault occures. When it is disabled
+ // a FaultException will be throw which is easier to catch and deal with, however. as the name implies, this is slower
+ private static boolean fastMem = true;
+
+ // This MUST be a power of two. If it is not horrible things will happen
+ // NOTE: This value can be much higher without breaking the classfile
+ // specs (around 1024) but Hotstop seems to do much better with smaller
+ // methods.
+ private static int MAX_INSN_PER_METHOD = 32;
+
+ // Don't touch this
+ private static int MAX_BYTES_PER_METHOD = MAX_INSN_PER_METHOD*4;
+ private static int METHOD_MASK = ~(MAX_BYTES_PER_METHOD-1);
+
+ // Store frequently used registers in local variables
+ // Unfortunately this doesn't seem to speed things up much
+ private static String[] freqRegs = { /*"r2", "r29", "r3", "r16", "r5", "r17", "r6", "r18", "r4", "r31", "r19"*/ };
+
+ // True to try to determine which case statement are needed and only include them
+ private static boolean pruneCases = true;
+
+ // True to insert some code in the output to help diagnore compiler problems
+ private final static boolean debugCompiler = false;
+
+ // True to print various statistics about the compilation
+ private final static boolean printStats = true;
+
+ public static void main(String[] s) throws Exception {
if (s.length != 2) {
System.err.println("usage: java " + Compiler.class.getName() + " <classname> <binary.mips>");
- System.exit(-1);
+ System.exit(1);
}
String packageName = null;
packageName = s[0].substring(0, s[0].lastIndexOf('.'));
className = s[0].substring(s[0].lastIndexOf('.') + 1);
}
-
- System.out.println(prefix + "// This file was generated by MipsToJava");
- if (packageName != null) System.out.println(prefix + "package " + packageName + ";");
- System.out.println(prefix + "public class " + className + " {");
- System.out.println(prefix + "");
- System.out.println(prefix + " public " + className + "() { }");
- System.out.println(prefix + "");
- System.out.println(prefix + " // memory");
- System.out.println(prefix + " public int mem_read[][] = new int[65535][];");
- System.out.println(prefix + "");
- System.out.println(prefix + " // same as mem_read unless a page is write-protected");
- System.out.println(prefix + " public int mem_write[][] = new int[65535][];");
- System.out.println(prefix + "");
- System.out.println(prefix + " // program counter");
- System.out.println(prefix + " int pc = 0;");
- System.out.println(prefix + "");
- System.out.println(prefix + " // temporary");
- System.out.println(prefix + " int tmp = 0;");
- System.out.println(prefix + "");
- System.out.println(prefix + " // MIPS multiply/divide subsystem; 64-bit result");
- System.out.println(prefix + " long hilo = 0;");
- System.out.println(prefix + "");
- System.out.println(prefix + " // General Purpose registers");
- System.out.println(prefix + " final int r0 = 0;");
- System.out.println(prefix + " int r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0,");
- System.out.println(prefix + " r8 = 0, r9 = 0, r10 = 0, r11 = 0, r12 = 0, r13 = 0, r14 = 0, r15 = 0,");
- System.out.println(prefix + " r16 = 0, r17 = 0, r18 = 0, r19 = 0, r20 = 0, r21 = 0, r22 = 0, r23 = 0,");
- System.out.println(prefix + " r24 = 0, r25 = 0, r26 = 0, r27 = 0, r28 = 0, r29 = 0, r30 = 0, r31 = 0;");
- System.out.println(prefix + "");
-
- dis = new DataInputStream(new FileInputStream(s[1]));
-
- // read the ELF header
- if (dis.readByte() != 0x7f || dis.readByte() != 'E' || dis.readByte() != 'L' || dis.readByte() != 'F')
- throw new RuntimeException("input file is not an ELF binary");
- dis.skip(12);
-
- if (dis.readShort() != 2)
- throw new RuntimeException("binary is not a linked executable");
-
- if (dis.readShort() != 8)
- throw new RuntimeException("binary is not a MIPS R3000 binary");
-
- dis.skip(4);
- int entry_point = dis.readInt();
- String entry_point_string = Long.toString(entry_point & 0xffffffffL, 16);
-
- int ph_offset = dis.readInt();
- int sh_offset = dis.readInt();
- if (ph_offset == 0) throw new RuntimeException("binary is not statically linked");
- dis.skip(4);
- dis.skip(2);
-
- int ph_entry_size = dis.readShort();
- int ph_num_entries = dis.readShort();
- int sh_entry_size = dis.readShort();
- int sh_num_entries = dis.readShort();
- int string_table_section_number = dis.readShort();
-
- int skipamount = sh_offset - (4 + 12 + 2 + 2 + 4 + 4 + 4 + 4 + 4 + 2 + 2 + 2 + 2 + 2 + 2);
- while (skipamount>0) skipamount -= (int)dis.skip(skipamount);
-
- int[] p_type = new int[sh_num_entries];
- int[] p_ofs = new int[sh_num_entries];
- int[] addr = new int[sh_num_entries];
- int[] p_size = new int[sh_num_entries];
- int[] p_name = new int[sh_num_entries];
- for(int i=0; i<sh_num_entries; i++) {
- p_name[i] = dis.readInt();
- p_type[i] = dis.readInt();
- dis.skip(4);
- addr[i] = dis.readInt();
- p_ofs[i] = dis.readInt();
- p_size[i] = dis.readInt();
- dis.skip(sh_entry_size - 4 * 6);
- }
-
- dis.close();
- dis = new DataInputStream(new FileInputStream(s[1]));
-
- int seek = p_ofs[string_table_section_number];
- while (seek > 0) seek -= dis.skip(seek);
- char[] stringTable = new char[p_size[string_table_section_number]];
- for(int i=0; i<p_size[string_table_section_number]; i++)
- stringTable[i] = (char)dis.readByte();
-
- dis.close();
- dis = new DataInputStream(new FileInputStream(s[1]));
-
- int pos = 0;
- for(int i=0; i<sh_num_entries; i++) {
-
- String name = "";
- for(int j=p_name[i]; j<stringTable.length && stringTable[j] != 0; j++) name += stringTable[j];
- System.out.println();
- System.out.println(prefix + "// section \"" + name +
- "\" #" + i + "; file offset 0x" + Long.toString(p_ofs[i] & 0xffffffffL, 16) +
- ", vma 0x" + Long.toString(addr[i] & 0xffffffffL, 16) +
- ", size 0x" + Long.toString(p_size[i] & 0xffffffff, 16));
-
- if (name.equals(".sdata")) {
- if (last_emit != -1) {
- System.out.println(prefix + " case 0x" + Long.toString((last_emit & 0xffffffffL) + 0x1000, 16) + ": return;");
- System.out.println(prefix + " default: throw new Error(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16));");
- System.out.println(prefix + " }");
- System.out.println(prefix + " }");
- last_emit = -1;
- }
- pos = 0; dis.close(); dis = new DataInputStream(new FileInputStream(s[1]));
- while(pos < p_ofs[i]) pos += dis.skip(p_ofs[i] - pos);
- String base = "0x" + Long.toString((addr[i] & 0xffff0000L) >> 16, 16);
- System.out.println(prefix + " private void initData() {");
- System.out.println(prefix + " r28 = 0x" + Long.toString((addr[i] - Short.MIN_VALUE - 12) & 0xffffffffL, 16) + ";");
- System.out.println(prefix + " mem_read[" + base + "] = mem_write[" + base + "] = new int[65535];");
- for(long k=addr[i] & 0xffffffffL; k<((addr[i] + p_size[i]) & 0xffffffffL); k += 4)
- System.out.println(prefix + " mem_write[" + base + "][0x" + Long.toString(k & 0xffff, 16) + "] = 0x" +
- Long.toString(dis.readInt() & 0xffffffffL, 16) + ";");
- System.out.println(prefix + " }");
-
- } else if (name.equals(".text") /*|| name.equals(".init")*/) {
- if (pos > p_ofs[i]) { pos = 0; dis.close(); dis = new DataInputStream(new FileInputStream(s[1])); }
- while(pos < p_ofs[i]) pos += dis.skip(p_ofs[i] - pos);
- int remaining = p_size[i];
- for(int ofs = addr[i]; ofs < addr[i] + p_size[i];) {
- String base = Long.toString(ofs & 0xffffff00L, 16);
- int len = Math.min(((ofs + 0x100) & 0xffffff00) - ofs, remaining);
- emit(ofs, len, dis);
- last_emit = ofs;
- remaining -= len;
- ofs += len;
- }
+ ELF elf = new ELF(s[1]);
+ if(elf.header.type != ELF.ELFHeader.ET_EXEC) throw new IOException("Binary is not an executable");
+ if(elf.header.machine != ELF.ELFHeader.EM_MIPS) throw new IOException("Binary is not for the MIPS I Architecture");
+
+ p("// This file was generated by MipsToJava");
+ if (packageName != null) p("package " + packageName + ";");
+ p("public class " + className + " extends Runtime {");
+ p("");
+ p(" // program counter");
+ p(" private int pc = 0;");
+ if(debugCompiler)
+ p(" private int lastPC = 0;");
+ p("");
+ p(" // General Purpose registers");
+ p(" private final static int r0 = 0;");
+ p(" int r1 = 0, r2 = 0, r3 = 0, r4 = 0, r5 = 0, r6 = 0, r7 = 0,");
+ p(" r8 = 0, r9 = 0, r10 = 0, r11 = 0, r12 = 0, r13 = 0, r14 = 0, r15 = 0,");
+ p(" r16 = 0, r17 = 0, r18 = 0, r19 = 0, r20 = 0, r21 = 0, r22 = 0, r23 = 0,");
+ p(" r24 = 0, r25 = 0, r26 = 0, r27 = 0, r28 = 0, r29 = 0, r30 = 0, r31 = 0,");
+ p(" hi = 0, lo = 0;");
+ p(" // FP registers");
+ p(" private int f0 = 0, f1 = 0, f2 = 0, f3 = 0, f4 = 0, f5 = 0, f6 = 0, f7 = 0,");
+ p(" f8 = 0, f9 = 0, f10 = 0, f11 = 0, f12 = 0, f13 = 0, f14 = 0, f15 = 0,");
+ p(" f16 = 0, f17 = 0, f18 = 0, f19 = 0, f20 = 0, f21 = 0, f22 = 0, f23 = 0,");
+ p(" f24 = 0, f25 = 0, f26 = 0, f27 = 0, f28 = 0, f29 = 0, f30 = 0, f31 = 0;");
+ p(" // FP Control Register");
+ p(" private int fcsr = 0;");
+ p("");
+ indent++;
+ // These should all be inlind by javac
+ p("private final void setFC(boolean b) { fcsr = (fcsr&~0x800000) | (b ? 0x800000 : 0x000000); }");
+ p("private final int roundingMode() { return fcsr & 3; /* bits 0-1 */ }");
+ indent--;
+
+ Set jumpableAddresses = null;
+ if(pruneCases) {
+ // Find all possible branches
+ jumpableAddresses = new HashSet();
+
+ jumpableAddresses.add(new Integer(elf.header.entry));
+
+ ELF.SHeader text = elf.sectionWithName(".text");
+ if(text == null) throw new Error("No .text segment");
+ findBranchesInText(text.addr,new DataInputStream(text.getInputStream()),text.size,jumpableAddresses);
+
+ findBranchesInSymtab(elf.getSymtab(),jumpableAddresses);
+
+ for(int i=0;i<elf.sheaders.length;i++) {
+ ELF.SHeader sheader = elf.sheaders[i];
+ String name = sheader.name;
+ // if this section doesn't get loaded into our address space don't worry about it
+ if(sheader.addr == 0x0) continue;
+ if(name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors"))
+ findBranchesInData(new DataInputStream(sheader.getInputStream()),sheader.size,jumpableAddresses,text.addr,text.addr+text.size);
}
}
-
- if (last_emit != -1) {
- System.out.println(prefix + " case 0x" + Long.toString((last_emit & 0xffffffffL) + 0x1000, 16) + ": return;");
- System.out.println(prefix + " default: throw new Error(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16));");
- System.out.println(prefix + " }");
- System.out.println(prefix + " }");
- last_emit = -1;
+
+ // Generate main body functions
+ int highestAddr = 0;
+ indent=1;
+ for(int i=0;i<elf.sheaders.length;i++) {
+ ELF.SHeader sheader = elf.sheaders[i];
+ String name = sheader.name;
+ // if this section doesn't get loaded into our address space don't worry about it
+ if(sheader.addr == 0x0) continue;
+
+ highestAddr = Math.max(highestAddr, sheader.addr + sheader.size);
+
+ if(name.equals(".text")) {
+ emitText(sheader.addr, new DataInputStream(sheader.getInputStream()),sheader.size,jumpableAddresses);
+ endMethod(nextEmitTextAddr);
+ } else if(name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors")) {
+ emitData(sheader.addr, new DataInputStream(sheader.getInputStream()), sheader.size,name.equals(".rodata"));
+ } else if(name.equals(".bss") || name.equals(".sbss")) {
+ if(sheader.entsize != 0) throw new CompilationException("bss segment has data!");
+ emitBSS(sheader.addr,sheader.size);
+ } else {
+ throw new CompilationException("Unknown segment: " + name);
+ }
+ }
+ indent = 0;
+
+ p(" public " + className + "() throws FaultException {");
+ if(fastMem) {
+ p(" super(false); // don't allow empty pages");
+ p(" if(PAGE_SIZE != " + toHex(Runtime.PAGE_SIZE) + ") throw new Error(\"Runtime.PAGE_SIZE mismatch\");");
+ } else {
+ p(" super(true); // allow empty pages");
}
+ p(" // init data");
+ p(" entryPoint = " + toHex(elf.header.entry) + ";");
+ p(inits.toString());
+ p(" brk = (" + toHex(highestAddr) + "+PAGE_SIZE-1) >>> PAGE_SHIFT;");
+ p(" state = INITIALIZED;");
+ p(" }");
+ p("");
+ p();
+ p(" public static void main(String[] javaArgs) throws Exception {");
+ p(" String[] args = new String[javaArgs.length+1];");
+ p(" System.arraycopy(javaArgs,0,args,1,javaArgs.length);");
+ p(" args[0] = \"" + className + "\";");
+ p(" " + className + " me = new " + className + "();");
+ p(" // User data");
+ p(" int addr = me.sbrk(PAGE_SIZE);");
+ p(" for(int i=0;i<10;i++) {");
+ p(" String s = \"User Info item: \" + (i+1) + \"\\0\";");
+ p(" byte[] b = s.getBytes(\"US-ASCII\");");
+ p(" me.copyout(b,addr,b.length);");
+ p(" me.setUserInfo(i,addr);");
+ p(" addr += b.length;");
+ p(" }");
+ p(" // End user data");
+ p(" int status = me.run(args);");
+ p(" System.err.println(\"Exit status: \" + status);");
+ p(" System.exit(status);");
+ p(" }");
+ p();
+ p(" protected void _start(int pc) {");
+ p(" // set the stack pointer");
+ p(" r26 = STUFF_BASE;");
+ p(" r27 = PAGE_SIZE;");
+ p(" r29 = INITIAL_SP;");
+ p(" // set the \"return address\" from _start to point at the \"magic exit address\" (0xdeadbeef)");
+ p(" r31 = 0xdeadbeef;");
+ p(" this.pc = pc;");
+ p(" }");
+
+ p();
+ p(" protected void _execute() throws ExecutionException { trampoline(); }");
+ p();
+ p(" private final void trampoline() throws ExecutionException {");
+ p(" boolean finished = false;");
+ p(" while(!finished) {");
+ p(" switch(this.pc & " + toHex(~(MAX_BYTES_PER_METHOD-1)) + ") {");
+ p(runs.toString());
+ p(" default: throw new Error(\"invalid address 0x\" + Long.toString(this.pc&0xffffffffL,16));");
+ p(" }");
+ p(" }");
+ p(" }");
+ p("}");
+ }
- System.out.println();
- System.out.println(prefix + " public static void main(String[] s) { new " + className + "().main(); }");
- System.out.println();
- System.out.println(prefix + " public void main() {");
- System.out.println();
- System.out.println(prefix + " // allocate the stack");
- System.out.println(prefix + " mem_read[1] = mem_write[1] = new int[65535];");
- System.out.println();
- System.out.println(prefix + " // set the stack pointer");
- System.out.println(prefix + " r29 = 0x0001ff00;");
- System.out.println();
- System.out.println(prefix + " // set the \"return address\" from _start to point at the \"magic exit address\" (0xdeadbeef)");
- System.out.println(prefix + " r31 = 0xdeadbeef;");
- System.out.println();
- System.out.println(prefix + " // read in the .data segment");
- System.out.println(prefix + " initData();");
- System.out.println();
- System.out.println(prefix + " trampoline(0x" + entry_point_string + ");");
- System.out.println();
- System.out.println(prefix + " }");
+ private static int startOfMethod = 0;
+ private static int endOfMethod = 0;
+
+ private static void startMethod(int addr) {
+ addr &= ~(MAX_BYTES_PER_METHOD-1);
+ endOfMethod = addr + MAX_BYTES_PER_METHOD;
+ String methodName = "run_" + Long.toString(addr & 0xffffffffL, 16);
+ runs.append(indents[4] + "case " + toHex(addr) + ": finished = !" + methodName + "(); break;\n");
+ p("private final boolean " + methodName + "() throws ExecutionException { /"+"* " + toHex(addr) + " - " + toHex(endOfMethod) + " *" + "/");
+ indent++;
+ p("int addr, tmp;");
+ for(int i=0;i<freqRegs.length;i++)
+ p("int " + freqRegs[i] + " = this." + freqRegs[i] + ";");
+ p("for(;;) {");
+ indent++;
+ p("switch(pc) {");
+ indent++;
+ startOfMethod = addr;
- System.out.println();
- System.out.println(prefix + " public void trampoline(int pc) {");
- System.out.println(prefix + " this.pc = pc;");
- System.out.println(prefix + " while(true) {");
- System.out.println(prefix + " switch(this.pc & 0xffffff00) {");
- System.out.println(prefix + " case 0xdeadbe00: System.out.println(\"exiting with return value \" + r2); System.exit(r2); continue;");
- System.out.print(runs);
- System.out.println(prefix + " default: throw new Error(\"invalid address 0x\" + Long.toString(this.pc&0xffffffffL,16));");
- System.out.println(prefix + " }");
- System.out.println(prefix + " }");
- System.out.println(prefix + " }");
- System.out.println(prefix + "}");
+ }
+ private static void endMethod() { endMethod(endOfMethod); }
+ private static void endMethod(int lastAddr) {
+ if(startOfMethod == 0) return;
+ // This isn't strictly necessary; its just here to work around unreachable code errors
+ p("case " + toHex(lastAddr) + ":");
+ indent++;
+ p("pc=" + toHex(lastAddr) + ";");
+ leaveMethod();
+ indent--;
+ if(debugCompiler)
+ p("default: throw new Error(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16) + \" (got here from 0x\" + Long.toString(lastPC&0xffffffffL,16)+\")\");");
+ else
+ p("default: throw new Error(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16));");
+ indent--;
+ p("}"); // end switch
+ p("/* NOT REACHED */");
+ indent--;
+ p("}"); // end for
+ indent--;
+ p("}"); // end method
+ endOfMethod = 0;
}
- static int _instruction;
- static boolean readnext = true;
+ private static void branch(int pc, int target) {
+ if(debugCompiler)
+ p("lastPC = " + toHex(pc) + ";");
+ p("pc=" + toHex(target) + ";");
+ if((pc&METHOD_MASK) == (target&METHOD_MASK))
+ p("continue;");
+ else
+ leaveMethod();
+ }
+
+ private static void leaveMethod() { leaveMethod(true); }
+ private static void leaveMethod(boolean cont) {
+ for(int i=0;i<freqRegs.length;i++)
+ p("this." + freqRegs[i] + " = " + freqRegs[i] + ";");
+ p("return " + (cont?"true":"false") + ";");
+ }
- /** reads <tt>numbytes</tt> from the stream, emitting <tt>case</tt> blocks starting at vaddr <tt>ofs</tt> */
- static void emit(int vaddr, int numbytes, DataInputStream dis) throws IOException {
- if (last_emit != -1 && ((last_emit & 0xffffff00) != (vaddr & 0xffffff00))) {
- System.out.println(prefix + " case 0x" + Long.toString((last_emit & 0xffffffffL) + 0x1000, 16) + ": return;");
- System.out.println(prefix + " default: throw new Error(\"invalid address 0x\" + Long.toString(pc&0xffffffffL,16));");
- System.out.println(prefix + " }");
- System.out.println(prefix + " }");
+ private static int nextEmitTextAddr = -1;
+ private static void emitText(int addr, DataInputStream dis, int size, Set jumpableAddresses) throws CompilationException,IOException {
+ if(addr < nextEmitTextAddr) throw new CompilationException("Out of order sections");
+ if((addr&3)!=0 || (size&3)!=0) throw new CompilationException("Section on weird boundaries");
+ int count = size/4;
+ int nextInsn = dis.readInt();
+ if(nextInsn == -1) throw new Error("Actually read -1 at " + toHex(addr));
+ int insn;
+
+ for(int i=0;i<count;i++,addr+=4) {
+ insn = nextInsn;
+ nextInsn = (i == count-1) ? -1 : dis.readInt();
+ if(addr >= endOfMethod) { endMethod(); startMethod(addr); }
+ if(jumpableAddresses==null || addr == startOfMethod || jumpableAddresses.contains(new Integer(addr))) {
+ p("case " + toHex(addr) + ":");
+ unreachable = false;
+ } else if(unreachable) {
+ continue;
+ } else if(debugCompiler) {
+ p("/" + "* pc = " + toHex(addr) + "*" + "/");
+ }
+ indent++;
+ emitInstruction(addr,insn,nextInsn);
+ indent--;
}
- if (last_emit == -1 || ((last_emit & 0xffffff00) != (vaddr & 0xffffff00))) {
- System.out.println("");
- System.out.println(prefix + " private void run_" + Long.toString(vaddr & 0xffffff00L,16) + "() {");
- runs += " case 0x" + Long.toString(vaddr & 0xffffff00L,16) +
- ": run_" + Long.toString(vaddr & 0xffffff00L,16) + "(); continue;\n";
- System.out.println(prefix + " switch(pc) {");
+ nextEmitTextAddr = addr;
+ dis.close();
+ }
+
+ private static int initDataCount = 0;
+ private static void emitData(int addr, DataInputStream dis, int size, boolean readOnly) throws CompilationException,IOException {
+ if((addr&3)!=0 || (size&3)!=0) throw new CompilationException("Section on weird boundaries");
+ int count = size/4;
+ String varname = "_data" + (++initDataCount);
+ p("private final static int[] " + varname + " = {");
+ indent++;
+ for(int i=0;i<count;) {
+ StringBuffer sb = new StringBuffer();
+ for(int j=0;j<8 && i<count;j++,i++) {
+ sb.append(toHex8(dis.readInt()));
+ if(i!=count-1) sb.append(",");
+ }
+ p(sb.toString());
}
- int ofs = vaddr;
- try {
- OUTER: for(ofs = vaddr; ofs < vaddr + numbytes; ofs+=4) {
- if (readnext) _instruction = dis.readInt();
- readnext = true;
-
- String istring = Long.toString(_instruction & 0xffffffffL, 16);
- while(istring.length() < 8) istring = "0" + istring;
- String ostring = Long.toString(ofs & 0xffffffffL, 16);
- while(ostring.length() < 8) ostring = "0" + ostring;
- System.out.print(prefix + " /* " + istring + " */ case 0x" + ostring + ": System.out.println(\"pc=0x\" + Long.toString(pc&0xffffffffL,16));");
-
- emit_instruction(ofs, _instruction);
+ indent--;
+ p("};");
+ inits.append(indents[2] + "initPages(" + varname + "," + toHex(addr) + "," + (readOnly?"true":"false") + ");\n");
+ dis.close();
+ }
+
+ private static int initBSSCount = 0;
+ private static void emitBSS(int addr, int size) throws CompilationException,IOException {
+ if((addr&3)!=0 || (size&3)!=0) throw new CompilationException("Section on weird boundaries");
+ int count = size/4;
+ inits.append(indents[2] + "clearPages(" + toHex(addr) + "," + toHex(count) + ");\n");
+ }
+
+ private static void findBranchesInSymtab(ELF.Symtab symtab, Set jumps) {
+ ELF.Symbol[] symbols = symtab.symbols;
+ int n=0;
+ for(int i=0;i<symbols.length;i++) {
+ ELF.Symbol s = symbols[i];
+ if(s.type == ELF.Symbol.STT_FUNC) {
+ //System.err.println("Adding symbol: " + s.name + " at " + toHex(s.addr));
+ if(jumps.add(new Integer(s.addr))) n++;
}
- } catch (EOFException e) {
- emit(ofs, " // warning, reached EOF before section end");
- } finally {
- last_emit = ofs;
- }
+ }
+ if(printStats) System.err.println("Found " + n + " additional possible branch targets in Symtab");
}
-
- private static void emit_instruction(int ofs, int instruction) throws IOException {
- int op = (instruction >>> 26) & 0xff; // bits 26-31
- int rs = (instruction >> 21) & 0x1f; // bits 21-25
- int rt = (instruction >> 16) & 0x1f; // bits 16-20
- int rd = (instruction >> 11) & 0x1f; // bits 11-15
- int shamt = (instruction >> 6) & 0x1f; // bits 6-10
- int subcode = instruction & 0x3f; // bits 0-5
+
+ private static void findBranchesInText(int addr, DataInputStream dis, int size, Set jumps) throws IOException {
+ int count = size/4;
+ int pc = addr;
+ int n=0;
- int branch_offset = (((instruction & 0x8000) << 16) | ((instruction & 0x7fff))) * 4;
- int jump_target = (instruction & 0x03ffffff) * 4;
- int signed_immediate = (int)((short)(instruction & 0xffff));
- int unsigned_immediate = instruction & 0xffff;
-
- switch(op) {
-
- case 0: {
- switch(subcode) {
-
- case 0:
- if (instruction == 0) emit(ofs, " /* NOP */;"); // NOP
- else emit(ofs, " r" + rd + " = r" + rt + " << " + shamt + ";"); // SLL
- break;
-
- case 1: throw new Error("opcode 0, subcode 1 is not part of the MIPS I instruction set");
-
- case 2:
- emit(ofs, " r" + rd + " = r" + rt + " >>> " + shamt + ";"); // SRL
- break;
-
- case 3:
- emit(ofs, " r" + rd + " = r" + rt + " >> " + shamt + ";"); // SRA
- break;
-
- case 4:
- emit(ofs, " r" + rd + " = r" + rt + " << (r" + rs + " % 32);"); // SLLV
- break;
-
- case 5: throw new Error("opcode 0, subcode 5 is not part of the MIPS I instruction set");
-
- case 6:
- emit(ofs, " r" + rd + " = r" + rs + " >>> (r" + rt + " % 32);"); // SRLV
- break;
-
- case 7:
- emit(ofs, " r" + rd + " = r" + rs + " >> (r" + rt + " % 32);"); // SRAV
- break;
-
- case 8:
- _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
- emit(ofs, " pc = r" + rs + "; return;"); // JR
- break;
-
- case 9:
- _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
- emit(ofs, " r" + rd + " = pc + 8; pc = r" + rs + "; return;"); // JALR
- break;
-
- case 10: throw new Error("opcode 0, subcode 10 is not part of the MIPS I instruction set");
- case 11: throw new Error("opcode 0, subcode 11 is not part of the MIPS I instruction set");
-
- case 12:
- emit(ofs, " syscall(" + ((instruction & 0x07ffffc0) >> 6) + ");"); // SYSCALL
+ for(int i=0;i<count;i++,pc+=4) {
+ int insn = dis.readInt();
+ int op = (insn >>> 26) & 0xff;
+ int rs = (insn >>> 21) & 0x1f;
+ int rt = (insn >>> 16) & 0x1f;
+ int signedImmediate = (insn << 16) >> 16;
+ int branchTarget = signedImmediate;
+ int jumpTarget = (insn & 0x03ffffff);
+ int subcode = insn & 0x3f;
+
+ switch(op) {
+ case 0:
+ switch(subcode) {
+ case 9: // JALR
+ if(jumps.add(new Integer(pc+8))) n++; // return address
break;
-
- case 13:
- emit(ofs, " /* BREAKPOINT */");
+ case 12: // SYSCALL
+ if(jumps.add(new Integer(pc+4))) n++;
break;
-
- case 14: throw new Error("opcode 0, subcode 14 is not part of the MIPS I instruction set");
- case 15: throw new Error("opcode 0, subcode 15 is not part of the MIPS I instruction set");
-
- case 16:
- emit(ofs, " r" + rd + " = (int)(hilo >>> 32);"); // MFHI
- break;
-
- case 17:
- emit(ofs, " hilo = (hilo & 0x00000000ffffffffL) | ((r" + rs + " & 0xffffffffL) << 32);"); // MTHI
- break;
-
- case 18:
- emit(ofs, " r" + rd + " = (int)hilo;"); // MFLO
- break;
-
- case 19:
- emit(ofs, " hilo = (hilo & 0xffffffff00000000L) | (r" + rs + " & 0xffffffffL);"); // MTLO
- break;
-
- case 20: throw new Error("opcode 0, subcode 20 is not part of the MIPS I instruction set");
- case 22: throw new Error("opcode 0, subcode 22 is not part of the MIPS I instruction set");
- case 23: throw new Error("opcode 0, subcode 23 is not part of the MIPS I instruction set");
-
- case 24:
- emit(ofs, " hilo = ((long)r" + rs + ") * ((long)r" + rt + ");"); // MULT
- break;
-
- case 25:
- emit(ofs, " hilo = (r" + rs + " & 0xffffffffL) * (r" + rt + " & 0xffffffffL);"); // MULTU
- break;
-
- case 26:
- emit(ofs, " hilo = (((r" + rs + " % r" + rt +") & 0xffffffffL) << 32) | ((r" + rs + " / r" + rt +") & 0xffffffffL);"); // DIV
- break;
-
- case 27:
- emit(ofs, " hilo = (((r" + rs + " & 0xffffffffL) % (r" + rt +" & 0xffffffffL)) << 32) | " + // DIVU
- "((r" + rs + " & 0xffffffffL) / (r" + rt +" & 0xffffffffL));");
- break;
-
- case 28: throw new Error("opcode 0, subcode 28 is not part of the MIPS I instruction set");
- case 29: throw new Error("opcode 0, subcode 29 is not part of the MIPS I instruction set");
- case 30: throw new Error("opcode 0, subcode 30 is not part of the MIPS I instruction set");
- case 31: throw new Error("opcode 0, subcode 31 is not part of the MIPS I instruction set");
-
- case 32:
- emit(ofs, " r" + rd + " = r" + rs + " + r" + rt + ";"); // ADD
- break;
-
- case 33:
- emit(ofs, " r" + rd + " = (int)(((r" + rs + " & 0xffffffffL) + (r" + rt + " & 0xffffffffL)));"); // ADDU
- break;
-
- case 34:
- emit(ofs, " r" + rd + " = r" + rs + " - r" + rt + ";"); // SUB
- break;
-
- case 35:
- emit(ofs, " r" + rd + " = (int)(((r" + rs + " & 0xffffffffL) - (r" + rt + " & 0xffffffffL)));"); // SUBU
- break;
-
- case 36:
- emit(ofs, " r" + rd + " = r" + rs + " & r" + rt + ";"); // AND
- break;
-
- case 37:
- emit(ofs, " r" + rd + " = r" + rs + " | r" + rt + ";"); // OR
- break;
-
- case 38:
- emit(ofs, " r" + rd + " = r" + rs + " ^ r" + rt + ";"); // XOR
- break;
-
- case 39:
- emit(ofs, " r" + rd + " = ~(r" + rs + " | r" + rt + ");"); // NOR
- break;
-
- case 40: throw new Error("opcode 0, subcode 40 is not part of the MIPS I instruction set");
- case 41: throw new Error("opcode 0, subcode 41 is not part of the MIPS I instruction set");
-
- case 42:
- emit(ofs, " r" + rd + " = (r" + rs + " < r" + rt + ") ? 1 : 0;"); // SLT
- break;
-
- case 43:
- emit(ofs, " r" + rd + " = ((r" + rs + " & 0xffffffffL) < (r" + rt + " & 0xffffffffL)) ? 1 : 0;"); // SLTU
- break;
-
- case 44: case 45: case 46: case 47: case 48: case 49: case 50: case 51: case 52: case 53: case 54:
- case 55: case 56: case 57: case 58: case 59: case 60: case 61: case 62: case 63:
- throw new Error("opcode 0, subcode " + subcode + " is not part of the MIPS I instruction set");
- default:
- throw new Error("opcode 0, subcode " + subcode + " is not a valid MIPS instruction");
- }
- break;
}
-
- case 1: {
+ break;
+ case 1:
switch(rt) {
- case 0:
- _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
- emit(ofs, " if (r" + rs + " < 0) { pc += " + (branch_offset + 4) + "; return; }; "); // BLTZ
- break;
-
- case 1:
- _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
- emit(ofs, " if (r" + rs + " >= 0) { pc += " + (branch_offset + 4) + "; return; }; "); // BGEZ
- break;
-
- case 16:
- _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
- emit(ofs, " r31 = pc + 4; if (r" + rs + " < 0) { pc += " + (branch_offset + 4) + "; return; }; "); // BLTZAL
- break;
-
- case 17:
- _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
- emit(ofs, " r31 = pc + 4; if (r" + rs + " >= 0) { pc += " + (branch_offset + 4) + "; return; }; "); // BGEZAL
- break;
-
- default: throw new Error("opcode 1, subcode " + rt + " is not part of the MIPS I instruction set");
+ case 16: // BLTZAL
+ case 17: // BGTZAL
+ if(jumps.add(new Integer(pc+8))) n++; // return address
+ // fall through
+ case 0: // BLTZ
+ case 1: // BGEZ
+ if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
+ break;
}
break;
- }
-
- case 2:
- _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
- emit(ofs, " pc &= 0xf0000000; pc |= 0x" + Long.toString(jump_target & 0xffffffffL, 16) + "; return;"); // J
- break;
-
- case 3:
- _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
- emit(ofs, " r31 = pc + 4; pc &= 0xf0000000; pc |= 0x" + Long.toString(jump_target & 0xffffffffL, 16) + "; return;"); // JAL
- break;
-
- case 4:
- _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
- emit(ofs, " if (r" + rs + " == r" + rt + ") { pc += 0x" + Long.toString((branch_offset + 4) & 0xffffffffL, 16) + "; return; }; "); // BEQ
- break;
-
- case 5:
- _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
- emit(ofs, " if (r" + rs + " != r" + rt + ") { pc += 0x" + Long.toString((branch_offset + 4) & 0xffffffffL, 16) + "; return; }; "); // BNE
- break;
-
- case 6:
- _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
- emit(ofs, " if (r" + rs + " <= 0) { pc += 0x" + Long.toString((branch_offset + 4) & 0xffffffffL, 16) + "; return; }; "); // BLEZ
- break;
-
- case 7:
- _instruction = dis.readInt(); readnext = false; emit_instruction(ofs, _instruction);
- emit(ofs, " if (r" + rs + " > 0) { pc += 0x" + Long.toString((branch_offset + 4) & 0xffffffffL, 16) + "; return; }; "); // BGTZ
- break;
-
- case 8: case 9:
- emit(ofs, " r" + rt + " = r" + rs + " + " + signed_immediate + ";"); // ADDI[U]
- break;
-
- case 10:
- emit(ofs, " r" + rt + " = (r" + rs + " < " + signed_immediate + ") ? 1 : 0;"); // SLTI
- break;
-
- case 11:
- emit(ofs, " r" + rt + " = ((r" + rs + " & 0xffffffffL) < (" + signed_immediate + " & 0xffffffffL)) ? 1 : 0;"); // SLTIU
- break;
-
- case 12:
- emit(ofs, " r" + rt + " = r" + rs + " & " + unsigned_immediate + ";"); // ANDI
+ case 3: // JAL
+ if(jumps.add(new Integer(pc+8))) n++; // return address
+ // fall through
+ case 2: // J
+ if(jumps.add(new Integer((pc&0xf0000000)|(jumpTarget << 2)))) n++;
break;
-
- case 13:
- emit(ofs, " r" + rt + " = r" + rs + " | " + unsigned_immediate + ";"); // ORI
+ case 4: // BEQ
+ case 5: // BNE
+ case 6: // BLEZ
+ case 7: // BGTZ
+ if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
break;
-
- case 14:
- emit(ofs, " r" + rt + " = r" + rs + " ^ " + unsigned_immediate + ";"); // XORI
- break;
-
- case 15:
- emit(ofs, " r" + rt + " = " + unsigned_immediate + " << 16;"); // LUI
- break;
-
- case 16: /* throw new Error("coprocessor instructions (opcode 16) are not implemented"); */
- emit(ofs, " throw new Error(\"coprocessor instructions (opcode 16) are not implemented\");");
- break;
- case 17: throw new Error("coprocessor instructions (opcode 17) are not implemented");
- case 18: throw new Error("coprocessor instructions (opcode 18) are not implemented");
-
- case 19: case 20: case 21: case 22: case 23: case 24: case 25: case 26: case 27: case 28:
- throw new Error("opcode " + op + " is not part of the MIPS I instruction set @" + Long.toString(ofs & 0xffffffffL, 16));
-
- case 32: {
- String dest = "(r" + rs + " + " + signed_immediate + ")";
- emit(ofs, " tmp = mem_read[" + dest + ">>>16][" + dest + " & 0xffff]; r" + rt + " = ((tmp & 0x80) << 24) | (tmp & 0x7f);"); // LB
- break;
- }
-
- case 33: {
- String dest = "(r" + rs + " + " + signed_immediate + ")";
- emit(ofs, " tmp = mem_read[" + dest + ">>>16][" + dest + " & 0xffff]; r" + rt + " = ((tmp & 0x8000) << 16) | (tmp & 0x7fff);"); // LH
- break;
- }
-
- case 34:
- emit(ofs, " throw new Error(\"LWL (opcode 34) is not supported; are you sure you used -mstrict-align?\");");
- break;
-
- case 35: {
- String dest = "(r" + rs + " + " + signed_immediate + ")";
- emit(ofs, " r" + rt + " = mem_read[" + dest + ">>>16][" + dest + " & 0xffff];"); // LW
- break;
- }
-
- case 36: {
- String dest = "(r" + rs + " + " + signed_immediate + ")";
- emit(ofs, " r" + rt + " = mem_read[" + dest + ">>>16][" + dest + " & 0xffff] & 0xff;"); // LBU
- break;
- }
-
- case 37: {
- String dest = "(r" + rs + " + " + signed_immediate + ")";
- emit(ofs, " r" + rt + " = mem_read[" + dest + ">>>16][" + dest + " & 0xffff] & 0xffff;"); // LHU
- break;
- }
-
- case 38:
- emit(ofs, " throw new Error(\"LWR (opcode 38) is not supported; are you sure you used -mstrict-align?\");");
- break;
-
- case 39: throw new Error("opcode 39 is not part of the MIPS I instruction set");
-
- case 40: {
- String dest = "(r" + rs + " + " + signed_immediate + ")";
- emit(ofs, " tmp = mem_read[" + dest + ">>>16][" + dest + " & 0xffff]; " +
- "mem_write[" + dest + ">>>16][" + dest + " & 0xffff] = (tmp & 0xffffff00) | (r" + rt + " & 0xff);"); // SB
- break;
- }
-
- case 41: {
- String dest = "(r" + rs + " + " + signed_immediate + ")";
- emit(ofs, " tmp = mem_read[" + dest + ">>>16][" + dest + " & 0xffff]; " +
- "mem_write[" + dest + ">>>16][" + dest + " & 0xffff] = (tmp & 0xffff0000) | (r" + rt + " & 0xffff);"); // SH
+ case 17: // FPU Instructions
+ switch(rs) {
+ case 8: // BC1F, BC1T
+ if(jumps.add(new Integer(pc+branchTarget*4+4))) n++;
+ break;
+ }
break;
+ }
+ }
+ dis.close();
+ if(printStats) System.err.println("Found " + n + " additional possible branch targets in Text segment");
+ }
+
+ private static void findBranchesInData(DataInputStream dis, int size, Set jumps, int textStart, int textEnd) throws IOException {
+ int count = size/4;
+ int n=0;
+ for(int i=0;i<count;i++) {
+ int word = dis.readInt();
+ if((word&3)==0 && word >= textStart && word < textEnd) {
+ if(jumps.add(new Integer(word))) n++;
+ }
+ }
+ dis.close();
+ if(n>0 && printStats) System.err.println("Found " + n + " additional possible branch targets in Data segment");
+ }
+
+ private static boolean unreachable = false;
+
+ private static void emitInstruction(int pc, int insn, int nextInsn) throws IOException,CompilationException {
+ if(insn == -1) throw new Error("insn is -1");
+
+ int op = (insn >>> 26) & 0xff; // bits 26-31
+ int rs = (insn >>> 21) & 0x1f; // bits 21-25
+ int rt = (insn >>> 16) & 0x1f; // bits 16-20
+ int ft = (insn >>> 16) & 0x1f;
+ int rd = (insn >>> 11) & 0x1f; // bits 11-15
+ int fs = (insn >>> 11) & 0x1f;
+ int shamt = (insn >>> 6) & 0x1f; // bits 6-10
+ int fd = (insn >>> 6) & 0x1f;
+ int subcode = insn & 0x3f; // bits 0-5
+
+ int jumpTarget = (insn & 0x03ffffff); // bits 0-25
+ int unsignedImmediate = insn & 0xffff;
+ int signedImmediate = (insn << 16) >> 16;
+ int branchTarget = signedImmediate;
+
+ int tmp, addr; // temporaries
+
+ //if(pc%64==0) p("System.err.println(\"Executing: " + toHex(pc) + "\");");
+ //p("/" + "*" + (pc == -1 ? "Delay Slot" : toHex(pc)) + " *" + "/ ");
+ if(pc==-1) p("/" + "* Next insn is delay slot *" + "/ ");
+
+ switch(op) {
+ case 0: {
+ switch(subcode) {
+ case 0: // SLL
+ if(insn == 0)
+ p("/* NOOP */");
+ else
+ p( "r"+rd+" = r"+rt+" << "+shamt+";");
+ break;
+ case 2: // SRL
+ p( "r"+rd+" = r"+rt+" >>> "+shamt+";");
+ break;
+ case 3: // SRA
+ p( "r"+rd+" = r"+rt+" >> "+shamt+";");
+ break;
+ case 4: // SLLV
+ p( "r"+rd+" = r"+rt+" << (r"+rs+"&0x1f);");
+ break;
+ case 6: // SRLV
+ p( "r"+rd+" = r"+rt+" >>> (r"+rs+"&0x1f);");
+ break;
+ case 7: // SRAV
+ p( "r"+rd+" = r"+rt+" >> (r"+rs+"&0x1f);");
+ break;
+ case 8: // JR
+ if(pc == -1) throw new Error("pc modifying insn in delay slot");
+ emitInstruction(-1,nextInsn,-1);
+ if(debugCompiler) p("lastPC = " + toHex(pc) + ";");
+ p("pc=r" + rs + ";");
+ leaveMethod();
+ unreachable = true;
+ break;
+ case 9: // JALR
+ if(pc == -1) throw new Error("pc modifying insn in delay slot");
+ emitInstruction(-1,nextInsn,-1);
+ if(debugCompiler) p("lastPC = " + toHex(pc) + ";");
+ p("pc=r" + rs + ";");
+ p("r" + RA + "=" + toHex(pc+8 /*skip this insn and delay slot*/) + ";");
+ leaveMethod();
+ unreachable = true;
+ break;
+ case 12: // SYSCALL
+ p( "r"+V0+" = syscall(r"+V0+",r"+A0+",r"+A1+",r"+A2+",r"+A3+");");
+ p("if (state != RUNNING) {");
+ indent++;
+ p("pc = " + toHex(pc+4) + ";");
+ leaveMethod(false);
+ indent--;
+ p("}");
+ break;
+ case 13: // BREAK
+ p( "throw new ExecutionException(\"Break\");");
+ break;
+ case 16: // MFHI
+ p( "r"+rd+" = hi;");
+ break;
+ case 17: // MTHI
+ p( "hi = r"+rs+";");
+ break;
+ case 18: // MFLO
+ p( "r"+rd+" = lo;");
+ break;
+ case 19: // MTLO
+ p( "lo = r"+rs+";");
+ break;
+ case 24: // MULT
+ p( "{ long hilo = (long)(r"+rs+") * ((long)r"+rt+"); " +
+ "hi = (int) (hilo >>> 32); " +
+ "lo = (int) hilo; }");
+ break;
+ case 25: // MULTU
+ p( "{ long hilo = (r"+rs+" & 0xffffffffL) * (r"+rt+" & 0xffffffffL); " +
+ "hi = (int) (hilo >>> 32); " +
+ "lo = (int) hilo; } ");
+ break;
+ case 26: // DIV
+ p( "hi = r"+rs+"%r"+rt+"; lo = r"+rs+"/r"+rt+";");
+ break;
+ case 27: // DIVU
+ p( "hi = (int)((r"+rs+" & 0xffffffffL) % (r"+rt+" & 0xffffffffL)); " +
+ "lo = (int)((r"+rs+" & 0xffffffffL) / (r"+rt+" & 0xffffffffL));");
+ break;
+ case 32: // ADD
+ throw new CompilationException("ADD (add with oveflow trap) not suported");
+ /*This must trap on overflow
+ p( "r"+rd+" = r"+rs+" + r"+rt+";");
+ break;*/
+ case 33: // ADDU
+ p( "r"+rd+" = r"+rs+" + r"+rt+";");
+ break;
+ case 34: // SUB
+ throw new CompilationException("SUB (add with oveflow trap) not suported");
+ /*This must trap on overflow
+ p( "r"+rd+" = r"+rs+" - r"+rt+";");
+ break;*/
+ case 35: // SUBU
+ p( "r"+rd+" = r"+rs+" - r"+rt+";");
+ break;
+ case 36: // AND
+ p( "r"+rd+" = r"+rs+" & r"+rt+";");
+ break;
+ case 37: // OR
+ p( "r"+rd+" = r"+rs+" | r"+rt+";");
+ break;
+ case 38: // XOR
+ p( "r"+rd+" = r"+rs+" ^ r"+rt+";");
+ break;
+ case 39: // NOR
+ p( "r"+rd+" = ~(r"+rs+" | r"+rt+");");
+ break;
+ case 42: // SLT
+ p( "r"+rd+" = r"+rs+" < r"+rt+" ? 1 : 0;");
+ break;
+ case 43: // SLTU
+ p( "r"+rd+" = ((r"+rs+" & 0xffffffffL) < (r"+rt+" & 0xffffffffL)) ? 1 : 0;");
+ break;
+ default:
+ throw new RuntimeException("Illegal instruction 0/" + subcode);
}
-
- case 42:
- emit(ofs, " throw new Error(\"SWL (opcode 42) is not supported; are you sure you used -mstrict-align?\");");
- break;
-
- case 43: {
- String dest = "(r" + rs + " + " + signed_immediate + ")";
- emit(ofs, " mem_write[" + dest + ">>>16][" + dest + " & 0xffff] = r" + rt + ";"); // SW
- break;
+ break;
+ }
+ case 1: {
+ switch(rt) {
+ case 0: // BLTZ
+ if(pc == -1) throw new Error("pc modifying insn in delay slot");
+ p("if(r" + rs + " < 0) {");
+ indent++;
+ emitInstruction(-1,nextInsn,-1);
+ branch(pc,pc+branchTarget*4+4);
+ indent--;
+ p("}");
+ break;
+ case 1: // BGEZ
+ if(pc == -1) throw new Error("pc modifying insn in delay slot");
+ p("if(r" + rs + " >= 0) {");
+ indent++;
+ emitInstruction(-1,nextInsn,-1);
+ branch(pc,pc+branchTarget*4+4);
+ indent--;
+ p("}");
+ break;
+ case 16: // BLTZAL
+ if(pc == -1) throw new Error("pc modifying insn in delay slot");
+ p("if(r" + rs + " < 0) {");
+ indent++;
+ emitInstruction(-1,nextInsn,-1);
+ p("r" + RA + "=" + toHex(pc+8 /*skip this insn and delay slot*/) + ";");
+ branch(pc,pc+branchTarget*4+4);
+ indent--;
+ p("}");
+ break;
+ case 17: // BGEZAL
+ if(pc == -1) throw new Error("pc modifying insn in delay slot");
+ p("if(r" + rs + " >= 0) {");
+ indent++;
+ emitInstruction(-1,nextInsn,-1);
+ p("r" + RA + "=" + toHex(pc+8 /*skip this insn and delay slot*/) + ";");
+ branch(pc,pc+branchTarget*4+4);
+ indent--;
+ p("}");
+ break;
+ default:
+ throw new RuntimeException("Illegal Instruction 1/" + rt);
}
-
- case 44: throw new Error("opcode 44 is not part of the MIPS I instruction set");
- case 45: throw new Error("opcode 45 is not part of the MIPS I instruction set");
-
- case 46:
- emit(ofs, " throw new Error(\"SWR (opcode 46) is not supported; are you sure you used -mstrict-align?\");");
- break;
-
- case 47: throw new Error("opcode 47 is not part of the MIPS I instruction set");
- case 48: throw new Error("opcode 48 is not part of the MIPS I instruction set");
-
- case 49:
- emit(ofs, " throw new Error(\"floating point operations (opcode 49) are not yet supported\");");
- break;
-
- case 50:
- emit(ofs, " throw new Error(\"floating point operations (opcode 50) are not yet supported\");");
- break;
-
- case 51: case 52: case 53: case 54: case 55: case 56:
- throw new Error("opcode " + op + " is not part of the MIPS I instruction set");
-
- case 57:
- emit(ofs, " throw new Error(\"floating point operations (opcode 57) are not yet supported\");");
- break;
-
- case 58:
- emit(ofs, " throw new Error(\"floating point operations (opcode 58) are not yet supported\");");
- break;
-
- case 60: case 61: case 62: case 63:
- throw new Error("opcode " + op + " is not part of the MIPS I instruction set");
-
- default:
- throw new Error("unknown opcode " + op);
+ break;
+ }
+ case 2: { // J
+ if(pc == -1) throw new Error("pc modifying insn in delay slot");
+ emitInstruction(-1,nextInsn,-1);
+ branch(pc,(pc&0xf0000000)|(jumpTarget << 2));
+ unreachable = true;
+ break;
+ }
+ case 3: { // JAL
+ if(pc == -1) throw new Error("pc modifying insn in delay slot");
+ emitInstruction(-1,nextInsn,-1);
+ p("r" + RA + "=" + toHex(pc+8 /*skip this insn and delay slot*/) + ";");
+ branch(pc, (pc&0xf0000000)|(jumpTarget << 2));
+ unreachable = true;
+ break;
+ }
+ case 4: // BEQ
+ if(pc == -1) throw new Error("pc modifying insn in delay slot");
+ p("// BEQ");
+ p("if(r" + rs + " == r" + rt + ") {");
+ indent++;
+ emitInstruction(-1,nextInsn,-1);
+ branch(pc,pc+branchTarget*4+4);
+ indent--;
+ p("}");
+ break;
+ case 5: // BNE
+ if(pc == -1) throw new Error("pc modifying insn in delay slot");
+ p("if(r" + rs + " != r" + rt + ") {");
+ indent++;
+ emitInstruction(-1,nextInsn,-1);
+ branch(pc,pc+branchTarget*4+4);
+ indent--;
+ p("}");
+ break;
+ case 6: //BLEZ
+ if(pc == -1) throw new Error("pc modifying insn in delay slot");
+ p("if(r" + rs + " <= 0) {");
+ indent++;
+ emitInstruction(-1,nextInsn,-1);
+ branch(pc,pc+branchTarget*4+4);
+ indent--;
+ p("}");
+ break;
+ case 7: //BGTZ
+ if(pc == -1) throw new Error("pc modifying insn in delay slot");
+ p("if(r" + rs + " > 0) {");
+ indent++;
+ emitInstruction(-1,nextInsn,-1);
+ branch(pc,pc+branchTarget*4+4);
+ indent--;
+ p("}");
+ break;
+ case 8: // ADDI
+ p( "r"+rt+" = r"+rs+" + "+signedImmediate +";");
+ break;
+ case 9: // ADDIU
+ p( "r"+rt+" = r"+rs+" + "+signedImmediate+";");
+ break;
+ case 10: // SLTI
+ p( "r"+rt+" = r"+rs+" < "+signedImmediate+" ? 1 : 0;");
+ break;
+ case 11: // SLTIU
+ p( "r"+rt+" = (r"+rs+"&0xffffffffL) < ("+unsignedImmediate+"&0xffffffffL) ? 1 : 0;");
+ break;
+ case 12: // ANDI
+ p( "r"+rt+" = r"+rs+" & "+unsignedImmediate+";");
+ break;
+ case 13: // ORI
+ p( "r"+rt+" = r"+rs+" | "+unsignedImmediate+";");
+ break;
+ case 14: // XORI
+ p( "r"+rt+" = r"+rs+" ^ "+unsignedImmediate+";");
+ break;
+ case 15: // LUI
+ p( "r"+rt+" = "+unsignedImmediate+" << 16;");
+ break;
+ case 16:
+ throw new CompilationException("TLB/Exception support not implemented");
+ case 17: { // FPU
+ switch(rs) {
+ case 0: // MFC.1
+ p( "r"+rt+" = f"+rd+";");
+ break;
+ case 2: // CFC.1
+ if(fs != 31) throw new CompilationException("FCR " + fs + " unavailable");
+ p( "r"+rt+" = fcsr;");
+ break;
+ case 4: // MTC.1
+ p( "f"+rd+" = r"+rt+";");
+ break;
+ case 6: // CTC.1
+ if(fs != 31) throw new CompilationException("FCR " + fs + " unavailable");
+ p( "fcsr = r"+rt+";");
+ break;
+ case 8: {// BC1F, BC1T
+ tmp = (insn>>>16)&1;
+ p("//BC1F, BC1T");
+ p("if(((fcsr&0x800000)!=0) == (" + tmp + "!=0)) {");
+ indent++;
+ emitInstruction(-1,nextInsn,-1);
+ branch(pc,pc+branchTarget*4+4);
+ indent--;
+ p("}");
+ break;
+ }
+ case 16: { // Single
+ switch(subcode) {
+ case 0: // ADD.S
+ p(setFloat(fd,getFloat(fs)+"+"+getFloat(ft)));
+ break;
+ case 1: // SUB.S
+ p(setFloat(fd,getFloat(fs)+"-"+getFloat(ft)));
+ break;
+ case 2: // MUL.S
+ p(setFloat(fd,getFloat(fs)+"*"+getFloat(ft)));
+ break;
+ case 3: // DIV.S
+ p(setFloat(fd,getFloat(fs)+"/"+getFloat(ft)));
+ break;
+ case 5: // ABS.S
+ p(setFloat(fd,"Math.abs("+getFloat(fs)+")"));
+ break;
+ case 6: // MOV.S
+ p("f"+fd+" = f"+fs+"; // MOV.S");
+ break;
+ case 7: // NEG.S
+ p(setFloat(fd,"-"+getFloat(fs))); // FEATURE: just flip the sign bit
+ break;
+ case 33: // CVT.D.S
+ p(setDouble(fd,"(float)"+getFloat(fs)));
+ break;
+ case 36: // CVT.W.D
+ p("// CVT.W.D");
+ p("switch(roundingMode()) {");
+ indent++;
+ p("case 0: f"+fd+" = (int)Math.floor("+getFloat(fs)+"+0.5); break; // Round to nearest");
+ p("case 1: f"+fd+" = (int)"+getFloat(fs)+"; break; // Round towards zero");
+ p("case 2: f"+fd+" = (int)Math.ceil("+getFloat(fs)+"); break; // Round towards plus infinity");
+ p("case 3: f"+fd+" = (int)Math.floor("+getFloat(fs)+"); break; // Round towards minus infinity");
+ indent--;
+ p("}");
+ break;
+ case 50: // C.EQ.D
+ p("setFC("+getFloat(fs)+"=="+getFloat(ft)+");"); // FEATURE: just compare the ints, be sure things are normalized
+ break;
+ case 60: // C.LT.D
+ p("setFC("+getFloat(fs)+"<"+getFloat(ft)+");");
+ break;
+ case 62: // C.LE.D
+ p("setFC("+getFloat(fs)+"<="+getFloat(ft)+");");
+ break;
+ default: throw new CompilationException("Invalid Instruction 17/" + rs + "/" + subcode);
+ }
+ break;
+ }
+ case 17: { // Double
+ switch(subcode) {
+ case 0: // ADD.D
+ p(setDouble(fd,getDouble(fs)+"+"+getDouble(ft)));
+ break;
+ case 1: // SUB.D
+ p(setDouble(fd,getDouble(fs)+"-"+getDouble(ft)));
+ break;
+ case 2: // MUL.D
+ p(setDouble(fd,getDouble(fs)+"*"+getDouble(ft)));
+ break;
+ case 3: // DIV.D
+ p(setDouble(fd,getDouble(fs)+"/"+getDouble(ft)));
+ break;
+ case 5: // ABS.D
+ p(setDouble(fd,"Math.abs("+getDouble(fs)+")"));
+ break;
+ case 6: // MOV.D
+ p("f"+fd+" = f"+fs+";");
+ p("f"+(fd+1)+" = f"+(fs+1)+";");
+ break;
+ case 7: // NEG.D
+ p(setDouble(fd,"-"+getDouble(fs))); // FEATURE: just flip the sign bit
+ break;
+ case 32: // CVT.S.D
+ p(setFloat(fd,"(float)"+getDouble(fs)));
+ break;
+ case 36: // CVT.W.D
+ p("// CVT.W.D");
+ p("switch(roundingMode()) {");
+ indent++;
+ p("case 0: f"+fd+" = (int)Math.floor("+getDouble(fs)+"+0.5); break; // Round to nearest");
+ p("case 1: f"+fd+" = (int)"+getDouble(fs)+"; break; // Round towards zero");
+ p("case 2: f"+fd+" = (int)Math.ceil("+getDouble(fs)+"); break; // Round towards plus infinity");
+ p("case 3: f"+fd+" = (int)Math.floor("+getDouble(fs)+"); break; // Round towards minus infinity");
+ indent--;
+ p("}");
+ break;
+ case 50: // C.EQ.D
+ p("setFC("+getDouble(fs)+"=="+getDouble(ft)+");"); // FEATURE: just compare the ints, be sure things are normalized
+ break;
+ case 60: // C.LT.D
+ p("setFC("+getDouble(fs)+"<"+getDouble(ft)+");");
+ break;
+ case 62: // C.LE.D
+ p("setFC("+getDouble(fs)+"<="+getDouble(ft)+");");
+ break;
+ default: throw new CompilationException("Invalid Instruction 17/" + rs + "/" + subcode);
+ }
+ break;
+ }
+ case 20: { // Integer
+ switch(subcode) {
+ case 32: // CVT.S.W
+ p(" // CVS.S.W");
+ p(setFloat(fd,"((float)f"+fs+")"));
+ break;
+ case 33: // CVT.D.W
+ p("// CVT.D.W");
+ p(setDouble(fd,"((double)f"+fs+")"));
+ break;
+ default: throw new CompilationException("Invalid Instruction 17/" + rs + "/" + subcode);
+ }
+ break;
+ }
+ default:
+ throw new CompilationException("Invalid Instruction 17/" + rs);
}
+ break;
+ }
+ case 18: case 19:
+ throw new CompilationException("coprocessor 2 and 3 instructions not available");
+ case 32: { // LB
+ p("addr=r" + rs +"+"+signedImmediate + ";");
+ memRead("addr&~3","tmp");
+ p("switch(addr&3) {");
+ indent++;
+ p("case 0: tmp = (tmp>>>24)&0xff; break;");
+ p("case 1: tmp = (tmp>>>16)&0xff; break;");
+ p("case 2: tmp = (tmp>>> 8)&0xff; break;");
+ p("case 3: tmp = (tmp>>> 0)&0xff; break;");
+ indent--;
+ p("}");
+ p("if((tmp&0x80)!=0) tmp |= 0xffffff00; // sign extend");
+ p("r"+rt+" = tmp;");
+ break;
+ }
+ case 33: { // LH
+ p("addr=r" + rs +"+"+signedImmediate + ";");
+ memRead("addr&~3","tmp");
+ p("switch(addr&2) {");
+ indent++;
+ p("case 0: tmp = (tmp>>>16)&0xffff; break;");
+ p("case 2: tmp = (tmp>>> 0)&0xffff; break;");
+ indent--;
+ p("}");
+ p("if((tmp&0x8000)!=0) tmp |= 0xffff0000; // sign extend");
+ p("r"+rt+" = tmp;");
+ break;
+ }
+ case 34: { // LWL;
+ p("addr=r" + rs +"+"+signedImmediate + ";");
+ memRead("addr&~3","tmp");
+ p("switch(addr&3) {");
+ indent++;
+ p("case 0: r"+rt+" = (r"+rt+"&0x00000000)|(tmp<< 0); break;");
+ p("case 1: r"+rt+" = (r"+rt+"&0x000000ff)|(tmp<< 8); break;");
+ p("case 2: r"+rt+" = (r"+rt+"&0x0000ffff)|(tmp<<16); break;");
+ p("case 3: r"+rt+" = (r"+rt+"&0x00ffffff)|(tmp<<24); break;");
+ indent--;
+ p("}");
+ break;
+ }
+ case 35: // LW
+ memRead("r" + rs +"+"+signedImmediate,"r"+rt);
+ break;
+ case 36: { // LBU
+ p("addr=r" + rs +"+"+signedImmediate + ";");
+ memRead("addr&~3","tmp");
+ p("switch(addr&3) {");
+ indent++;
+ p("case 0: r"+rt+" = (tmp>>>24)&0xff; break;");
+ p("case 1: r"+rt+" = (tmp>>>16)&0xff; break;");
+ p("case 2: r"+rt+" = (tmp>>> 8)&0xff; break;");
+ p("case 3: r"+rt+" = (tmp>>> 0)&0xff; break;");
+ indent--;
+ p("}");
+ break;
+ }
+ case 37: { // LHU
+ p("addr=r" + rs +"+"+signedImmediate + ";");
+ memRead("addr&~3","tmp");
+ p("switch(addr&2) {");
+ indent++;
+ p("case 0: r"+rt+" = (tmp>>>16)&0xffff; break;");
+ p("case 2: r"+rt+" = (tmp>>> 0)&0xffff; break;");
+ indent--;
+ p("}");
+ break;
+ }
+ case 38: { // LWR
+ p("addr=r" + rs +"+"+signedImmediate + ";");
+ memRead("addr&~3","tmp");
+ p("switch(addr&3) {");
+ indent++;
+ p("case 0: r"+rt+" = (r"+rt+"&0xffffff00)|(tmp>>>24); break;");
+ p("case 1: r"+rt+" = (r"+rt+"&0xffff0000)|(tmp>>>16); break;");
+ p("case 2: r"+rt+" = (r"+rt+"&0xff000000)|(tmp>>> 8); break;");
+ p("case 3: r"+rt+" = (r"+rt+"&0x00000000)|(tmp>>> 0); break;");
+ indent--;
+ p("}");
+ break;
+ }
+ case 40: { // SB
+ p("// SB");
+ p("addr=r" + rs +"+"+signedImmediate + ";");
+ memRead("addr&~3","tmp");
+ p("switch(addr&3) {");
+ indent++;
+ p("case 0: tmp = (tmp&0x00ffffff) | ((r"+rt+"&0xff)<<24); break;");
+ p("case 1: tmp = (tmp&0xff00ffff) | ((r"+rt+"&0xff)<<16); break;");
+ p("case 2: tmp = (tmp&0xffff00ff) | ((r"+rt+"&0xff)<< 8); break;");
+ p("case 3: tmp = (tmp&0xffffff00) | ((r"+rt+"&0xff)<< 0); break;");
+ indent--;
+ p("}");
+ memWrite("addr&~3","tmp");
+ break;
+ }
+ case 41: { // SH
+ p("// SH");
+ p("addr=r" + rs +"+"+signedImmediate + ";");
+ memRead("addr&~3","tmp");
+ p("switch(addr&2) {");
+ indent++;
+ p("case 0: tmp = (tmp&0x0000ffff) | ((r"+rt+"&0xffff)<<16); break;");
+ p("case 2: tmp = (tmp&0xffff0000) | ((r"+rt+"&0xffff)<< 0); break;");
+ indent--;
+ p("}");
+ memWrite("addr&~3","tmp");
+ break;
+ }
+ case 42: { // SWL
+ p(" // SWL");
+ p("addr=r" + rs +"+"+signedImmediate + ";");
+ memRead("addr&~3","tmp");
+ p("switch(addr&3) {");
+ indent++;
+ p("case 0: tmp=(tmp&0x00000000)|(r"+rt+">>> 0); break;");
+ p("case 1: tmp=(tmp&0xff000000)|(r"+rt+">>> 8); break;");
+ p("case 2: tmp=(tmp&0xffff0000)|(r"+rt+">>>16); break;");
+ p("case 3: tmp=(tmp&0xffffff00)|(r"+rt+">>>24); break;");
+ indent--;
+ p("}");
+ memWrite("addr&~3","tmp");
+ break;
+ }
+ case 43: // SW
+ memWrite("r"+rs+"+"+signedImmediate,"r" + rt);
+ break;
+ case 46: { // SWR
+ p(" // SWR");
+ p("addr=r" + rs +"+"+signedImmediate + ";");
+ memRead("addr&~3","tmp");
+ p("switch(addr&3) {");
+ indent++;
+ p("case 0: tmp=(tmp&0x00ffffff)|(r"+rt+"<<24); break;");
+ p("case 1: tmp=(tmp&0x0000ffff)|(r"+rt+"<<16); break;");
+ p("case 2: tmp=(tmp&0x000000ff)|(r"+rt+"<< 8); break;");
+ p("case 3: tmp=(tmp&0x00000000)|(r"+rt+"<< 0); break;");
+ indent--;
+ p("}");
+ memWrite("addr&~3","tmp");
+ break;
+ }
+ case 49: // LWC1
+ memRead("r"+rs+"+"+signedImmediate,"f"+rt);
+ break;
+ case 57: // SWC1
+ memWrite("r"+rs+"+"+signedImmediate,"f"+rt);
+ break;
+ default:
+ throw new CompilationException("Invalid Instruction: " + op + " at " + toHex(pc));
+ }
}
-
-
-
- static String prefix = "";
- static void emit(int vaddr, String s) {
- if (s.indexOf("r0 = ") != -1) s = " /* NOP */";
- if (!s.trim().endsWith("return;") && s.indexOf("throw") == -1) s += " pc = 0x" + Long.toString((vaddr + 4) & 0xffffffffL,16) + ";";
- System.out.println(s);
+
+ private static void memWrite(String addr, String target) {
+ if(fastMem)
+ p("writePages[("+addr+")>>>"+Runtime.PAGE_SHIFT+"][(("+addr+")>>>2)&"+toHex(Runtime.PAGE_WORDS-1)+"] = " + target + ";");
+ else
+ p("memWrite(" + addr + "," + target + ");");
+
+ }
+
+ private static void memRead(String addr, String target) {
+ if(fastMem)
+ p(target + " = readPages[("+addr+")>>>"+Runtime.PAGE_SHIFT+"][(("+addr+")>>>2)&"+toHex(Runtime.PAGE_WORDS-1)+"];");
+ else
+ p(target + " = memRead(" + addr + ");");
+ }
+
+ private static String getFloat(int r) { return "(Float.intBitsToFloat(f"+r+"))"; }
+ private static String getDouble(int r) {
+ return "(Double.longBitsToDouble(((f"+(r+1)+"&0xffffffffL) << 32) | (f"+r+"&0xffffffffL)))";
+ }
+ private static String setFloat(int r, String expr) { return "f"+r+"=Float.floatToRawIntBits("+expr+");"; }
+ private static String setDouble(int r, String expr) {
+ return "{ long l = Double.doubleToLongBits("+expr+"); "+
+ "f"+(r+1)+" = (int)(l >>> 32); f"+r+" = (int)l; }";
+ }
+
+ private final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
+ private final static String toHex8(int n) {
+ String s = Long.toString(n & 0xffffffffL, 16);
+ StringBuffer sb = new StringBuffer("0x");
+ for(int i=8-s.length();i>0;i--) sb.append('0');
+ sb.append(s);
+ return sb.toString();
}
}