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;
9 import org.ibex.nestedvm.util.*;
10 import org.ibex.classgen.*;
12 // FEATURE: Eliminate unnecessary use of SWAP
13 // FEATURE: Put regs in low (<=3) local vars, small classfile size
15 /* FEATURE: Span large binaries across several classfiles
16 * We should be able to do this with no performance penalty
17 * Every method in the inner classes is static and takes the main class as an arg
18 * This makes them look just like methods in the main class because arg1 gets loaded into
22 /* FEATURE: smarter with local regs
23 * Be even smarter with the use of local registers. We need to only load fields into
24 * local regs when they are actually used and only write to fields if the regs could have
25 * changed. This should allow us to put more regs in local vars. Right now putting all used
26 * regs local vars makes code like this slower.
28 * void work(int a, int b) {
35 * Because all the regs used in "real work" are loaded/restored even for fast path
39 public class ClassFileCompiler extends Compiler implements CGConst {
40 private static final boolean OPTIMIZE_CP = true;
42 /** The stream to write the compiled output to */
43 private OutputStream os;
45 private PrintStream warn = System.err;
47 private final Type.Class me;
50 private MethodGen clinit, init;
52 public ClassFileCompiler(String path, String className, OutputStream os) throws IOException { this(new Seekable.File(path),className,os); }
53 public ClassFileCompiler(Seekable binary, String className, OutputStream os) throws IOException {
54 this(binary,className);
55 if(os == null) throw new NullPointerException();
58 public ClassFileCompiler(Seekable binary, String className, File outDir) throws IOException {
59 this(binary,className);
60 if(outDir == null) throw new NullPointerException();
63 private ClassFileCompiler(Seekable binary, String className) throws IOException {
64 super(binary,className);
65 me = Type.Class.instance(fullClassName);
68 public void setWarnWriter(PrintStream warn) { this.warn = warn; }
70 protected void _go() throws Exn, IOException {
73 } catch(ClassFile.Exn e) {
74 e.printStackTrace(warn);
75 throw new Exn("Class generation exception: " + e.toString());
79 private void __go() throws Exn, IOException {
80 if(!pruneCases) throw new Exn("-o prunecases MUST be enabled for ClassFileCompiler");
83 Type.Class superClass = Type.Class.instance(runtimeClass);
84 cg = new ClassFile(me,superClass,PUBLIC|FINAL|SUPER);
85 if(source != null) cg.setSourceFile(source);
88 cg.addField("pc",Type.INT,PRIVATE);
89 cg.addField("hi",Type.INT,PRIVATE);
90 cg.addField("lo",Type.INT,PRIVATE);
91 cg.addField("fcsr",Type.INT,PRIVATE);
92 for(int i=1;i<32;i++) cg.addField("r" + i,Type.INT,PRIVATE);
93 for(int i=0;i<32;i++) cg.addField("f" + i,singleFloat ? Type.FLOAT : Type.INT,PRIVATE);
96 clinit = cg.addMethod("<clinit>",Type.VOID,Type.NO_ARGS,PRIVATE|STATIC);
98 // GCCLASS_HINT: org.ibex.nestedvm.RuntimeCompiler.compile org.ibex.nestedvm.UnixRuntime.<init>
101 init = cg.addMethod("<init>",Type.VOID,Type.NO_ARGS,PUBLIC);
103 init.add(LDC,pageSize);
104 init.add(LDC,totalPages);
105 init.add(INVOKESPECIAL,me.method("<init>",Type.VOID,new Type[]{Type.INT,Type.INT}));
109 init = cg.addMethod("<init>",Type.VOID,new Type[]{Type.BOOLEAN},PUBLIC);
111 init.add(LDC,pageSize);
112 init.add(LDC,totalPages);
114 init.add(INVOKESPECIAL,me.method("<init>",Type.VOID,new Type[]{Type.INT,Type.INT,Type.BOOLEAN}));
118 init = cg.addMethod("<init>",Type.VOID,new Type[]{Type.INT,Type.INT},PUBLIC);
123 init.add(INVOKESPECIAL,me.method("<init>",Type.VOID,new Type[]{Type.INT,Type.INT,Type.BOOLEAN}));
127 init = cg.addMethod("<init>",Type.VOID,new Type[]{Type.INT,Type.INT,Type.BOOLEAN},PUBLIC);
132 init.add(INVOKESPECIAL,superClass.method("<init>",Type.VOID,new Type[]{Type.INT,Type.INT,Type.BOOLEAN}));
135 cg.addField("page",Type.INT.makeArray(),PRIVATE|FINAL);
138 init.add(GETFIELD,me.field("readPages",Type.INT.makeArray(2)));
141 init.add(PUTFIELD,me.field("page",Type.INT.makeArray()));
145 cg.addField("symbols",Type.Class.instance(hashClass),PRIVATE|STATIC|FINAL);
149 for(int i=0;i<elf.sheaders.length;i++) {
150 ELF.SHeader sheader = elf.sheaders[i];
151 String name = sheader.name;
152 // if this section doesn't get loaded into our address space don't worry about it
153 if(sheader.addr == 0x0) continue;
155 highestAddr = Math.max(highestAddr, sheader.addr + sheader.size);
157 if(name.equals(".text"))
158 emitText(sheader.addr, new DataInputStream(sheader.getInputStream()),sheader.size);
159 else if(name.equals(".data") || name.equals(".sdata") || name.equals(".rodata") || name.equals(".ctors") || name.equals(".dtors"))
160 emitData(sheader.addr, new DataInputStream(sheader.getInputStream()), sheader.size,name.equals(".rodata"));
161 else if(name.equals(".bss") || name.equals(".sbss"))
162 emitBSS(sheader.addr,sheader.size);
164 throw new Exn("Unknown segment: " + name);
172 Type.Class hash = Type.Class.instance(hashClass);
173 clinit.add(NEW,hash);
176 clinit.add(INVOKESPECIAL,hash.method("<init>",Type.VOID,Type.NO_ARGS));
177 clinit.add(PUTSTATIC,me.field("symbols",hash));
178 ELF.Symbol[] symbols = elf.getSymtab().symbols;
179 for(int i=0;i<symbols.length;i++) {
180 ELF.Symbol s = symbols[i];
181 if(s.type == ELF.Symbol.STT_FUNC && s.binding == ELF.Symbol.STB_GLOBAL && (s.name.equals("_call_helper") || !s.name.startsWith("_"))) {
183 clinit.add(LDC,s.name);
184 clinit.add(NEW,Type.INTEGER_OBJECT);
186 clinit.add(LDC,s.addr);
187 clinit.add(INVOKESPECIAL,Type.INTEGER_OBJECT.method("<init>",Type.VOID,new Type[]{Type.INT}));
188 clinit.add(INVOKEVIRTUAL,hash.method("put",Type.OBJECT,new Type[]{Type.OBJECT,Type.OBJECT}));
197 ELF.SHeader text = elf.sectionWithName(".text");
200 MethodGen tramp = cg.addMethod("trampoline",Type.VOID,Type.NO_ARGS,PRIVATE);
202 int start = tramp.size();
204 tramp.add(GETFIELD,me.field("state",Type.INT));
205 tramp.add(IFEQ,tramp.size()+2);
210 tramp.add(GETFIELD,me.field("pc",Type.INT));
211 tramp.add(LDC,methodShift);
214 int beg = text.addr >>> methodShift;
215 int end = ((text.addr + text.size + maxBytesPerMethod - 1) >>> methodShift);
217 MethodGen.Switch.Table tsi = new MethodGen.Switch.Table(beg,end-1);
218 tramp.add(TABLESWITCH,tsi);
219 for(int n=beg;n<end;n++) {
220 tsi.setTargetForVal(n,tramp.size());
221 tramp.add(INVOKESPECIAL,me.method("run_"+toHex(n<<methodShift),Type.VOID,Type.NO_ARGS));
222 tramp.add(GOTO,start);
224 tsi.setDefaultTarget(tramp.size());
227 tramp.add(NEW,Type.Class.instance("org.ibex.nestedvm.Runtime$ExecutionException"));
229 tramp.add(NEW, Type.STRINGBUFFER);
231 tramp.add(LDC,"Jumped to invalid address in trampoline (r2: ");
232 tramp.add(INVOKESPECIAL,Type.STRINGBUFFER.method("<init>",Type.VOID,new Type[]{Type.STRING}));
234 tramp.add(GETFIELD, me.field("r2",Type.INT));
235 tramp.add(INVOKEVIRTUAL,Type.STRINGBUFFER.method("append",Type.STRINGBUFFER,new Type[]{Type.INT}));
236 tramp.add(LDC," pc: ");
237 tramp.add(INVOKEVIRTUAL,Type.STRINGBUFFER.method("append",Type.STRINGBUFFER,new Type[]{Type.STRING}));
239 tramp.add(GETFIELD, me.field("pc",Type.INT));
240 tramp.add(INVOKEVIRTUAL,Type.STRINGBUFFER.method("append",Type.STRINGBUFFER,new Type[]{Type.INT}));
242 tramp.add(INVOKEVIRTUAL,Type.STRINGBUFFER.method("append",Type.STRINGBUFFER,new Type[]{Type.STRING}));
243 tramp.add(INVOKEVIRTUAL,Type.STRINGBUFFER.method("toString",Type.STRING,Type.NO_ARGS));
244 // GCCLASS_HINT: org.ibex.nestedvm.RuntimeCompiler.compile org.ibex.nestedvm.Runtime$ExecutionException.<init>
245 tramp.add(INVOKESPECIAL,Type.Class.instance("org.ibex.nestedvm.Runtime$ExecutionException").method("<init>",Type.VOID,new Type[]{Type.STRING}));
248 addConstReturnMethod("gp",gp.addr);
249 addConstReturnMethod("entryPoint",elf.header.entry);
250 addConstReturnMethod("heapStart",highestAddr);
252 if(userInfo != null) {
253 addConstReturnMethod("userInfoBase",userInfo.addr);
254 addConstReturnMethod("userInfoSize",userInfo.size);
258 Type.Class hashClassType = Type.Class.instance(hashClass);
259 MethodGen ls = cg.addMethod("lookupSymbol",Type.INT,new Type[]{Type.STRING},PROTECTED);
260 ls.add(GETSTATIC,me.field("symbols",hashClassType));
262 ls.add(INVOKEVIRTUAL,hashClassType.method("get",Type.OBJECT,new Type[]{Type.OBJECT}));
264 int b = ls.add(IFNULL);
265 ls.add(CHECKCAST,Type.INTEGER_OBJECT);
266 ls.add(INVOKEVIRTUAL,Type.INTEGER_OBJECT.method("intValue",Type.INT,Type.NO_ARGS));
268 ls.setArg(b,ls.size());
274 // Kind of a hack, referencing dup() gets us all the fields for free
275 // GCCLASS_HINT: org.ibex.nestedvm.RuntimeCompiler.compile org.ibex.nestedvm.Runtime$CPUState.dup
276 Type.Class cpuStateType = Type.Class.instance("org.ibex.nestedvm.Runtime$CPUState");
277 MethodGen setCPUState = cg.addMethod("setCPUState",Type.VOID,new Type[]{cpuStateType},PROTECTED);
278 MethodGen getCPUState = cg.addMethod("getCPUState",Type.VOID,new Type[]{cpuStateType},PROTECTED);
280 setCPUState.add(ALOAD_1);
281 getCPUState.add(ALOAD_1);
282 setCPUState.add(GETFIELD,cpuStateType.field("r",Type.INT.makeArray()));
283 getCPUState.add(GETFIELD,cpuStateType.field("r",Type.INT.makeArray()));
284 setCPUState.add(ASTORE_2);
285 getCPUState.add(ASTORE_2);
287 for(int i=1;i<32;i++) {
288 setCPUState.add(ALOAD_0);
289 setCPUState.add(ALOAD_2);
290 setCPUState.add(LDC,i);
291 setCPUState.add(IALOAD);
292 setCPUState.add(PUTFIELD,me.field("r"+i,Type.INT));
294 getCPUState.add(ALOAD_2);
295 getCPUState.add(LDC,i);
296 getCPUState.add(ALOAD_0);
297 getCPUState.add(GETFIELD,me.field("r"+i,Type.INT));
298 getCPUState.add(IASTORE);
301 setCPUState.add(ALOAD_1);
302 getCPUState.add(ALOAD_1);
303 setCPUState.add(GETFIELD,cpuStateType.field("f",Type.INT.makeArray()));
304 getCPUState.add(GETFIELD,cpuStateType.field("f",Type.INT.makeArray()));
305 setCPUState.add(ASTORE_2);
306 getCPUState.add(ASTORE_2);
308 for(int i=0;i<32;i++) {
309 setCPUState.add(ALOAD_0);
310 setCPUState.add(ALOAD_2);
311 setCPUState.add(LDC,i);
312 setCPUState.add(IALOAD);
313 if(singleFloat) setCPUState.add(INVOKESTATIC,Type.FLOAT_OBJECT.method("intBitsToFloat",Type.FLOAT,new Type[]{Type.INT}));
314 setCPUState.add(PUTFIELD,me.field("f"+i,singleFloat ? Type.FLOAT : Type.INT));
316 getCPUState.add(ALOAD_2);
317 getCPUState.add(LDC,i);
318 getCPUState.add(ALOAD_0);
319 getCPUState.add(GETFIELD,me.field("f"+i,singleFloat ? Type.FLOAT: Type.INT));
320 if(singleFloat) getCPUState.add(INVOKESTATIC,Type.FLOAT_OBJECT.method("floatToIntBits",Type.INT,new Type[]{Type.FLOAT}));
321 getCPUState.add(IASTORE);
324 String[] each = new String[] { "hi","lo","fcsr","pc" };
325 for(int i=0;i<each.length;i++) {
326 setCPUState.add(ALOAD_0);
327 setCPUState.add(ALOAD_1);
328 setCPUState.add(GETFIELD,cpuStateType.field(each[i],Type.INT));
329 setCPUState.add(PUTFIELD,me.field(each[i],Type.INT));
331 getCPUState.add(ALOAD_1);
332 getCPUState.add(ALOAD_0);
333 getCPUState.add(GETFIELD,me.field(each[i],Type.INT));
334 getCPUState.add(PUTFIELD,cpuStateType.field(each[i],Type.INT));
336 setCPUState.add(RETURN);
337 getCPUState.add(RETURN);
340 MethodGen execute = cg.addMethod("_execute",Type.VOID,Type.NO_ARGS,PROTECTED);
341 int tryStart = execute.size();
342 execute.add(ALOAD_0);
343 execute.add(INVOKESPECIAL,me.method("trampoline",Type.VOID,Type.NO_ARGS));
344 int tryEnd = execute.size();
347 int catchInsn = execute.size();
348 execute.add(ASTORE_1);
349 execute.add(NEW, Type.Class.instance("org.ibex.nestedvm.Runtime$FaultException"));
351 execute.add(ALOAD_1);
352 // GCCLASS_HINT: org.ibex.nestedvm.RuntimeCompiler.compile org.ibex.nestedvm.Runtime$FaultException.<init>
353 execute.add(INVOKESPECIAL,Type.Class.instance("org.ibex.nestedvm.Runtime$FaultException").method("<init>",Type.VOID,new Type[]{Type.Class.instance("java.lang.RuntimeException")}));
356 execute.addExceptionHandler(tryStart,tryEnd,catchInsn,Type.Class.instance("java.lang.RuntimeException"));
357 execute.addThrow(Type.Class.instance("org.ibex.nestedvm.Runtime$ExecutionException"));
359 MethodGen main = cg.addMethod("main",Type.VOID,new Type[]{Type.STRING.makeArray()},STATIC|PUBLIC);
362 main.add(INVOKESPECIAL,me.method("<init>",Type.VOID,Type.NO_ARGS));
363 main.add(LDC,fullClassName);
366 Type.Class ur = Type.Class.instance("org.ibex.nestedvm.UnixRuntime");
367 // GCCLASS_HINT: org.ibex.nestedvm.RuntimeCompiler.compile org.ibex.nestedvm.Runtime.runAndExec
368 main.add(INVOKESTATIC,ur.method("runAndExec",Type.INT,new Type[]{ur,Type.STRING,Type.STRING.makeArray()}));
370 // GCCLASS_HINT: org.ibex.nestedvm.RuntimeCompiler.compile org.ibex.nestedvm.Runtime.run
371 main.add(INVOKEVIRTUAL,me.method("run",Type.INT,new Type[]{Type.STRING,Type.STRING.makeArray()}));
373 main.add(INVOKESTATIC,Type.Class.instance("java.lang.System").method("exit",Type.VOID,new Type[]{Type.INT}));
377 if(!outDir.isDirectory()) throw new IOException("" + outDir + " isn't a directory");
384 private void addConstReturnMethod(String name, int val) {
385 MethodGen m = cg.addMethod(name,Type.INT,Type.NO_ARGS,PROTECTED);
390 private static int initDataCount;
391 private void emitData(int addr, DataInputStream dis, int size, boolean readOnly) throws Exn,IOException {
392 if((addr&3)!=0 || (size&3)!=0) throw new Exn("Data section on weird boundaries");
393 int last = addr + size;
395 int segSize = Math.min(size,28000); // must be a multiple of 56
396 StringBuffer sb = new StringBuffer();
397 for(int i=0;i<segSize;i+=7) {
399 for(int j=0;j<7;j++) {
401 byte b = (i+j < size) ? dis.readByte() : 1;
405 sb.append((char) ((l>>>(7*(7-j)))&0x7f));
407 String fieldname = "_data" + (++initDataCount);
408 cg.addField(fieldname,Type.INT.makeArray(),PRIVATE|STATIC|FINAL);
410 clinit.add(LDC,sb.toString());
411 clinit.add(LDC,segSize/4);
412 // GCCLASS_HINT: org.ibex.nestedvm.RuntimeCompiler.compile org.ibex.nestedvm.Runtime.decodeData
413 clinit.add(INVOKESTATIC,Type.Class.instance("org.ibex.nestedvm.Runtime").method("decodeData",Type.INT.makeArray(),new Type[]{Type.STRING,Type.INT}));
414 clinit.add(PUTSTATIC,me.field(fieldname,Type.INT.makeArray()));
416 init.add(GETSTATIC,me.field(fieldname,Type.INT.makeArray()));
418 init.add(LDC,readOnly ? 1 : 0);
419 // GCCLASS_HINT: org.ibex.nestedvm.RuntimeCompiler.compile org.ibex.nestedvm.Runtime.initPages
420 init.add(INVOKEVIRTUAL,me.method("initPages",Type.VOID,new Type[]{Type.INT.makeArray(),Type.INT,Type.BOOLEAN}));
428 private void emitBSS(int addr, int size) throws Exn {
429 if((addr&3)!=0) throw new Exn("BSS section on weird boundaries");
436 // GCCLASS_HINT: org.ibex.nestedvm.RuntimeCompiler.compile org.ibex.nestedvm.Runtime.clearPages
437 init.add(INVOKEVIRTUAL,me.method("clearPages",Type.VOID,new Type[]{Type.INT,Type.INT}));
441 private int startOfMethod = 0; // the start of this method (not necessarily the first instruction)
442 private int endOfMethod = 0; // the maximum end of this method (could end before it is full)
444 private MethodGen.PhantomTarget returnTarget; // where to jump when exiting the method
445 private MethodGen.PhantomTarget defaultTarget; // the default switch target (throws exn)
446 private MethodGen.PhantomTarget[] insnTargets; // the targets for each jumpable instruction
447 private MethodGen mg; // the method itself
449 private boolean jumpable(int addr) { return jumpableAddresses.get(new Integer(addr)) != null; }
451 private static final int UNREACHABLE = 1;
452 private static final int SKIP_NEXT = 2;
454 private boolean textDone; // a text segment was already processed
455 private void emitText(int addr, DataInputStream dis, int size) throws Exn,IOException {
456 if(textDone) throw new Exn("Multiple text segments");
459 if((addr&3)!=0 || (size&3)!=0) throw new Exn("Section on weird boundaries");
461 int insn,nextInsn=-1;
463 boolean skipNext = true;
464 boolean unreachable = false;
466 for(int i=0;i<count;i++,addr+=4) {
467 insn = skipNext ? dis.readInt() : nextInsn;
468 nextInsn = (i == count-1) ? -1 : dis.readInt();
469 if(addr >= endOfMethod) { endMethod(addr,unreachable); startMethod(addr); }
470 if(insnTargets[i%maxInsnPerMethod] != null) {
471 insnTargets[i%maxInsnPerMethod].setTarget(mg.size());
473 } else if(unreachable) {
477 int ret = emitInstruction(addr,insn,nextInsn);
478 unreachable = (ret & UNREACHABLE) != 0;
479 skipNext = (ret & SKIP_NEXT) != 0;
481 e.printStackTrace(warn);
482 warn.println("Exception at " + toHex(addr));
484 } catch(RuntimeException e) {
485 warn.println("Exception at " + toHex(addr));
488 if(skipNext) { addr+=4; i++; }
490 endMethod(0,unreachable);
494 private void startMethod(int first) {
495 startOfMethod = first & methodMask;
496 endOfMethod = startOfMethod + maxBytesPerMethod;
498 mg = cg.addMethod("run_" + toHex(startOfMethod),Type.VOID,Type.NO_ARGS,PRIVATE|FINAL);
501 mg.add(GETFIELD,me.field("page",Type.INT.makeArray()));
505 mg.add(GETFIELD,me.field("readPages",Type.INT.makeArray(2)));
508 mg.add(GETFIELD,me.field("writePages",Type.INT.makeArray(2)));
512 returnTarget = new MethodGen.PhantomTarget();
513 insnTargets = new MethodGen.PhantomTarget[maxBytesPerMethod/4];
515 int[] buf = new int[maxBytesPerMethod/4];
516 Object[] targetBuf = new Object[maxBytesPerMethod/4];
518 for(int addr=first;addr<endOfMethod;addr+=4) {
520 targetBuf[n] = insnTargets[(addr-startOfMethod)/4] = new MethodGen.PhantomTarget();
526 MethodGen.Switch.Lookup lsi = new MethodGen.Switch.Lookup(n);
527 System.arraycopy(buf,0,lsi.vals,0,n);
528 System.arraycopy(targetBuf,0,lsi.targets,0,n);
529 lsi.setDefaultTarget(defaultTarget = new MethodGen.PhantomTarget());
534 mg.add(GETFIELD,me.field("pc",Type.INT));
535 mg.add(LOOKUPSWITCH,lsi);
538 private void endMethod(int firstAddrOfNext,boolean unreachable) {
539 if(startOfMethod == 0) return;
543 mg.add(LDC,firstAddrOfNext);
545 // mark the start of the next method as jumpable
546 jumpableAddresses.put(new Integer(firstAddrOfNext),Boolean.TRUE);
549 returnTarget.setTarget(mg.size());
555 defaultTarget.setTarget(mg.size());
558 mg.add(NEW,Type.Class.instance("org.ibex.nestedvm.Runtime$ExecutionException"));
560 mg.add(NEW,Type.STRINGBUFFER);
562 mg.add(LDC,"Jumped to invalid address: ");
563 mg.add(INVOKESPECIAL,Type.STRINGBUFFER.method("<init>",Type.VOID,new Type[]{Type.STRING}));
565 mg.add(GETFIELD,me.field("pc",Type.INT));
566 mg.add(INVOKEVIRTUAL,Type.STRINGBUFFER.method("append",Type.STRINGBUFFER,new Type[]{Type.INT}));
567 mg.add(INVOKEVIRTUAL,Type.STRINGBUFFER.method("toString",Type.STRING,Type.NO_ARGS));
568 mg.add(INVOKESPECIAL,Type.Class.instance("org.ibex.nestedvm.Runtime$ExecutionException").method("<init>",Type.VOID,new Type[]{Type.STRING}));
571 mg.add(NEW,Type.Class.instance("org.ibex.nestedvm.Runtime$ExecutionException"));
573 mg.add(LDC,"Jumped to invalid address");
574 mg.add(INVOKESPECIAL,Type.Class.instance("org.ibex.nestedvm.Runtime$ExecutionException").method("<init>",Type.VOID,new Type[]{Type.STRING}));
578 endOfMethod = startOfMethod = 0;
582 private void leaveMethod() {
583 mg.add(GOTO,returnTarget);
586 private void link(int mypc) {
589 int ref = (mypc+8 + 32768) & ~65535;
590 int diff = (mypc+8) - ref;
591 if(diff < -32768 || diff > 32767) throw new Error("should never happen " + diff);
601 private void branch(int pc, int target) {
602 if((pc&methodMask) == (target&methodMask)) {
603 mg.add(GOTO,insnTargets[(target-startOfMethod)/4]);
612 // This assumes everything needed by ifInsn is already on the stack
613 private int doIfInstruction(byte op, int pc, int target, int nextInsn) throws Exn {
614 emitInstruction(-1,nextInsn,-1); // delay slot
615 if((target&methodMask) == (pc&methodMask)) {
616 mg.add(op,insnTargets[(target-startOfMethod)/4]);
618 int h = mg.add(MethodGen.negate(op));
620 mg.setArg(h,mg.size());
622 if(!jumpable(pc+4)) return SKIP_NEXT; // done - skip it
624 //System.err.println("Delay slot is jumpable - This code is untested + " + toHex(nextInsn));
625 if(pc+4==endOfMethod) {
626 // the delay slot is at the start of the next method
627 jumpableAddresses.put(new Integer(pc+8),Boolean.TRUE); // make the 2nd insn of the next method jumpable
628 branch(pc,pc+8); // jump over it
629 //System.err.println("delay slot: " + toHex(pc+8)); */
630 //unreachable = true;
631 //return false; // we still need to output it
634 //System.err.println("jumped over delay slot: " + toHex(pc+4));
635 // add another copy and jump over
637 int b = mg.add(GOTO);
638 insnTargets[(pc+4-startOfMethod)/4].setTarget(mg.size());
639 emitInstruction(-1,nextInsn,01); // delay slot
640 mg.setArg(b,mg.size());
646 private static final Float POINT_5_F = new Float(0.5f);
647 private static final Double POINT_5_D = new Double(0.5f);
648 private static final Long FFFFFFFF = new Long(0xffffffffL);
650 private int emitInstruction(int pc, int insn, int nextInsn) throws Exn {
651 MethodGen mg = this.mg; // smaller bytecode
652 if(insn == -1) throw new Exn("insn is -1");
656 int op = (insn >>> 26) & 0xff; // bits 26-31
657 int rs = (insn >>> 21) & 0x1f; // bits 21-25
658 int rt = (insn >>> 16) & 0x1f; // bits 16-20
659 int ft = (insn >>> 16) & 0x1f;
660 int rd = (insn >>> 11) & 0x1f; // bits 11-15
661 int fs = (insn >>> 11) & 0x1f;
662 int shamt = (insn >>> 6) & 0x1f; // bits 6-10
663 int fd = (insn >>> 6) & 0x1f;
664 int subcode = insn & 0x3f; // bits 0-5
665 int breakCode = (insn >>> 6) & 0xfffff; // bits 6-20
667 int jumpTarget = (insn & 0x03ffffff); // bits 0-25
668 int unsignedImmediate = insn & 0xffff;
669 int signedImmediate = (insn << 16) >> 16;
670 int branchTarget = signedImmediate;
722 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
723 emitInstruction(-1,nextInsn,-1);
731 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
732 emitInstruction(-1,nextInsn,-1);
745 // FEATURE: This is actually broken, but it happens to work for our code
746 // a func could theoretically jump back to here from a future point
747 restoreChangedRegs();
758 // GCCLASS_HINT: org.ibex.nestedvm.RuntimeCompiler.compile org.ibex.nestedvm.Runtime.syscall
759 mg.add(INVOKEVIRTUAL,me.method("syscall",Type.INT,new Type[]{Type.INT,Type.INT,Type.INT,Type.INT,Type.INT,Type.INT,Type.INT}));
763 mg.add(GETFIELD,me.field("state",Type.INT));
769 mg.setArg(b1,mg.size());
772 mg.add(NEW,Type.Class.instance("org.ibex.nestedvm.Runtime$ExecutionException"));
774 mg.add(LDC,"BREAK Code " + toHex(breakCode));
775 mg.add(INVOKESPECIAL,Type.Class.instance("org.ibex.nestedvm.Runtime$ExecutionException").method("<init>",Type.VOID,new Type[]{Type.STRING}));
809 mg.add(SWAP); //a(InstructionConstants.SWAP);
816 mg.add(SWAP); //a(InstructionConstants.SWAP);
823 mg.add(LDC,FFFFFFFF);
827 mg.add(LDC,FFFFFFFF);
869 mg.add(LDC,FFFFFFFF);
874 mg.add(LDC,FFFFFFFF);
891 mg.setArg(b1,mg.size());
896 throw new Exn("ADD (add with oveflow trap) not suported");
899 if(rt != 0 && rs != 0) {
911 throw new Exn("SUB (add with oveflow trap) not suported");
914 if(rt != 0 && rs != 0) {
949 if(rs != 0 || rt != 0) {
950 if(rs != 0 && rt != 0) {
971 b1 = mg.add(IF_ICMPLT);
974 mg.setArg(b1,mg.add(ICONST_1));
975 mg.setArg(b2,mg.size());
987 mg.add(LDC,FFFFFFFF);
991 mg.add(LDC,FFFFFFFF);
1001 mg.setArg(b1,mg.add(ICONST_1));
1002 mg.setArg(b2,mg.size());
1009 throw new Exn("Illegal instruction 0/" + subcode);
1016 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
1018 return doIfInstruction(IFLT,pc,pc+branchTarget*4+4,nextInsn);
1020 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
1022 return doIfInstruction(IFGE,pc,pc+branchTarget*4+4,nextInsn);
1024 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
1027 emitInstruction(-1,nextInsn,-1);
1029 branch(pc,pc+branchTarget*4+4);
1030 mg.setArg(b1,mg.size());
1033 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
1035 if(rs != 0) { // r0 is always >= 0
1039 emitInstruction(-1,nextInsn,-1);
1041 branch(pc,pc+branchTarget*4+4);
1042 if(b1 != -1) mg.setArg(b1,mg.size());
1043 if(b1 == -1) ret |= UNREACHABLE;
1046 throw new Exn("Illegal Instruction 1/" + rt);
1051 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
1052 emitInstruction(-1,nextInsn,-1);
1053 branch(pc,(pc&0xf0000000)|(jumpTarget << 2));
1058 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
1059 int target = (pc&0xf0000000)|(jumpTarget << 2);
1060 emitInstruction(-1,nextInsn,-1);
1067 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
1069 emitInstruction(-1,nextInsn,-1);
1070 branch(pc,pc+branchTarget*4+4);
1072 } else if(rs == 0 || rt == 0) {
1073 pushReg(rt == 0 ? R+rs : R+rt);
1074 return doIfInstruction(IFEQ,pc,pc+branchTarget*4+4,nextInsn);
1078 return doIfInstruction(IF_ICMPEQ,pc,pc+branchTarget*4+4,nextInsn);
1082 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
1085 return doIfInstruction(IFNE,pc,pc+branchTarget*4+4,nextInsn);
1088 return doIfInstruction(IF_ICMPNE,pc,pc+branchTarget*4+4,nextInsn);
1091 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
1093 return doIfInstruction(IFLE,pc,pc+branchTarget*4+4,nextInsn);
1095 if(pc == -1) throw new Exn("pc modifying insn in delay slot");
1097 return doIfInstruction(IFGT,pc,pc+branchTarget*4+4,nextInsn);
1099 throw new Exn("ADDI (add immediate with oveflow trap) not suported");
1101 if(rs != 0 && signedImmediate != 0 && rs == rt && doLocal(rt) && signedImmediate >= -32768 && signedImmediate <= 32767) {
1102 // HACK: This should be a little cleaner
1103 regLocalWritten[rt] = true;
1104 mg.add(IINC, new MethodGen.Pair(getLocalForReg(rt),signedImmediate));
1107 addiu(rs,signedImmediate);
1114 mg.add(LDC,signedImmediate);
1115 b1 = mg.add(IF_ICMPLT);
1118 mg.setArg(b1,mg.add(ICONST_1));
1119 mg.setArg(b2,mg.size());
1126 mg.add(LDC,FFFFFFFF);
1128 // Yes, this is correct, you have to sign extend the immediate then do an UNSIGNED comparison
1129 mg.add(LDC,new Long(signedImmediate&0xffffffffL));
1135 mg.setArg(b1,mg.add(ICONST_1));
1136 mg.setArg(b2,mg.size());
1142 mg.add(LDC,unsignedImmediate);
1148 if(rs != 0 && unsignedImmediate != 0) {
1150 mg.add(LDC,unsignedImmediate);
1155 mg.add(LDC,unsignedImmediate);
1162 mg.add(LDC,unsignedImmediate);
1168 mg.add(LDC,unsignedImmediate << 16);
1172 throw new Exn("TLB/Exception support not implemented");
1181 if(fs != 31) throw new Exn("FCR " + fs + " unavailable");
1188 if(rt != 0) pushReg(R+rt);
1189 else mg.add(ICONST_0);
1193 if(fs != 31) throw new Exn("FCR " + fs + " unavailable");
1198 case 8: {// BC1F, BC1T
1200 mg.add(LDC,0x800000);
1202 return doIfInstruction(((insn>>>16)&1) == 0 ? IFEQ : IFNE,pc,pc+branchTarget*4+4,nextInsn);
1206 { // Single/Double math
1207 boolean d = rs == 17;
1210 preSetDouble(F+fd,d);
1213 mg.add(d ? DADD : FADD);
1217 preSetDouble(F+fd,d);
1220 mg.add(d ? DSUB : FSUB);
1224 preSetDouble(F+fd,d);
1227 mg.add(d ? DMUL : FMUL);
1231 preSetDouble(F+fd,d);
1234 mg.add(d ? DDIV : FDIV);
1238 preSetDouble(F+fd,d);
1239 // NOTE: We can't use fneg/dneg here since they'll turn +0.0 into -0.0
1242 mg.add(d ? DUP2 : DUP);
1243 mg.add(d ? DCONST_0 : FCONST_0);
1244 mg.add(d ? DCMPG : FCMPG);
1247 mg.add(d ? DCONST_0 : FCONST_0);
1254 mg.add(d ? DSUB : FSUB);
1256 mg.setArg(b1,mg.size());
1272 preSetDouble(F+fd,d);
1274 mg.add(d ? DNEG : FNEG);
1289 case 36: { // CVT.W.D
1290 MethodGen.Switch.Table tsi = new MethodGen.Switch.Table(0,3);
1296 mg.add(TABLESWITCH,tsi);
1298 // Round towards plus infinity
1299 tsi.setTarget(2,mg.size());
1300 if(!d) mg.add(F2D); // Ugh.. java.lang.Math doesn't have a float ceil/floor
1301 mg.add(INVOKESTATIC,Type.Class.instance("java.lang.Math").method("ceil",Type.DOUBLE,new Type[]{Type.DOUBLE}));
1306 tsi.setTarget(0,mg.size());
1307 mg.add(LDC,d ? (Object)POINT_5_D : (Object)POINT_5_F);
1308 mg.add(d ? DADD : FADD);
1311 // Round towards minus infinity
1312 tsi.setTarget(3,mg.size());
1314 mg.add(INVOKESTATIC,Type.Class.instance("java.lang.Math").method("floor",Type.DOUBLE,new Type[]{Type.DOUBLE}));
1317 tsi.setTarget(1,mg.size());
1318 tsi.setDefaultTarget(mg.size());
1319 mg.setArg(b1,mg.size());
1321 mg.add(d ? D2I : F2I);
1331 mg.add(LDC,~0x800000);
1335 mg.add(d ? DCMPG : FCMPG);
1337 case 50: b1 = mg.add(IFNE); break;
1338 case 60: b1 = mg.add(IFGE); break;
1339 case 62: b1 = mg.add(IFGT); break;
1342 mg.add(LDC,0x800000);
1344 mg.setArg(b1,mg.size());
1347 default: throw new Exn("Invalid Instruction 17/" + rs + "/" + subcode);
1351 case 20: { // Integer
1365 default: throw new Exn("Invalid Instruction 17/" + rs + "/" + subcode);
1370 throw new Exn("Invalid Instruction 17/" + rs);
1375 throw new Exn("coprocessor 2 and 3 instructions not available");
1378 addiu(R+rs,signedImmediate);
1398 addiu(R+rs,signedImmediate);
1418 addiu(R+rs,signedImmediate);
1422 mg.add(LDC,0x00ffffff);
1453 memRead(R+rs,signedImmediate);
1458 addiu(R+rs,signedImmediate);
1479 addiu(R+rs,signedImmediate);
1494 // chars are unsigend so this works
1501 addiu(R+rs,signedImmediate);
1505 mg.add(LDC,0xffffff00);
1534 addiu(R+rs,signedImmediate);
1541 mg.add(LDC,0xff000000);
1576 addiu(R+rs,signedImmediate);
1616 addiu(R+rs,signedImmediate);
1623 mg.add(LDC,0xffffff00);
1651 preMemWrite2(R+rs,signedImmediate);
1656 addiu(R+rs,signedImmediate);
1663 mg.add(LDC,0x00ffffff);
1688 // This need to be atomic if we ever support threads (see SWC0/SC)
1691 memRead(R+rs,signedImmediate);
1697 memRead(R+rs,signedImmediate);
1701 /* This needs to fail (set rt to 0) if the memory location was modified
1702 * between the LL and SC if we ever support threads.
1707 preMemWrite2(R+rs,signedImmediate);
1716 preMemWrite2(R+rs,signedImmediate);
1721 throw new Exn("Invalid Instruction: " + op + " at " + toHex(pc));
1726 // Helper functions for emitText
1728 private static final int R = 0;
1729 private static final int F = 32;
1730 private static final int HI = 64;
1731 private static final int LO = 65;
1732 private static final int FCSR = 66;
1733 private static final int REG_COUNT=67;
1734 private static final String[] regField = {
1735 "r0","r1","r2","r3","r4","r5","r6","r7",
1736 "r8","r9","r10","r11","r12","r13","r14","r15",
1737 "r16","r17","r18","r19","r20","r21","r22","r23",
1738 "r24","r25","r26","r27","r28","r29","r30","r31",
1739 "f0","f1","f2","f3","f4","f5","f6","f7",
1740 "f8","f9","f10","f11","f12","f13","f14","f15",
1741 "f16","f17","f18","f19","f20","f21","f22","f23",
1742 "f24","f25","f26","f27","f28","f29","f30","f31",
1745 private static final int MAX_LOCALS = 4; // doLocal can return true for this many regs
1746 private static final int LOAD_LENGTH = 3; // number of instructions needed to load a field to a reg
1748 // Local register state info
1749 private int[] regLocalMapping = new int[REG_COUNT];
1750 private boolean[] regLocalWritten = new boolean[REG_COUNT];
1751 private int nextAvailLocal;
1752 private int loadsStart;
1754 private boolean doLocal(int reg) {
1755 return reg == R+2 || reg == R+3 || reg == R+4 || reg == R+29;
1758 private int getLocalForReg(int reg) {
1759 if(regLocalMapping[reg] != 0) return regLocalMapping[reg];
1760 regLocalMapping[reg] = nextAvailLocal++;
1761 return regLocalMapping[reg];
1764 private void fixupRegsStart() {
1765 for(int i=0;i<REG_COUNT;i++) {
1766 regLocalMapping[i] = 0;
1767 regLocalWritten[i] = false;
1769 nextAvailLocal = onePage ? 4 : 5;
1770 loadsStart = mg.size();
1771 for(int i=0;i<MAX_LOCALS*LOAD_LENGTH;i++)
1775 private void fixupRegsEnd() {
1777 for(int i=0;i<REG_COUNT;i++) {
1778 if(regLocalMapping[i] == 0) continue;
1779 mg.set(p++,ALOAD_0);
1780 mg.set(p++,GETFIELD,me.field(regField[i],Type.INT));
1781 mg.set(p++,ISTORE,regLocalMapping[i]);
1783 if(regLocalWritten[i]) {
1785 mg.add(ILOAD,regLocalMapping[i]);
1786 mg.add(PUTFIELD,me.field(regField[i],Type.INT));
1791 private void restoreChangedRegs() {
1792 for(int i=0;i<REG_COUNT;i++) {
1793 if(regLocalWritten[i]) {
1795 mg.add(ILOAD,regLocalMapping[i]);
1796 mg.add(PUTFIELD,me.field(regField[i],Type.INT));
1801 private int pushRegWZ(int reg) {
1803 warn.println("Warning: Pushing r0!");
1804 new Exception().printStackTrace(warn);
1806 return pushRegZ(reg);
1809 private int pushRegZ(int reg) {
1810 if(reg == R+0) return mg.add(ICONST_0);
1811 else return pushReg(reg);
1815 private int pushReg(int reg) {
1818 mg.add(ILOAD,getLocalForReg(reg));
1819 } else if(reg >= F+0 && reg <= F+31 && singleFloat) {
1821 mg.add(GETFIELD,me.field(regField[reg],Type.FLOAT));
1822 mg.add(INVOKESTATIC,Type.FLOAT_OBJECT.method("floatToIntBits",Type.INT,new Type[]{Type.FLOAT}));
1825 mg.add(GETFIELD,me.field(regField[reg],Type.INT));
1830 private int preSetRegStackPos;
1831 private int[] preSetRegStack = new int[8];
1833 // This can push ONE or ZERO words to the stack. If it pushed one it returns true
1834 private boolean preSetReg(int reg) {
1835 preSetRegStack[preSetRegStackPos] = reg;
1836 preSetRegStackPos++;
1845 private int setReg() {
1846 if(preSetRegStackPos==0) throw new RuntimeException("didn't do preSetReg");
1847 preSetRegStackPos--;
1848 int reg = preSetRegStack[preSetRegStackPos];
1851 mg.add(ISTORE,getLocalForReg(reg));
1852 regLocalWritten[reg] = true;
1853 } else if(reg >= F+0 && reg <= F+31 && singleFloat) {
1854 mg.add(INVOKESTATIC,Type.FLOAT_OBJECT.method("intBitsToFloat",Type.FLOAT,new Type[]{Type.INT}));
1855 mg.add(PUTFIELD,me.field(regField[reg],Type.FLOAT));
1857 mg.add(PUTFIELD,me.field(regField[reg],Type.INT));
1862 private int preSetPC() { return mg.add(ALOAD_0); }
1863 private int setPC() {
1864 return mg.add(PUTFIELD,me.field("pc",Type.INT));
1867 //unused - private InstructionHandle pushDouble(int reg) throws CompilationException { return pushDouble(reg,true); }
1868 private int pushFloat(int reg) throws Exn { return pushDouble(reg,false); }
1869 private int pushDouble(int reg, boolean d) throws Exn {
1870 if(reg < F || reg >= F+32) throw new IllegalArgumentException(""+reg);
1873 if(singleFloat) throw new Exn("Double operations not supported when singleFloat is enabled");
1874 if(reg == F+31) throw new Exn("Tried to use a double in f31");
1881 mg.add(LDC,FFFFFFFF);
1884 mg.add(INVOKESTATIC,Type.DOUBLE_OBJECT.method("longBitsToDouble",Type.DOUBLE,new Type[]{Type.LONG}));
1885 } else if(singleFloat) {
1887 mg.add(GETFIELD,me.field(regField[reg],Type.FLOAT));
1890 mg.add(INVOKESTATIC,Type.Class.instance("java.lang.Float").method("intBitsToFloat",Type.FLOAT,new Type[]{Type.INT}));
1895 private void preSetFloat(int reg) { preSetDouble(reg,false); }
1896 private void preSetDouble(int reg) { preSetDouble(reg,true); }
1897 private void preSetDouble(int reg, boolean d) { preSetReg(reg); }
1899 private int setFloat() throws Exn { return setDouble(false); }
1900 private int setDouble() throws Exn { return setDouble(true); }
1901 private int setDouble(boolean d) throws Exn {
1902 int reg = preSetRegStack[preSetRegStackPos-1];
1903 if(reg < F || reg >= F+32) throw new IllegalArgumentException(""+reg);
1906 if(singleFloat) throw new Exn("Double operations not supported when singleFloat is enabled");
1907 if(reg == F+31) throw new Exn("Tried to use a double in f31");
1908 mg.add(INVOKESTATIC,Type.DOUBLE_OBJECT.method("doubleToLongBits",Type.LONG,new Type[]{Type.DOUBLE}));
1913 if(preSetReg(reg+1))
1917 setReg(); // preSetReg was already done for this by preSetDouble
1918 } else if(singleFloat) {
1919 // HACK: Clean this up
1920 preSetRegStackPos--;
1921 mg.add(PUTFIELD,me.field(regField[reg],Type.FLOAT));
1923 //h = a(fac.createInvoke("java.lang.Float","floatToRawIntBits",Type.INT,new Type[]{Type.FLOAT},INVOKESTATIC));
1924 mg.add(INVOKESTATIC,Type.FLOAT_OBJECT.method("floatToRawIntBits",Type.INT,new Type[]{Type.FLOAT}));
1930 private void pushTmp() { mg.add(ILOAD_1); }
1931 private void setTmp() { mg.add(ISTORE_1); }
1933 private void addiu(int reg, int offset) {
1934 if(reg != R+0 && offset != 0) {
1938 } else if(reg != R+0) {
1944 private int memWriteStage;
1945 private void preMemWrite1() {
1946 if(memWriteStage!=0) throw new Error("pending preMemWrite1/2");
1956 private void preMemWrite2(int reg, int offset) {
1961 private void preMemWrite2() { preMemWrite2(false); }
1962 private void preMemWrite2(boolean addrInTmp) {
1963 if(memWriteStage!=1) throw new Error("pending preMemWrite2 or no preMemWrite1");
1966 if(nullPointerCheck) {
1970 // GCCLASS_HINT: org.ibex.nestedvm.RuntimeCompiler.compile org.ibex.nestedvm.Runtime.nullPointerCheck
1971 mg.add(INVOKEVIRTUAL,me.method("nullPointerCheck",Type.VOID,new Type[]{Type.INT}));
1977 } else if(fastMem) {
1980 mg.add(LDC,pageShift);
1989 mg.add(LDC,(pageSize>>2)-1);
1994 // pops an address and value off the stack, sets *addr to value
1995 private void memWrite() {
1996 if(memWriteStage!=2) throw new Error("didn't do preMemWrite1 or preMemWrite2");
2001 } else if(fastMem) {
2004 // GCCLASS_HINT: org.ibex.nestedvm.RuntimeCompiler.compile org.ibex.nestedvm.Runtime.unsafeMemWrite
2005 mg.add(INVOKEVIRTUAL,me.method("unsafeMemWrite",Type.VOID,new Type[]{Type.INT,Type.INT}));
2010 // reads the word at r[reg]+offset
2011 private void memRead(int reg, int offset) {
2017 private boolean didPreMemRead;
2018 private boolean preMemReadDoPreWrite;
2020 private void preMemRead() { preMemRead(false); }
2021 private void preMemRead(boolean preWrite) {
2022 if(didPreMemRead) throw new Error("pending preMemRead");
2023 didPreMemRead = true;
2024 preMemReadDoPreWrite = preWrite;
2028 mg.add(ALOAD,preWrite ? 3 : 2);
2032 // memRead pops an address off the stack, reads the value at that addr, and pushed the value
2033 // preMemRead MUST be called BEFORE the addresses is pushed
2034 private void memRead() { memRead(false); }
2036 private void memRead(boolean addrInTmp) {
2037 if(!didPreMemRead) throw new Error("didn't do preMemRead");
2038 didPreMemRead = false;
2039 if(preMemReadDoPreWrite)
2042 if(nullPointerCheck) {
2046 mg.add(INVOKEVIRTUAL,me.method("nullPointerCheck",Type.VOID,new Type[]{Type.INT}));
2052 if(preMemReadDoPreWrite)
2055 } else if(fastMem) {
2058 mg.add(LDC,pageShift);
2067 mg.add(LDC,(pageSize>>2)-1);
2069 if(preMemReadDoPreWrite)
2074 if(preMemReadDoPreWrite)
2076 // GCCLASS_HINT: org.ibex.nestedvm.RuntimeCompiler.compile org.ibex.nestedvm.Runtime.unsafeMemRead
2077 mg.add(INVOKEVIRTUAL,me.method("unsafeMemRead",Type.INT,new Type[]{Type.INT}));
2082 // This might come in handy for something else
2083 /*private boolean touchesReg(int insn, int reg) {
2084 if((reg < R+0 || reg >= R+32) && reg != FCSR) throw new IllegalArgumentException(""+reg);
2085 if(reg == R+0) return false; // r0 is never modified
2086 int op = (insn >>> 26) & 0xff; // bits 26-31
2087 int subcode = insn & 0x3f; // bits 0-5
2088 int rd = (insn >>> 11) & 0x1f; // bits 11-15
2089 int rt = (insn >>> 16) & 0x1f; // bits 16-20
2090 int rs = (insn >>> 21) & 0x1f; // bits 21-25
2094 if(subcode >= 0 && subcode <= 7) return reg == R+rd; // Shift ops
2095 if(subcode >= 32 && subcode <= 43) return reg == R+rd; // Other math ops
2096 if(subcode >= 24 && subcode <= 27) return reg == HI || reg == LO; // MULT/DIV
2098 case 13: return false; // BREAK
2101 case 0: return reg == R+rt; // MFC.1
2102 case 2: return reg == R+rt; // CFC.1
2103 case 4: return false; // MTC.1
2104 case 6: return false; // CTC.1
2107 if(subcode == 50 || subcode == 60 || subcode == 62) return reg == FCSR;
2108 return false; // everything else just touches f0-f31
2109 case 20: return false; // Integer - just touches f0-f31
2113 if(op >= 8 && op <= 15) return reg == R+rt; // XXXI instructions
2114 if(op >= 40 && op <= 46) return false; // Memory WRITE ops
2115 if(op == 49) return reg == F+rt; // LWC1
2116 if(op == 57) return false; // SWC1
2119 warn.println("Unknown instruction in touchesReg()- assuming it modifies all regs " + op + " " + subcode);
2120 new Exception().fillInStackTrace().printStackTrace(warn);