1 package org.ibex.nestedvm;
5 import org.ibex.nestedvm.util.*;
6 import org.ibex.classgen.*;
8 // FEATURE: Use IINC where possible
9 // FEATURE: Some kind of peephole optimization
10 // FEATURE: Special mode to support single-precision only - regs are floats not ints
12 /* FEATURE: Span large binaries across several classfiles
13 * We should be able to do this with no performance penalty
14 * Every method in the inner classes is static and takes the main class as an arg
15 * This makes them look just like methods in the main class because arg1 gets loaded into
19 /* FEATURE: smarter with local regs
20 * Be even smarter with the use of local registers. We need to only load fields into
21 * local regs when they are actually used and only write to fields if the regs could have
22 * changed. This should allow us to put more regs in local vars. Right now putting all used
23 * regs local vars makes code like this slower.
25 * void work(int a, int b) {
32 * Because all the regs used in "real work" are loaded/restored even for fast path
36 public class ClassFileCompiler extends Compiler implements CGConst {
37 /** The stream to write the compiled output to */
38 private OutputStream os;
39 private PrintStream warn = System.err;
42 private MethodGen clinit;
43 private MethodGen init;
44 private Type.Object me;
45 private Type.Object superClass;
47 public ClassFileCompiler(String path, String className, OutputStream os) throws IOException { this(new Seekable.File(path),className,os); }
48 public ClassFileCompiler(Seekable binary, String className, OutputStream os) throws IOException {
49 super(binary,className);
53 public void setWarnWriter(PrintStream warn) { this.warn = warn; }
55 protected void _go() throws Exn, IOException {
58 } catch(ClassGen.Exn e) {
60 throw new Exn("Class generation exception: " + e.toString());
64 private void __go() throws Exn, IOException {
65 if(!pruneCases) throw new Exn("-o prunecases MUST be enabled for ClassFileCompiler");
68 me = new Type.Object(fullClassName);
69 superClass = new Type.Object(runtimeClass);
70 cg = new ClassGen(me,superClass,ACC_PUBLIC|ACC_FINAL|ACC_SUPER);
71 if(source != null) cg.setSourceFile(source);
74 cg.addField("pc",Type.INT,ACC_PRIVATE);
75 cg.addField("hi",Type.INT,ACC_PRIVATE);
76 cg.addField("lo",Type.INT,ACC_PRIVATE);
77 cg.addField("fcsr",Type.INT,ACC_PRIVATE);
78 for(int i=1;i<32;i++) cg.addField("r" + i,Type.INT,ACC_PRIVATE);
79 for(int i=0;i<32;i++) cg.addField("f" + i,Type.INT,ACC_PRIVATE);
81 clinit = cg.addMethod("<clinit>",Type.VOID,Type.NO_ARGS,ACC_PRIVATE|ACC_STATIC);
83 init = cg.addMethod("<init>",Type.VOID,Type.NO_ARGS,ACC_PUBLIC);
85 init.add(LDC,pageSize);
86 init.add(LDC,totalPages);
87 init.add(INVOKESPECIAL,new MethodRef(me,"<init>",Type.VOID,new Type[]{Type.INT,Type.INT}));
90 init = cg.addMethod("<init>",Type.VOID,new Type[]{Type.INT,Type.INT},ACC_PUBLIC);
94 init.add(INVOKESPECIAL,new MethodRef(superClass,"<init>",Type.VOID,new Type[]{Type.INT,Type.INT}));
97 cg.addField("page",Type.arrayType(Type.INT),ACC_PRIVATE|ACC_FINAL);
100 init.add(GETFIELD,new FieldRef(me,"readPages",Type.arrayType(Type.INT,2)));
103 init.add(PUTFIELD,new FieldRef(me,"page",Type.arrayType(Type.INT)));
107 cg.addField("symbols",new Type.Object(hashClass),ACC_PRIVATE|ACC_STATIC|ACC_FINAL);
111 for(int i=0;i<elf.sheaders.length;i++) {
112 ELF.SHeader sheader = elf.sheaders[i];
113 String name = sheader.name;
114 // if this section doesn't get loaded into our address space don't worry about it
115 if(sheader.addr == 0x0) continue;
117 highestAddr = Math.max(highestAddr, sheader.addr + sheader.size);
119 if(name.equals(".text"))
120 emitText(sheader.addr, new DataInputStream(sheader.getInputStream()),sheader.size);
121 else if(name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors"))
122 emitData(sheader.addr, new DataInputStream(sheader.getInputStream()), sheader.size,name.equals(".rodata"));
123 else if(name.equals(".bss") || name.equals(".sbss"))
124 emitBSS(sheader.addr,sheader.size);
126 throw new Exn("Unknown segment: " + name);
134 Type.Object hash = new Type.Object(hashClass);
135 clinit.add(NEW,hash);
138 clinit.add(INVOKESPECIAL,new MethodRef(hash,"<init>",Type.VOID,Type.NO_ARGS));
139 clinit.add(PUTSTATIC,new FieldRef(me,"symbols",hash));
140 ELF.Symbol[] symbols = elf.getSymtab().symbols;
141 for(int i=0;i<symbols.length;i++) {
142 ELF.Symbol s = symbols[i];
143 if(s.type == ELF.Symbol.STT_FUNC && s.binding == ELF.Symbol.STB_GLOBAL && (s.name.equals("_call_helper") || !s.name.startsWith("_"))) {
145 clinit.add(LDC,s.name);
146 clinit.add(NEW,Type.INTEGER_OBJECT);
148 clinit.add(LDC,s.addr);
149 clinit.add(INVOKESPECIAL,new MethodRef(Type.INTEGER_OBJECT,"<init>",Type.VOID,new Type[]{Type.INT}));
150 clinit.add(INVOKEVIRTUAL,new MethodRef(hash,"put",Type.OBJECT,new Type[]{Type.OBJECT,Type.OBJECT}));
159 ELF.SHeader text = elf.sectionWithName(".text");
162 MethodGen tramp = cg.addMethod("trampoline",Type.VOID,Type.NO_ARGS,ACC_PRIVATE);
164 int start = tramp.size();
166 tramp.add(GETFIELD,new FieldRef(me,"state",Type.INT));
167 tramp.add(IFEQ,tramp.size()+2);
172 tramp.add(GETFIELD,new FieldRef(me,"pc",Type.INT));
173 tramp.add(LDC,methodShift);
176 int beg = text.addr >>> methodShift;
177 int end = ((text.addr + text.size + maxBytesPerMethod - 1) >>> methodShift);
179 MethodGen.TSI tsi = new MethodGen.TSI(beg,end-1);
180 tramp.add(TABLESWITCH,tsi);
181 for(int n=beg;n<end;n++) {
182 tsi.setTargetForVal(n,tramp.size());
183 tramp.add(INVOKESPECIAL,new MethodRef(me,"run_"+toHex(n<<methodShift),Type.VOID,Type.NO_ARGS));
184 tramp.add(GOTO,start);
186 tsi.setDefaultTarget(tramp.size());
189 tramp.add(NEW,new Type.Object("org.ibex.nestedvm.Runtime$ExecutionException"));
191 tramp.add(NEW, Type.STRINGBUFFER);
193 tramp.add(LDC,"Jumped to invalid address in trampoline (r2: ");
194 tramp.add(INVOKESPECIAL,new MethodRef(Type.STRINGBUFFER,"<init>",Type.VOID,new Type[]{Type.STRING}));
196 tramp.add(GETFIELD, new FieldRef(me,"r2",Type.INT));
197 tramp.add(INVOKEVIRTUAL,new MethodRef(Type.STRINGBUFFER,"append",Type.STRINGBUFFER,new Type[]{Type.INT}));
198 tramp.add(LDC," pc: ");
199 tramp.add(INVOKEVIRTUAL,new MethodRef(Type.STRINGBUFFER,"append",Type.STRINGBUFFER,new Type[]{Type.STRING}));
201 tramp.add(GETFIELD, new FieldRef(me,"pc",Type.INT));
202 tramp.add(INVOKEVIRTUAL,new MethodRef(Type.STRINGBUFFER,"append",Type.STRINGBUFFER,new Type[]{Type.INT}));
204 tramp.add(INVOKEVIRTUAL,new MethodRef(Type.STRINGBUFFER,"append",Type.STRINGBUFFER,new Type[]{Type.STRING}));
205 tramp.add(INVOKEVIRTUAL,new MethodRef(Type.STRINGBUFFER,"toString",Type.STRING,Type.NO_ARGS));
206 tramp.add(INVOKESPECIAL,new MethodRef(new Type.Object("org.ibex.nestedvm.Runtime$ExecutionException"),"<init>",Type.VOID,new Type[]{Type.STRING}));
211 } catch(ClassGen.Exn e) {
212 e.printStackTrace(warn);
213 throw new Exn("Generation of the trampoline method failed. Try increasing maxInsnPerMethod");
216 addConstReturnMethod("gp",gp.addr);
217 addConstReturnMethod("entryPoint",elf.header.entry);
218 addConstReturnMethod("heapStart",highestAddr);
220 if(userInfo != null) {
221 addConstReturnMethod("userInfoBase",userInfo.addr);
222 addConstReturnMethod("userInfoSize",userInfo.size);
226 Type.Object hashClassType = new Type.Object(hashClass);
227 MethodGen ls = cg.addMethod("lookupSymbol",Type.INT,new Type[]{Type.STRING},ACC_PROTECTED);
228 ls.add(GETSTATIC,new FieldRef(me,"symbols",hashClassType));
230 ls.add(INVOKEVIRTUAL,new MethodRef(hashClassType,"get",Type.OBJECT,new Type[]{Type.OBJECT}));
232 int b = ls.add(IFNULL);
233 ls.add(CHECKCAST,Type.INTEGER_OBJECT);
234 ls.add(INVOKEVIRTUAL,new MethodRef(Type.INTEGER_OBJECT,"intValue",Type.INT,Type.NO_ARGS));
236 ls.setArg(b,ls.size());
242 Type.Object cpuStateType = new Type.Object("org.ibex.nestedvm.Runtime$CPUState");
243 MethodGen setCPUState = cg.addMethod("setCPUState",Type.VOID,new Type[]{cpuStateType},ACC_PROTECTED);
244 MethodGen getCPUState = cg.addMethod("getCPUState",Type.VOID,new Type[]{cpuStateType},ACC_PROTECTED);
246 setCPUState.add(ALOAD_1);
247 getCPUState.add(ALOAD_1);
248 setCPUState.add(GETFIELD,new FieldRef(cpuStateType,"r",Type.arrayType(Type.INT)));
249 getCPUState.add(GETFIELD,new FieldRef(cpuStateType,"r",Type.arrayType(Type.INT)));
250 setCPUState.add(ASTORE_2);
251 getCPUState.add(ASTORE_2);
253 for(int i=1;i<32;i++) {
254 setCPUState.add(ALOAD_0);
255 setCPUState.add(ALOAD_2);
256 setCPUState.add(LDC,i);
257 setCPUState.add(IALOAD);
258 setCPUState.add(PUTFIELD,new FieldRef(me,"r"+i,Type.INT));
260 getCPUState.add(ALOAD_2);
261 getCPUState.add(LDC,i);
262 getCPUState.add(ALOAD_0);
263 getCPUState.add(GETFIELD,new FieldRef(me,"r"+i,Type.INT));
264 getCPUState.add(IASTORE);
267 setCPUState.add(ALOAD_1);
268 getCPUState.add(ALOAD_1);
269 setCPUState.add(GETFIELD,new FieldRef(cpuStateType,"f",Type.arrayType(Type.INT)));
270 getCPUState.add(GETFIELD,new FieldRef(cpuStateType,"f",Type.arrayType(Type.INT)));
271 setCPUState.add(ASTORE_2);
272 getCPUState.add(ASTORE_2);
274 for(int i=0;i<32;i++) {
275 setCPUState.add(ALOAD_0);
276 setCPUState.add(ALOAD_2);
277 setCPUState.add(LDC,i);
278 setCPUState.add(IALOAD);
279 setCPUState.add(PUTFIELD,new FieldRef(me,"f"+i,Type.INT));
281 getCPUState.add(ALOAD_2);
282 getCPUState.add(LDC,i);
283 getCPUState.add(ALOAD_0);
284 getCPUState.add(GETFIELD,new FieldRef(me,"f"+i,Type.INT));
285 getCPUState.add(IASTORE);
288 String[] each = new String[] { "hi","lo","fcsr","pc" };
289 for(int i=0;i<each.length;i++) {
290 setCPUState.add(ALOAD_0);
291 setCPUState.add(ALOAD_1);
292 setCPUState.add(GETFIELD,new FieldRef(cpuStateType,each[i],Type.INT));
293 setCPUState.add(PUTFIELD,new FieldRef(me,each[i],Type.INT));
295 getCPUState.add(ALOAD_1);
296 getCPUState.add(ALOAD_0);
297 getCPUState.add(GETFIELD,new FieldRef(me,each[i],Type.INT));
298 getCPUState.add(PUTFIELD,new FieldRef(cpuStateType,each[i],Type.INT));
300 setCPUState.add(RETURN);
301 getCPUState.add(RETURN);
304 MethodGen execute = cg.addMethod("_execute",Type.VOID,Type.NO_ARGS,ACC_PROTECTED);
305 int tryStart = execute.size();
306 execute.add(ALOAD_0);
307 execute.add(INVOKESPECIAL,new MethodRef(me,"trampoline",Type.VOID,Type.NO_ARGS));
308 int tryEnd = execute.size();
311 int catchInsn = execute.size();
312 execute.add(ASTORE_1);
313 execute.add(NEW, new Type.Object("org.ibex.nestedvm.Runtime$FaultException"));
315 execute.add(ALOAD_1);
316 execute.add(INVOKESPECIAL,new MethodRef("org.ibex.nestedvm.Runtime$FaultException","<init>",Type.VOID,new Type[]{new Type.Object("java.lang.RuntimeException")}));
319 execute.addExceptionHandler(tryStart,tryEnd,catchInsn,new Type.Object("java.lang.RuntimeException"));
320 execute.addThrow(new Type.Object("org.ibex.nestedvm.Runtime$ExecutionException"));
322 MethodGen main = cg.addMethod("main",Type.VOID,new Type[]{Type.arrayType(Type.STRING)},ACC_STATIC|ACC_PUBLIC);
325 main.add(INVOKESPECIAL,new MethodRef(me,"<init>",Type.VOID,Type.NO_ARGS));
326 main.add(LDC,fullClassName);
329 Type.Object ur = new Type.Object("org.ibex.nestedvm.UnixRuntime");
330 main.add(INVOKESTATIC,new MethodRef(ur,"runAndExec",Type.INT,new Type[]{ur,Type.STRING,Type.arrayType(Type.STRING)}));
332 main.add(INVOKEVIRTUAL,new MethodRef(me,"run",Type.INT,new Type[]{Type.STRING,Type.arrayType(Type.STRING)}));
334 main.add(INVOKESTATIC,new MethodRef(new Type.Object("java.lang.System"),"exit",Type.VOID,new Type[]{Type.INT}));
340 private void addConstReturnMethod(String name, int val) {
341 MethodGen m = cg.addMethod(name,Type.INT,Type.NO_ARGS,ACC_PROTECTED);
346 private static int initDataCount;
347 private void emitData(int addr, DataInputStream dis, int size, boolean readOnly) throws Exn,IOException {
348 if((addr&3)!=0 || (size&3)!=0) throw new Exn("Data section on weird boundaries");
349 int last = addr + size;
351 int segSize = Math.min(size,28000); // must be a multiple of 56
352 StringBuffer sb = new StringBuffer();
353 for(int i=0;i<segSize;i+=7) {
355 for(int j=0;j<7;j++) {
357 byte b = (i+j < size) ? dis.readByte() : 1;
361 sb.append((char) ((l>>>(7*(7-j)))&0x7f));
363 String fieldname = "_data" + (++initDataCount);
364 cg.addField(fieldname,Type.arrayType(Type.INT),ACC_PRIVATE|ACC_STATIC|ACC_FINAL);
366 clinit.add(LDC,sb.toString());
367 clinit.add(LDC,segSize/4);
368 clinit.add(INVOKESTATIC,new MethodRef(new Type.Object("org.ibex.nestedvm.Runtime"),"decodeData",Type.arrayType(Type.INT),new Type[]{Type.STRING,Type.INT}));
369 clinit.add(PUTSTATIC,new FieldRef(me,fieldname,Type.arrayType(Type.INT)));
372 init.add(GETSTATIC,new FieldRef(me,fieldname,Type.arrayType(Type.INT)));
374 init.add(LDC,readOnly ? 1 : 0);
375 init.add(INVOKEVIRTUAL,new MethodRef(me,"initPages",Type.VOID,new Type[]{Type.arrayType(Type.INT),Type.INT,Type.BOOLEAN}));
383 private void emitBSS(int addr, int size) throws Exn {
384 if((addr&3)!=0) throw new Exn("BSS section on weird boundaries");
391 init.add(INVOKEVIRTUAL,new MethodRef(me,"clearPages",Type.VOID,new Type[]{Type.INT,Type.INT}));
395 private boolean textDone; // a text segment was already processed
396 private int startOfMethod = 0; // the start of this method (not necessarily the first instruction)
397 private int endOfMethod = 0; // the maximum end of this method (could end before it is full)
398 private boolean unreachable = false; // is the current pc is reachable
400 private MethodGen.PhantomTarget returnTarget; // where to jump when exiting the method
401 private MethodGen.PhantomTarget defaultTarget; // the default switch target (throws exn)
402 private MethodGen.PhantomTarget[] insnTargets; // the targets for each jumpable instruction
403 private MethodGen mg; // the method itself
405 private boolean jumpable(int addr) { return jumpableAddresses.get(new Integer(addr)) != null; }
407 private void emitText(int addr, DataInputStream dis, int size) throws Exn,IOException {
408 if(textDone) throw new Exn("Multiple text segments");
411 if((addr&3)!=0 || (size&3)!=0) throw new Exn("Section on weird boundaries");
413 int insn,nextInsn=-1;
414 boolean skipNext = true;
416 for(int i=0;i<count;i++,addr+=4) {
417 insn = skipNext ? dis.readInt() : nextInsn;
418 nextInsn = (i == count-1) ? -1 : dis.readInt();
419 if(addr >= endOfMethod) { endMethod(addr); startMethod(addr); }
420 if(insnTargets[(addr-startOfMethod)/4] != null) {
421 insnTargets[(addr-startOfMethod)/4].setTarget(mg.size());
423 } else if(unreachable) {
427 skipNext = emitInstruction(addr,insn,nextInsn);
428 } catch(RuntimeException e) {
429 warn.println("Exception at " + toHex(addr));
432 if(skipNext) { addr+=4; i++; }
439 private void startMethod(int first) {
440 startOfMethod = first & methodMask;
441 endOfMethod = startOfMethod + maxBytesPerMethod;
443 mg = cg.addMethod("run_" + toHex(startOfMethod),Type.VOID,Type.NO_ARGS,ACC_PRIVATE|ACC_FINAL);
446 mg.add(GETFIELD,new FieldRef(me,"page",Type.arrayType(Type.INT)));
450 mg.add(GETFIELD,new FieldRef(me,"readPages",Type.arrayType(Type.INT,2)));
453 mg.add(GETFIELD,new FieldRef(me,"writePages",Type.arrayType(Type.INT,2)));
457 returnTarget = new MethodGen.PhantomTarget();
458 insnTargets = new MethodGen.PhantomTarget[maxBytesPerMethod/4];
460 int[] buf = new int[maxBytesPerMethod/4];
461 Object[] targetBuf = new Object[maxBytesPerMethod/4];
463 for(int addr=first;addr<endOfMethod;addr+=4) {
465 targetBuf[n] = insnTargets[(addr-startOfMethod)/4] = new MethodGen.PhantomTarget();
471 MethodGen.LSI lsi = new MethodGen.LSI(n);
472 System.arraycopy(buf,0,lsi.vals,0,n);
473 System.arraycopy(targetBuf,0,lsi.targets,0,n);
474 lsi.setDefaultTarget(defaultTarget = new MethodGen.PhantomTarget());
479 mg.add(GETFIELD,new FieldRef(me,"pc",Type.INT));
480 mg.add(LOOKUPSWITCH,lsi);
483 private void endMethod(int firstAddrOfNext) {
484 if(startOfMethod == 0) return;
488 mg.add(LDC,firstAddrOfNext);
490 // mark the start of the next method as jumpable
491 jumpableAddresses.put(new Integer(firstAddrOfNext),Boolean.TRUE);
494 returnTarget.setTarget(mg.size());
500 defaultTarget.setTarget(mg.size());
503 mg.add(NEW,new Type.Object("org.ibex.nestedvm.Runtime$ExecutionException"));
505 mg.add(NEW,Type.STRINGBUFFER);
507 mg.add(LDC,"Jumped to invalid address: ");
508 mg.add(INVOKESPECIAL,new MethodRef(Type.STRINGBUFFER,"<init>",Type.VOID,new Type[]{Type.STRING}));
510 mg.add(GETFIELD,new FieldRef(me,"pc",Type.INT));
511 mg.add(INVOKEVIRTUAL,new MethodRef(Type.STRINGBUFFER,"append",Type.STRINGBUFFER,new Type[]{Type.INT}));
512 mg.add(INVOKEVIRTUAL,new MethodRef(Type.STRINGBUFFER,"toString",Type.STRING,Type.NO_ARGS));
513 mg.add(INVOKESPECIAL,new MethodRef(new Type.Object("org.ibex.nestedvm.Runtime$ExecutionException"),"<init>",Type.VOID,new Type[]{Type.STRING}));
516 mg.add(NEW,new Type.Object("org.ibex.nestedvm.Runtime$ExecutionException"));
518 mg.add(LDC,"Jumped to invalid address");
519 mg.add(INVOKESPECIAL,new MethodRef(new Type.Object("org.ibex.nestedvm.Runtime$ExecutionException"),"<init>",Type.VOID,new Type[]{Type.STRING}));
523 endOfMethod = startOfMethod = 0;
527 private void leaveMethod() {
528 mg.add(GOTO,returnTarget);
531 private void link(int mypc) {
534 int ref = (mypc+8 + 32768) & ~65535;
535 int diff = (mypc+8) - ref;
536 if(diff < -32768 || diff > 32767) throw new Error("should never happen " + diff);
546 private void branch(int pc, int target) {
547 if((pc&methodMask) == (target&methodMask)) {
548 mg.add(GOTO,insnTargets[(target-startOfMethod)/4]);
557 // This assumes everything needed by ifInsn is already on the stack
558 private boolean doIfInstruction(byte op, int pc, int target, int nextInsn) throws Exn {
559 emitInstruction(-1,nextInsn,-1); // delay slot
560 if((target&methodMask) == (pc&methodMask)) {
561 mg.add(op,insnTargets[(target-startOfMethod)/4]);
563 int h = mg.add(MethodGen.negate(op));
565 mg.setArg(h,mg.size());
567 if(!jumpable(pc+4)) return true; // done - skip it
569 //System.err.println("Delay slot is jumpable - This code is untested + " + toHex(nextInsn));
570 if(pc+4==endOfMethod) {
571 // the delay slot is at the start of the next method
572 jumpableAddresses.put(new Integer(pc+8),Boolean.TRUE); // make the 2nd insn of the next method jumpable
573 branch(pc,pc+8); // jump over it
574 //System.err.println("delay slot: " + toHex(pc+8)); */
576 return false; // we still need to output it
578 //System.err.println("jumped over delay slot: " + toHex(pc+4));
579 // add another copy and jump over
581 int b = mg.add(GOTO);
582 insnTargets[(pc+4-startOfMethod)/4].setTarget(mg.size());
583 emitInstruction(-1,nextInsn,01); // delay slot
584 mg.setArg(b,mg.size());
590 private static final Float POINT_5_F = new Float(0.5f);
591 private static final Double POINT_5_D = new Double(0.5f);
592 private static final Long FFFFFFFF = new Long(0xffffffffL);
594 private boolean emitInstruction(int pc, int insn, int nextInsn) throws Exn {
595 MethodGen mg = this.mg; // smaller bytecode
596 if(insn == -1) throw new Exn("insn is -1");
598 int op = (insn >>> 26) & 0xff; // bits 26-31
599 int rs = (insn >>> 21) & 0x1f; // bits 21-25
600 int rt = (insn >>> 16) & 0x1f; // bits 16-20
601 int ft = (insn >>> 16) & 0x1f;
602 int rd = (insn >>> 11) & 0x1f; // bits 11-15
603 int fs = (insn >>> 11) & 0x1f;
604 int shamt = (insn >>> 6) & 0x1f; // bits 6-10
605 int fd = (insn >>> 6) & 0x1f;
606 int subcode = insn & 0x3f; // bits 0-5
607 int breakCode = (insn >>> 6) & 0xfffff; // bits 6-20
609 int jumpTarget = (insn & 0x03ffffff); // bits 0-25
610 int unsignedImmediate = insn & 0xffff;
611 int signedImmediate = (insn << 16) >> 16;
612 int branchTarget = signedImmediate;
664 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
665 emitInstruction(-1,nextInsn,-1);
673 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
674 emitInstruction(-1,nextInsn,-1);
687 // FEATURE: This is actually broken, but it happens to work for our code
688 // a func could theoretically jump back to here from a future point
689 restoreChangedRegs();
700 mg.add(INVOKEVIRTUAL,new MethodRef(me,"syscall",Type.INT,new Type[]{Type.INT,Type.INT,Type.INT,Type.INT,Type.INT,Type.INT,Type.INT}));
704 mg.add(GETFIELD,new FieldRef(me,"state",Type.INT));
710 mg.setArg(b1,mg.size());
713 mg.add(NEW,new Type.Object("org.ibex.nestedvm.Runtime$ExecutionException"));
715 mg.add(LDC,"BREAK Code " + toHex(breakCode));
716 mg.add(INVOKESPECIAL,new MethodRef(new Type.Object("org.ibex.nestedvm.Runtime$ExecutionException"),"<init>",Type.VOID,new Type[]{Type.STRING}));
750 mg.add(SWAP); //a(InstructionConstants.SWAP);
757 mg.add(SWAP); //a(InstructionConstants.SWAP);
764 mg.add(LDC,FFFFFFFF);
768 mg.add(LDC,FFFFFFFF);
810 mg.add(LDC,FFFFFFFF);
815 mg.add(LDC,FFFFFFFF);
832 mg.setArg(b1,mg.size());
837 throw new Exn("ADD (add with oveflow trap) not suported");
840 if(rt != 0 && rs != 0) {
852 throw new Exn("SUB (add with oveflow trap) not suported");
855 if(rt != 0 && rs != 0) {
890 if(rs != 0 || rt != 0) {
891 if(rs != 0 && rt != 0) {
912 b1 = mg.add(IF_ICMPLT);
915 mg.setArg(b1,mg.add(ICONST_1));
916 mg.setArg(b2,mg.size());
928 mg.add(LDC,FFFFFFFF);
932 mg.add(LDC,FFFFFFFF);
942 mg.setArg(b1,mg.add(ICONST_1));
943 mg.setArg(b2,mg.size());
950 throw new Exn("Illegal instruction 0/" + subcode);
957 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
959 return doIfInstruction(IFLT,pc,pc+branchTarget*4+4,nextInsn);
961 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
963 return doIfInstruction(IFGE,pc,pc+branchTarget*4+4,nextInsn);
965 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
968 emitInstruction(-1,nextInsn,-1);
970 branch(pc,pc+branchTarget*4+4);
971 mg.setArg(b1,mg.size());
974 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
976 if(rs != 0) { // r0 is always >= 0
980 emitInstruction(-1,nextInsn,-1);
982 branch(pc,pc+branchTarget*4+4);
983 if(b1 != -1) mg.setArg(b1,mg.size());
984 if(b1 == -1) unreachable = true;
987 throw new Exn("Illegal Instruction 1/" + rt);
992 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
993 emitInstruction(-1,nextInsn,-1);
994 branch(pc,(pc&0xf0000000)|(jumpTarget << 2));
999 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
1000 int target = (pc&0xf0000000)|(jumpTarget << 2);
1001 emitInstruction(-1,nextInsn,-1);
1008 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
1010 emitInstruction(-1,nextInsn,-1);
1011 branch(pc,pc+branchTarget*4+4);
1013 } else if(rs == 0 || rt == 0) {
1014 pushReg(rt == 0 ? R+rs : R+rt);
1015 return doIfInstruction(IFEQ,pc,pc+branchTarget*4+4,nextInsn);
1019 return doIfInstruction(IF_ICMPEQ,pc,pc+branchTarget*4+4,nextInsn);
1023 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
1026 return doIfInstruction(IFNE,pc,pc+branchTarget*4+4,nextInsn);
1029 return doIfInstruction(IF_ICMPNE,pc,pc+branchTarget*4+4,nextInsn);
1032 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
1034 return doIfInstruction(IFLE,pc,pc+branchTarget*4+4,nextInsn);
1036 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
1038 return doIfInstruction(IFGT,pc,pc+branchTarget*4+4,nextInsn);
1040 throw new Exn("ADDI (add immediate with oveflow trap) not suported");
1043 addiu(rs,signedImmediate);
1049 mg.add(LDC,signedImmediate);
1050 b1 = mg.add(IF_ICMPLT);
1053 mg.setArg(b1,mg.add(ICONST_1));
1054 mg.setArg(b2,mg.size());
1061 mg.add(LDC,FFFFFFFF);
1063 // Yes, this is correct, you have to sign extend the immediate then do an UNSIGNED comparison
1064 mg.add(LDC,new Long(signedImmediate&0xffffffffL));
1070 mg.setArg(b1,mg.add(ICONST_1));
1071 mg.setArg(b2,mg.size());
1077 mg.add(LDC,unsignedImmediate);
1083 if(rs != 0 && unsignedImmediate != 0) {
1085 mg.add(LDC,unsignedImmediate);
1090 mg.add(LDC,unsignedImmediate);
1097 mg.add(LDC,unsignedImmediate);
1103 mg.add(LDC,unsignedImmediate << 16);
1107 throw new Exn("TLB/Exception support not implemented");
1116 if(fs != 31) throw new Exn("FCR " + fs + " unavailable");
1130 if(fs != 31) throw new Exn("FCR " + fs + " unavailable");
1135 case 8: {// BC1F, BC1T
1137 mg.add(LDC,0x800000);
1139 return doIfInstruction(((insn>>>16)&1) == 0 ? IFEQ : IFNE,pc,pc+branchTarget*4+4,nextInsn);
1143 { // Single/Double math
1144 boolean d = rs == 17;
1147 preSetDouble(F+fd,d);
1150 mg.add(d ? DADD : FADD);
1154 preSetDouble(F+fd,d);
1157 mg.add(d ? DSUB : FSUB);
1161 preSetDouble(F+fd,d);
1164 mg.add(d ? DMUL : FMUL);
1168 preSetDouble(F+fd,d);
1171 mg.add(d ? DDIV : FDIV);
1175 preSetDouble(F+fd,d);
1176 // NOTE: We can't use fneg/dneg here since they'll turn +0.0 into -0.0
1179 mg.add(d ? DUP2 : DUP);
1180 mg.add(d ? DCONST_0 : FCONST_0);
1181 mg.add(d ? DCMPG : FCMPG);
1184 mg.add(d ? DCONST_0 : FCONST_0);
1191 mg.add(d ? DSUB : FSUB);
1193 mg.setArg(b1,mg.size());
1210 preSetDouble(F+fd,d);
1212 mg.add(d ? DNEG : FNEG);
1227 case 36: { // CVT.W.D
1228 MethodGen.TSI tsi = new MethodGen.TSI(0,3);
1234 mg.add(TABLESWITCH,tsi);
1236 // Round towards plus infinity
1237 tsi.setTarget(2,mg.size());
1238 if(!d) mg.add(F2D); // Ugh.. java.lang.Math doesn't have a float ceil/floor
1239 mg.add(INVOKESTATIC,new MethodRef("java.lang.Math","ceil",Type.DOUBLE,new Type[]{Type.DOUBLE}));
1244 tsi.setTarget(0,mg.size());
1245 mg.add(LDC,d ? (Object)POINT_5_D : (Object)POINT_5_F);
1246 mg.add(d ? DADD : FADD);
1249 // Round towards minus infinity
1250 tsi.setTarget(3,mg.size());
1252 mg.add(INVOKESTATIC,new MethodRef("java.lang.Math","floor",Type.DOUBLE,new Type[]{Type.DOUBLE}));
1255 tsi.setTarget(1,mg.size());
1256 tsi.setDefaultTarget(mg.size());
1257 mg.setArg(b1,mg.size());
1259 mg.add(d ? D2I : F2I);
1269 mg.add(LDC,~0x800000);
1273 mg.add(d ? DCMPG : FCMPG);
1275 case 50: b1 = mg.add(IFNE); break;
1276 case 60: b1 = mg.add(IFGE); break;
1277 case 62: b1 = mg.add(IFGT); break;
1280 mg.add(LDC,0x800000);
1282 mg.setArg(b1,mg.size());
1285 default: throw new Exn("Invalid Instruction 17/" + rs + "/" + subcode);
1289 case 20: { // Integer
1303 default: throw new Exn("Invalid Instruction 17/" + rs + "/" + subcode);
1308 throw new Exn("Invalid Instruction 17/" + rs);
1313 throw new Exn("coprocessor 2 and 3 instructions not available");
1316 addiu(R+rs,signedImmediate);
1336 addiu(R+rs,signedImmediate);
1356 addiu(R+rs,signedImmediate);
1360 mg.add(LDC,0x00ffffff);
1391 memRead(R+rs,signedImmediate);
1396 addiu(R+rs,signedImmediate);
1417 addiu(R+rs,signedImmediate);
1432 // chars are unsigend so this works
1439 addiu(R+rs,signedImmediate);
1443 mg.add(LDC,0xffffff00);
1472 addiu(R+rs,signedImmediate);
1479 mg.add(LDC,0xff000000);
1514 addiu(R+rs,signedImmediate);
1554 addiu(R+rs,signedImmediate);
1561 mg.add(LDC,0xffffff00);
1589 preMemWrite2(R+rs,signedImmediate);
1594 addiu(R+rs,signedImmediate);
1601 mg.add(LDC,0x00ffffff);
1626 // This need to be atomic if we ever support threads (see SWC0/SC)
1629 memRead(R+rs,signedImmediate);
1635 memRead(R+rs,signedImmediate);
1639 /* This needs to fail (set rt to 0) if the memory location was modified
1640 * between the LL and SC if we ever support threads.
1645 preMemWrite2(R+rs,signedImmediate);
1654 preMemWrite2(R+rs,signedImmediate);
1659 throw new Exn("Invalid Instruction: " + op + " at " + toHex(pc));
1664 // Helper functions for emitText
1666 private static final int R = 0;
1667 private static final int F = 32;
1668 private static final int HI = 64;
1669 private static final int LO = 65;
1670 private static final int FCSR = 66;
1671 private static final int REG_COUNT=67;
1673 private int[] regLocalMapping = new int[REG_COUNT];
1674 private int[] regLocalReadCount = new int[REG_COUNT];
1675 private int[] regLocalWriteCount = new int[REG_COUNT];
1676 private int nextAvailLocal;
1677 private int loadsStart;
1678 private static final int MAX_LOCALS = 4;
1679 private static final int LOAD_LENGTH = 3;
1681 private boolean doLocal(int reg) {
1682 return reg == R+2 || reg == R+3 || reg == R+4 || reg == R+29;
1685 private int getLocalForReg(int reg) {
1686 if(regLocalMapping[reg] != 0) return regLocalMapping[reg];
1687 if(nextAvailLocal == 0) nextAvailLocal = onePage ? 3 : 4;
1688 regLocalMapping[reg] = nextAvailLocal++;
1689 return regLocalMapping[reg];
1692 private void fixupRegsStart() {
1693 for(int i=0;i<REG_COUNT;i++)
1694 regLocalMapping[i] = regLocalReadCount[i] = regLocalWriteCount[i] = 0;
1696 loadsStart = mg.size();
1697 for(int i=0;i<MAX_LOCALS*LOAD_LENGTH;i++)
1701 private void fixupRegsEnd() {
1703 for(int i=0;i<REG_COUNT;i++) {
1704 if(regLocalMapping[i] == 0) continue;
1705 mg.set(p++,ALOAD_0);
1706 mg.set(p++,GETFIELD,new FieldRef(me,regField(i),Type.INT));
1707 mg.set(p++,ISTORE,regLocalMapping[i]);
1709 if(regLocalWriteCount[i] > 0) {
1711 mg.add(ILOAD,regLocalMapping[i]);
1712 mg.add(PUTFIELD,new FieldRef(me,regField(i),Type.INT));
1717 private void restoreChangedRegs() {
1718 for(int i=0;i<REG_COUNT;i++) {
1719 if(regLocalWriteCount[i] > 0) {
1721 mg.add(ILOAD,regLocalMapping[i]);
1722 mg.add(PUTFIELD,new FieldRef(me,regField(i),Type.INT));
1727 private static final String[] regField = {
1728 "r0","r1","r2","r3","r4","r5","r6","r7",
1729 "r8","r9","r10","r11","r12","r13","r14","r15",
1730 "r16","r17","r18","r19","r20","r21","r22","r23",
1731 "r24","r25","r26","r27","r28","r29","r30","r31",
1733 "f0","f1","f2","f3","f4","f5","f6","f7",
1734 "f8","f9","f10","f11","f12","f13","f14","f15",
1735 "f16","f17","f18","f19","f20","f21","f22","f23",
1736 "f24","f25","f26","f27","f28","f29","f30","f31",
1741 private static String regField(int reg) { return regField[reg]; }
1743 private int pushRegWZ(int reg) {
1745 warn.println("Warning: Pushing r0!");
1746 new Exception().printStackTrace(warn);
1748 return pushRegZ(reg);
1751 private int pushRegZ(int reg) {
1752 if(reg == R+0) return mg.add(ICONST_0);
1753 else return pushReg(reg);
1757 private int pushReg(int reg) {
1760 regLocalReadCount[reg]++;
1761 mg.add(ILOAD,getLocalForReg(reg));
1765 mg.add(GETFIELD,new FieldRef(me,regField(reg),Type.INT));
1770 private int preSetRegStackPos;
1771 private int[] preSetRegStack = new int[8];
1773 // This can push ONE or ZERO words to the stack. If it pushed one it returns true
1774 private boolean preSetReg(int reg) {
1775 regField(reg); // just to check for validity
1776 preSetRegStack[preSetRegStackPos] = reg;
1777 preSetRegStackPos++;
1786 private int setReg() {
1787 if(preSetRegStackPos==0) throw new RuntimeException("didn't do preSetReg");
1788 preSetRegStackPos--;
1789 int reg = preSetRegStack[preSetRegStackPos];
1792 mg.add(ISTORE,getLocalForReg(reg));
1793 regLocalWriteCount[reg]++;
1795 mg.add(PUTFIELD,new FieldRef(me,regField(reg),Type.INT));
1800 private int preSetPC() { return mg.add(ALOAD_0); }
1801 private int setPC() {
1802 return mg.add(PUTFIELD,new FieldRef(me,"pc",Type.INT));
1805 //unused - private InstructionHandle pushFloat(int reg) throws CompilationException { return pushDouble(reg,false); }
1806 //unused - private InstructionHandle pushDouble(int reg) throws CompilationException { return pushDouble(reg,true); }
1807 private int pushDouble(int reg, boolean d) throws Exn {
1808 if(reg < F || reg >= F+32) throw new IllegalArgumentException(""+reg);
1811 if(reg == F+31) throw new Exn("Tried to use a double in f31");
1818 mg.add(LDC,FFFFFFFF);
1821 mg.add(INVOKESTATIC,new MethodRef(Type.DOUBLE_OBJECT,"longBitsToDouble",Type.DOUBLE,new Type[]{Type.LONG}));
1824 mg.add(INVOKESTATIC,new MethodRef("java.lang.Float","intBitsToFloat",Type.FLOAT,new Type[]{Type.INT}));
1829 private void preSetFloat(int reg) { preSetDouble(reg,false); }
1830 private void preSetDouble(int reg) { preSetDouble(reg,true); }
1831 private void preSetDouble(int reg, boolean d) { preSetReg(reg); }
1833 private int setFloat() throws Exn { return setDouble(false); }
1834 private int setDouble() throws Exn { return setDouble(true); }
1835 private int setDouble(boolean d) throws Exn {
1836 int reg = preSetRegStack[preSetRegStackPos-1];
1837 if(reg < F || reg >= F+32) throw new IllegalArgumentException(""+reg);
1840 if(reg == F+31) throw new Exn("Tried to use a double in f31");
1841 mg.add(INVOKESTATIC,new MethodRef(Type.DOUBLE_OBJECT,"doubleToLongBits",Type.LONG,new Type[]{Type.DOUBLE}));
1846 if(preSetReg(reg+1))
1850 setReg(); // preSetReg was already done for this by preSetDouble
1852 //h = a(fac.createInvoke("java.lang.Float","floatToRawIntBits",Type.INT,new Type[]{Type.FLOAT},INVOKESTATIC));
1853 mg.add(INVOKESTATIC,new MethodRef(Type.FLOAT_OBJECT,"floatToRawIntBits",Type.INT,new Type[]{Type.FLOAT}));
1859 private void pushTmp() { mg.add(ILOAD_1); }
1860 private void setTmp() { mg.add(ISTORE_1); }
1862 private void addiu(int reg, int offset) {
1863 if(reg != R+0 && offset != 0) {
1867 } else if(reg != R+0) {
1873 private int memWriteStage;
1874 private void preMemWrite1() {
1875 if(memWriteStage!=0) throw new Error("pending preMemWrite1/2");
1885 private void preMemWrite2(int reg, int offset) {
1890 private void preMemWrite2() { preMemWrite2(false); }
1891 private void preMemWrite2(boolean addrInTmp) {
1892 if(memWriteStage!=1) throw new Error("pending preMemWrite2 or no preMemWrite1");
1895 if(nullPointerCheck) {
1899 mg.add(INVOKEVIRTUAL,new MethodRef(me,"nullPointerCheck",Type.VOID,new Type[]{Type.INT}));
1905 } else if(fastMem) {
1908 mg.add(LDC,pageShift);
1917 mg.add(LDC,(pageSize>>2)-1);
1922 // pops an address and value off the stack, sets *addr to value
1923 private void memWrite() {
1924 if(memWriteStage!=2) throw new Error("didn't do preMemWrite1 or preMemWrite2");
1929 } else if(fastMem) {
1932 mg.add(INVOKEVIRTUAL,new MethodRef(me,"unsafeMemWrite",Type.VOID,new Type[]{Type.INT,Type.INT}));
1937 // reads the word at r[reg]+offset
1938 private void memRead(int reg, int offset) {
1944 private boolean didPreMemRead;
1945 private boolean preMemReadDoPreWrite;
1947 private void preMemRead() { preMemRead(false); }
1948 private void preMemRead(boolean preWrite) {
1949 if(didPreMemRead) throw new Error("pending preMemRead");
1950 didPreMemRead = true;
1951 preMemReadDoPreWrite = preWrite;
1955 mg.add(ALOAD,preWrite ? 3 : 2);
1959 // memRead pops an address off the stack, reads the value at that addr, and pushed the value
1960 // preMemRead MUST be called BEFORE the addresses is pushed
1961 private void memRead() { memRead(false); }
1963 private void memRead(boolean addrInTmp) {
1964 if(!didPreMemRead) throw new Error("didn't do preMemRead");
1965 didPreMemRead = false;
1966 if(preMemReadDoPreWrite)
1969 if(nullPointerCheck) {
1973 mg.add(INVOKEVIRTUAL,new MethodRef(me,"nullPointerCheck",Type.VOID,new Type[]{Type.INT}));
1979 if(preMemReadDoPreWrite)
1982 } else if(fastMem) {
1985 mg.add(LDC,pageShift);
1994 mg.add(LDC,(pageSize>>2)-1);
1996 if(preMemReadDoPreWrite)
2001 if(preMemReadDoPreWrite)
2003 mg.add(INVOKEVIRTUAL,new MethodRef(me,"unsafeMemRead",Type.INT,new Type[]{Type.INT}));
2008 // This might come in handy for something else
2009 /*private boolean touchesReg(int insn, int reg) {
2010 if((reg < R+0 || reg >= R+32) && reg != FCSR) throw new IllegalArgumentException(""+reg);
2011 if(reg == R+0) return false; // r0 is never modified
2012 int op = (insn >>> 26) & 0xff; // bits 26-31
2013 int subcode = insn & 0x3f; // bits 0-5
2014 int rd = (insn >>> 11) & 0x1f; // bits 11-15
2015 int rt = (insn >>> 16) & 0x1f; // bits 16-20
2016 int rs = (insn >>> 21) & 0x1f; // bits 21-25
2020 if(subcode >= 0 && subcode <= 7) return reg == R+rd; // Shift ops
2021 if(subcode >= 32 && subcode <= 43) return reg == R+rd; // Other math ops
2022 if(subcode >= 24 && subcode <= 27) return reg == HI || reg == LO; // MULT/DIV
2024 case 13: return false; // BREAK
2027 case 0: return reg == R+rt; // MFC.1
2028 case 2: return reg == R+rt; // CFC.1
2029 case 4: return false; // MTC.1
2030 case 6: return false; // CTC.1
2033 if(subcode == 50 || subcode == 60 || subcode == 62) return reg == FCSR;
2034 return false; // everything else just touches f0-f31
2035 case 20: return false; // Integer - just touches f0-f31
2039 if(op >= 8 && op <= 15) return reg == R+rt; // XXXI instructions
2040 if(op >= 40 && op <= 46) return false; // Memory WRITE ops
2041 if(op == 49) return reg == F+rt; // LWC1
2042 if(op == 57) return false; // SWC1
2045 warn.println("Unknown instruction in touchesReg()- assuming it modifies all regs " + op + " " + subcode);
2046 new Exception().fillInStackTrace().printStackTrace(warn);