1 // Copyright 2000-2005 the Contributors, as shown in the revision logs.
2 // Licensed under the Apache License 2.0 ("the License").
3 // You may not use this file except in compliance with the License.
5 package org.ibex.nestedvm;
10 import org.ibex.nestedvm.util.*;
12 public abstract class Compiler implements Registers {
13 /** The ELF binary being read */
16 /** The name of the class beging generated */
17 final String fullClassName;
19 /** The name of the binary this class is begin generated from */
20 String source = "unknown.mips.binary";
21 public void setSource(String source) { this.source = source; }
23 /** Thrown when the compilation fails for some reason */
24 static class Exn extends Exception { public Exn(String s) { super(s); } }
26 // Set this to true to enable fast memory access
27 // When this is enabled a Java RuntimeException will be thrown when a page fault occures. When it is disabled
28 // a FaultException will be throw which is easier to catch and deal with, however. as the name implies, this is slower
29 boolean fastMem = true;
31 // This MUST be a power of two. If it is not horrible things will happen
32 // NOTE: This value can be much higher without breaking the classfile
33 // specs (around 1024) but Hotstop seems to do much better with smaller
35 int maxInsnPerMethod = 128;
38 int maxBytesPerMethod;
41 void maxInsnPerMethodInit() throws Exn {
42 if((maxInsnPerMethod&(maxInsnPerMethod-1)) != 0) throw new Exn("maxBytesPerMethod is not a power of two");
43 maxBytesPerMethod = maxInsnPerMethod*4;
44 methodMask = ~(maxBytesPerMethod-1);
45 while(maxBytesPerMethod>>>methodShift != 1) methodShift++;
48 // True to try to determine which case statement are needed and only include them
49 boolean pruneCases = true;
51 boolean assumeTailCalls = true;
53 // True to insert some code in the output to help diagnore compiler problems
54 boolean debugCompiler = false;
56 // True to print various statistics about the compilation
57 boolean printStats = false;
59 // True to generate runtime statistics that slow execution down significantly
60 boolean runtimeStats = false;
62 boolean supportCall = true;
64 boolean nullPointerCheck = false;
66 String runtimeClass = "org.ibex.nestedvm.Runtime";
68 String hashClass = "java.util.Hashtable";
72 boolean lessConstants;
77 int totalPages = 65536;
81 void pageSizeInit() throws Exn {
82 if((pageSize&(pageSize-1)) != 0) throw new Exn("pageSize not a multiple of two");
83 if((totalPages&(totalPages-1)) != 0) throw new Exn("totalPages not a multiple of two");
84 while(pageSize>>>pageShift != 1) pageShift++;
87 /** A set of all addresses that can be jumped too (only available if pruneCases == true) */
88 Hashtable jumpableAddresses;
90 /** Some important symbols */
91 ELF.Symbol userInfo, gp;
93 private static void usage() {
94 System.err.println("Usage: java Compiler [-outfile output.java] [-o options] [-dumpoptions] <classname> <binary.mips>");
95 System.err.println("-o takes mount(8) like options and can be specified multiple times");
96 System.err.println("Available options:");
97 for(int i=0;i<options.length;i+=2)
98 System.err.print(options[i] + ": " + wrapAndIndent(options[i+1],18-2-options[i].length(),18,62));
102 public static void main(String[] args) throws IOException {
103 String outfile = null;
104 String outdir = null;
106 String className = null;
107 String mipsBinaryFileName = null;
108 String outformat = null;
109 boolean dumpOptions = false;
111 while(args.length-arg > 0) {
112 if(args[arg].equals("-outfile")) {
114 if(arg==args.length) usage();
116 } else if(args[arg].equals("-d")) {
118 if(arg==args.length) usage();
120 } else if(args[arg].equals("-outformat")) {
122 if(arg==args.length) usage();
123 outformat = args[arg];
124 } else if(args[arg].equals("-o")) {
126 if(arg==args.length) usage();
127 if(o==null || o.length() == 0)
129 else if(args[arg].length() != 0)
130 o += "," + args[arg];
131 } else if(args[arg].equals("-dumpoptions")) {
133 } else if(className == null) {
134 className = args[arg];
135 } else if(mipsBinaryFileName == null) {
136 mipsBinaryFileName = args[arg];
142 if(className == null || mipsBinaryFileName == null) usage();
144 Seekable mipsBinary = new Seekable.File(mipsBinaryFileName);
147 OutputStream os = null;
148 Compiler comp = null;
149 if(outformat == null || outformat.equals("class")) {
150 if(outfile != null) {
151 os = new FileOutputStream(outfile);
152 comp = new ClassFileCompiler(mipsBinary,className,os);
153 } else if(outdir != null) {
154 File f = new File(outdir);
155 if(!f.isDirectory()) {
156 System.err.println(outdir + " doesn't exist or is not a directory");
159 comp = new ClassFileCompiler(mipsBinary,className,f);
161 System.err.println("Refusing to write a classfile to stdout - use -outfile foo.class");
164 } else if(outformat.equals("javasource") || outformat .equals("java")) {
165 w = outfile == null ? new OutputStreamWriter(System.out): new FileWriter(outfile);
166 comp = new JavaSourceCompiler(mipsBinary,className,w);
168 System.err.println("Unknown output format: " + outformat);
172 comp.parseOptions(o);
173 comp.setSource(mipsBinaryFileName);
176 System.err.println("== Options ==");
177 for(int i=0;i<options.length;i+=2)
178 System.err.println(options[i] + ": " + comp.getOption(options[i]).get());
179 System.err.println("== End Options ==");
185 System.err.println("Compiler Error: " + e.getMessage());
188 if(w != null) w.close();
189 if(os != null) os.close();
193 public Compiler(Seekable binary, String fullClassName) throws IOException {
194 this.fullClassName = fullClassName;
195 elf = new ELF(binary);
197 if(elf.header.type != ELF.ET_EXEC) throw new IOException("Binary is not an executable");
198 if(elf.header.machine != ELF.EM_MIPS) throw new IOException("Binary is not for the MIPS I Architecture");
199 if(elf.ident.data != ELF.ELFDATA2MSB) throw new IOException("Binary is not big endian");
202 abstract void _go() throws Exn, IOException;
204 private boolean used;
205 public void go() throws Exn, IOException {
206 if(used) throw new RuntimeException("Compiler instances are good for one shot only");
209 if(onePage && pageSize <= 4096) pageSize = 4*1024*1024;
210 if(nullPointerCheck && !fastMem) throw new Exn("fastMem must be enabled for nullPointerCheck to be of any use");
211 if(onePage && !fastMem) throw new Exn("fastMem must be enabled for onePage to be of any use");
212 if(totalPages == 1 && !onePage) throw new Exn("totalPages == 1 and onePage is not set");
213 if(onePage) totalPages = 1;
215 maxInsnPerMethodInit();
218 // Get a copy of the symbol table in the elf binary
219 ELF.Symtab symtab = elf.getSymtab();
220 if(symtab == null) throw new Exn("Binary has no symtab (did you strip it?)");
223 userInfo = symtab.getGlobalSymbol("user_info");
224 gp = symtab.getGlobalSymbol("_gp");
225 if(gp == null) throw new Exn("no _gp symbol (did you strip the binary?)");
228 // Find all possible branches
229 jumpableAddresses = new Hashtable();
231 jumpableAddresses.put(new Integer(elf.header.entry),Boolean.TRUE);
233 ELF.SHeader text = elf.sectionWithName(".text");
234 if(text == null) throw new Exn("No .text segment");
236 findBranchesInSymtab(symtab,jumpableAddresses);
238 for(int i=0;i<elf.sheaders.length;i++) {
239 ELF.SHeader sheader = elf.sheaders[i];
240 String name = sheader.name;
241 // if this section doesn't get loaded into our address space don't worry about it
242 if(sheader.addr == 0x0) continue;
243 if(name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors"))
244 findBranchesInData(new DataInputStream(sheader.getInputStream()),sheader.size,jumpableAddresses,text.addr,text.addr+text.size);
247 findBranchesInText(text.addr,new DataInputStream(text.getInputStream()),text.size,jumpableAddresses);
250 if(unixRuntime && runtimeClass.startsWith("org.ibex.nestedvm.")) runtimeClass = "org.ibex.nestedvm.UnixRuntime";
252 for(int i=0;i<elf.sheaders.length;i++) {
253 String name = elf.sheaders[i].name;
254 if((elf.sheaders[i].flags & ELF.SHF_ALLOC) !=0 && !(
255 name.equals(".text")|| name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") ||
256 name.equals(".ctors") || name.equals(".dtors") || name.equals(".bss") || name.equals(".sbss")))
257 throw new Exn("Unknown section: " + name);
262 private void findBranchesInSymtab(ELF.Symtab symtab, Hashtable jumps) {
263 ELF.Symbol[] symbols = symtab.symbols;
265 for(int i=0;i<symbols.length;i++) {
266 ELF.Symbol s = symbols[i];
267 if(s.type == ELF.Symbol.STT_FUNC) {
268 if(jumps.put(new Integer(s.addr),Boolean.TRUE) == null) {
269 //System.err.println("Adding symbol from symtab: " + s.name + " at " + toHex(s.addr));
274 if(printStats) System.err.println("Found " + n + " additional possible branch targets in Symtab");
277 private void findBranchesInText(int base, DataInputStream dis, int size, Hashtable jumps) throws IOException {
281 int[] lui_val = new int[32];
282 int[] lui_pc = new int[32];
283 //Interpreter inter = new Interpreter(source);
285 for(int i=0;i<count;i++,pc+=4) {
286 int insn = dis.readInt();
287 int op = (insn >>> 26) & 0xff;
288 int rs = (insn >>> 21) & 0x1f;
289 int rt = (insn >>> 16) & 0x1f;
290 int signedImmediate = (insn << 16) >> 16;
291 int unsignedImmediate = insn & 0xffff;
292 int branchTarget = signedImmediate;
293 int jumpTarget = (insn & 0x03ffffff);
294 int subcode = insn & 0x3f;
300 if(jumps.put(new Integer(pc+8),Boolean.TRUE) == null) n++; // return address
303 if(jumps.put(new Integer(pc+4),Boolean.TRUE) == null) n++;
311 if(jumps.put(new Integer(pc+8),Boolean.TRUE) == null) n++; // return address
315 if(jumps.put(new Integer(pc+branchTarget*4+4),Boolean.TRUE) == null) n++;
320 if(jumps.put(new Integer(pc+8),Boolean.TRUE) == null) n++; // return address
323 if(jumps.put(new Integer((pc&0xf0000000)|(jumpTarget << 2)),Boolean.TRUE) == null) n++;
329 if(jumps.put(new Integer(pc+branchTarget*4+4),Boolean.TRUE) == null) n++;
332 if(pc - lui_pc[rs] <= 4*32) {
333 int t = (lui_val[rs]<<16)+signedImmediate;
334 if((t&3)==0 && t >= base && t < base+size) {
335 if(jumps.put(new Integer(t),Boolean.TRUE) == null) {
336 //System.err.println("Possible jump to " + toHex(t) + " (" + inter.sourceLine(t) + ") from " + toHex(pc) + " (" + inter.sourceLine(pc) + ")");
340 // we just blew it away
341 if(rt == rs) lui_pc[rs] = 0;
346 lui_val[rt] = unsignedImmediate;
351 case 17: // FPU Instructions
353 case 8: // BC1F, BC1T
354 if(jumps.put(new Integer(pc+branchTarget*4+4),Boolean.TRUE) == null) n++;
361 if(printStats) System.err.println("Found " + n + " additional possible branch targets in Text segment");
364 private void findBranchesInData(DataInputStream dis, int size, Hashtable jumps, int textStart, int textEnd) throws IOException {
367 for(int i=0;i<count;i++) {
368 int word = dis.readInt();
369 if((word&3)==0 && word >= textStart && word < textEnd) {
370 if(jumps.put(new Integer(word),Boolean.TRUE) == null) {
371 //System.err.println("Added " + toHex(word) + " as possible branch target (fron data segment)");
377 if(n>0 && printStats) System.err.println("Found " + n + " additional possible branch targets in Data segment");
380 // Helper functions for pretty output
381 final static String toHex(int n) { return "0x" + Long.toString(n & 0xffffffffL, 16); }
382 final static String toHex8(int n) {
383 String s = Long.toString(n & 0xffffffffL, 16);
384 StringBuffer sb = new StringBuffer("0x");
385 for(int i=8-s.length();i>0;i--) sb.append('0');
387 return sb.toString();
390 final static String toOctal3(int n) {
391 char[] buf = new char[3];
392 for(int i=2;i>=0;i--) {
393 buf[i] = (char) ('0' + (n & 7));
396 return new String(buf);
400 private class Option {
401 private java.lang.reflect.Field field;
402 public Option(String name) throws NoSuchFieldException { field = name==null ? null : Compiler.class.getDeclaredField(name); }
403 public void set(Object val) {
404 if(field == null) return;
406 /*field.setAccessible(true); NOT in JDK 1.1 */
407 field.set(Compiler.this,val);
408 } catch(IllegalAccessException e) {
409 System.err.println(e);
412 public Object get() {
413 if(field == null) return null;
415 /*field.setAccessible(true); NOT in JDK 1.1 */
416 return field.get(Compiler.this);
417 } catch(IllegalAccessException e) {
418 System.err.println(e); return null;
421 public Class getType() { return field == null ? null : field.getType(); }
424 private static String[] options = {
425 "fastMem", "Enable fast memory access - RuntimeExceptions will be thrown on faults",
426 "nullPointerCheck", "Enables checking at runtime for null pointer accessses (slows things down a bit, only applicable with fastMem)",
427 "maxInsnPerMethod", "Maximum number of MIPS instructions per java method (128 is optimal with Hotspot)",
428 "pruneCases", "Remove unnecessary case 0xAABCCDD blocks from methods - may break some weird code",
429 "assumeTailCalls", "Assume the JIT optimizes tail calls",
430 "optimizedMemcpy", "Use an optimized java version of memcpy where possible",
431 "debugCompiler", "Output information in the generated code for debugging the compiler - will slow down generated code significantly",
432 "printStats", "Output some useful statistics about the compilation",
433 "runtimeStats", "Keep track of some statistics at runtime in the generated code - will slow down generated code significantly",
434 "supportCall", "Keep a stripped down version of the symbol table in the generated code to support the call() method",
435 "runtimeClass", "Full classname of the Runtime class (default: Runtime) - use this is you put Runtime in a package",
436 "hashClass", "Full classname of a Hashtable class (default: java.util.HashMap) - this must support get() and put()",
437 "unixRuntime", "Use the UnixRuntime (has support for fork, wai, du, pipe, etc)",
438 "pageSize", "The page size (must be a power of two)",
439 "totalPages", "Total number of pages (total mem = pageSize*totalPages, must be a power of two)",
440 "onePage", "One page hack (FIXME: document this better)",
441 "lessConstants", "Use less constants at the cost of speed (FIXME: document this better)",
442 "singleFloat", "Support single precision (32-bit) FP ops only"
445 private Option getOption(String name) {
446 name = name.toLowerCase();
448 for(int i=0;i<options.length;i+=2)
449 if(options[i].toLowerCase().equals(name))
450 return new Option(options[i]);
452 } catch(NoSuchFieldException e) {
457 public void parseOptions(String opts) {
458 if(opts == null || opts.length() == 0) return;
459 StringTokenizer st = new StringTokenizer(opts,",");
460 while(st.hasMoreElements()) {
461 String tok = st.nextToken();
464 if(tok.indexOf("=") != -1) {
465 key = tok.substring(0,tok.indexOf("="));
466 val = tok.substring(tok.indexOf("=")+1);
467 } else if(tok.startsWith("no")) {
468 key = tok.substring(2);
474 Option opt = getOption(key);
476 System.err.println("WARNING: No such option: " + key);
480 if(opt.getType() == String.class)
482 else if(opt.getType() == Integer.TYPE)
484 opt.set(parseInt(val));
485 } catch(NumberFormatException e) {
486 System.err.println("WARNING: " + val + " is not an integer");
488 else if(opt.getType() == Boolean.TYPE)
489 opt.set(new Boolean(val.toLowerCase().equals("true")||val.toLowerCase().equals("yes")));
491 throw new Error("Unknown type: " + opt.getType());
495 private static Integer parseInt(String s) {
498 if(!s.startsWith("0x") && s.endsWith("m")) { s = s.substring(0,s.length()-1); mult = 1024*1024; }
499 else if(!s.startsWith("0x") && s.endsWith("k")) { s = s.substring(0,s.length()-1); mult = 1024; }
501 if(s.length() > 2 && s.startsWith("0x")) n = Integer.parseInt(s.substring(2),16);
502 else n = Integer.parseInt(s);
503 return new Integer(n*mult);
506 private static String wrapAndIndent(String s, int firstindent, int indent, int width) {
507 StringTokenizer st = new StringTokenizer(s," ");
508 StringBuffer sb = new StringBuffer();
509 for(int i=0;i<firstindent;i++)
512 while(st.hasMoreTokens()) {
513 String tok = st.nextToken();
514 if(tok.length() + sofar + 1 > width && sofar > 0) {
516 for(int i=0;i<indent;i++) sb.append(' ');
518 } else if(sofar > 0) {
523 sofar += tok.length();
526 return sb.toString();
529 // This ugliness is to work around a gcj static linking bug (Bug 12908)
530 // The best solution is to force gnu.java.locale.Calendar to be linked in but this'll do
531 static String dateTime() {
533 return new Date().toString();
534 } catch(RuntimeException e) {