|
|
1.1 root 1: /*
2: * Cisco 7200 (Predator) simulation platform.
3: * Copyright (c) 2005,2006 Christophe Fillot ([email protected])
4: *
5: * MIPS64 JIT compiler.
6: */
7:
8: #include <stdio.h>
9: #include <stdlib.h>
10: #include <unistd.h>
11: #include <string.h>
12: #include <sys/types.h>
13: #include <sys/stat.h>
14: #include <sys/mman.h>
15: #include <signal.h>
16: #include <fcntl.h>
17: #include <assert.h>
18:
19: #include ARCH_INC_FILE
20:
21: #include "rbtree.h"
22: #include "cp0.h"
23: #include "memory.h"
24: #include "cpu.h"
25: #include "device.h"
26: #include "mips64_exec.h"
27: #include "insn_lookup.h"
28: #include "ptask.h"
29:
30: extern rbtree_tree *sym_tree;
31:
32: #if DEBUG_BLOCK_TIMESTAMP
33: static volatile m_uint64_t jit_jiffies = 0;
34: #endif
35:
36: /* MIPS jump instructions for block scan */
37: struct insn_jump mips64_insn_jumps[] = {
38: { "b" , 0xffff0000, 0x10000000, 16, 1 },
39: { "bal" , 0xffff0000, 0x04110000, 16, 1 },
40: { "beq" , 0xfc000000, 0x10000000, 16, 1 },
41: { "beql" , 0xfc000000, 0x50000000, 16, 1 },
42: { "bgez" , 0xfc1f0000, 0x04010000, 16, 1 },
43: { "bgezl" , 0xfc1f0000, 0x04030000, 16, 1 },
44: { "bgezal" , 0xfc1f0000, 0x04110000, 16, 1 },
45: { "bgezall" , 0xfc1f0000, 0x04130000, 16, 1 },
46: { "bgtz" , 0xfc1f0000, 0x1c000000, 16, 1 },
47: { "bgtzl" , 0xfc1f0000, 0x5c000000, 16, 1 },
48: { "blez" , 0xfc1f0000, 0x18000000, 16, 1 },
49: { "blezl" , 0xfc1f0000, 0x58000000, 16, 1 },
50: { "bltz" , 0xfc1f0000, 0x04000000, 16, 1 },
51: { "bltzl" , 0xfc1f0000, 0x04020000, 16, 1 },
52: { "bltzal" , 0xfc1f0000, 0x04100000, 16, 1 },
53: { "bltzall" , 0xfc1f0000, 0x04120000, 16, 1 },
54: { "bne" , 0xfc000000, 0x14000000, 16, 1 },
55: { "bnel" , 0xfc000000, 0x54000000, 16, 1 },
56: { "j" , 0xfc000000, 0x08000000, 26, 0 },
57: { NULL , 0x00000000, 0x00000000, 0, 0 },
58: };
59:
60: /* Instruction Lookup Table */
61: static insn_lookup_t *ilt = NULL;
62:
63: static void *mips64_jit_get_insn(int index)
64: {
65: return(&mips64_insn_tags[index]);
66: }
67:
68: static int mips64_jit_chk_lo(struct insn_tag *tag,int value)
69: {
70: return((value & tag->mask) == (tag->value & 0xFFFF));
71: }
72:
73: static int mips64_jit_chk_hi(struct insn_tag *tag,int value)
74: {
75: return((value & (tag->mask >> 16)) == (tag->value >> 16));
76: }
77:
78: /* Initialize instruction lookup table */
79: void mips64_jit_create_ilt(void)
80: {
81: int i,count;
82:
83: for(i=0,count=0;mips64_insn_tags[i].emit;i++)
84: count++;
85:
86: ilt = ilt_create(count,
87: (ilt_get_insn_cbk_t)mips64_jit_get_insn,
88: (ilt_check_cbk_t)mips64_jit_chk_lo,
89: (ilt_check_cbk_t)mips64_jit_chk_hi);
90: }
91:
92: /* Find the JIT code emitter for the specified MIPS instruction */
93: struct insn_tag *insn_tag_find(mips_insn_t ins)
94: {
95: struct insn_tag *tag = NULL;
96: int index;
97:
98: index = ilt_lookup(ilt,ins);
99: tag = mips64_jit_get_insn(index);
100: return tag;
101: }
102:
103: /* Check if the specified MIPS instruction is a jump */
104: struct insn_jump *insn_jump_find(mips_insn_t ins)
105: {
106: struct insn_jump *jump = NULL;
107: int i;
108:
109: for(i=0;mips64_insn_jumps[i].name;i++)
110: if ((ins & mips64_insn_jumps[i].mask) == mips64_insn_jumps[i].value) {
111: jump = &mips64_insn_jumps[i];
112: break;
113: }
114:
115: return(jump);
116: }
117:
118: /* Fetch a MIPS instruction */
119: static inline mips_insn_t insn_fetch(insn_block_t *b)
120: {
121: return(vmtoh32(b->mips_code[b->mips_trans_pos]));
122: }
123:
124: /* Emit a breakpoint if necessary */
125: static void insn_emit_breakpoint(cpu_mips_t *cpu,insn_block_t *b)
126: {
127: m_uint64_t pc;
128: int i;
129:
130: pc = b->start_pc+((b->mips_trans_pos-1)<<2);
131:
132: for(i=0;i<MIPS64_MAX_BREAKPOINTS;i++)
133: if (pc == cpu->breakpoints[i]) {
134: mips64_emit_breakpoint(b);
135: break;
136: }
137: }
138:
139: /* Fetch a MIPS instruction and emit corresponding translated code */
140: struct insn_tag *insn_fetch_and_emit(cpu_mips_t *cpu,insn_block_t *block,
141: int delay_slot)
142: {
143: struct insn_tag *tag;
144: mips_insn_t code;
145:
146: code = insn_fetch(block);
147: tag = insn_tag_find(code);
148: assert(tag);
149:
150: if (delay_slot && !tag->delay_slot) {
151: fprintf(stderr,"%% Invalid instruction 0x%8.8x in delay slot.\n",code);
152: return NULL;
153: }
154:
155: if (!delay_slot) {
156: block->jit_insn_ptr[block->mips_trans_pos] = block->jit_ptr;
157: }
158:
159: if (delay_slot != 2)
160: block->mips_trans_pos++;
161:
162: #if BREAKPOINT_ENABLE
163: if (cpu->breakpoints_enabled)
164: insn_emit_breakpoint(cpu,block);
165: #endif
166:
167: mips64_inc_cp0_count_reg(block);
168:
169: if (!delay_slot)
170: mips64_check_pending_irq(block);
171:
172: tag->emit(cpu,block,code);
173: return tag;
174: }
175:
176: /*
177: * This is a special case of MIPS instruction emitting, when this fucking
178: * GCC compiler has been able to optimize a lot and to branch in a delay
179: * slot.
180: */
181: u_char *insn_special_emit(cpu_mips_t *cpu,insn_block_t *block,m_uint64_t vaddr)
182: {
183: struct insn_tag *tag;
184: mips_insn_t code;
185: u_int offset;
186: u_char *cptr;
187:
188: /* the caller will patch to this address */
189: cptr = block->jit_ptr;
190:
191: offset = (vaddr - block->start_pc) >> 2;
192: block->jit_insn_ptr[offset] = cptr;
193: code = vmtoh32(block->mips_code[offset]);
194: tag = insn_tag_find(code);
195: assert(tag);
196:
197: tag->emit(cpu,block,code);
198: mips64_set_pc(block,vaddr+4);
199: insn_block_push_epilog(block);
200: return cptr;
201: }
202:
203: /* Add end of JIT block */
204: void insn_block_add_end(insn_block_t *b)
205: {
206: mips64_set_pc(b,b->start_pc+(b->mips_trans_pos<<2));
207: insn_block_push_epilog(b);
208: }
209:
210: /* Create a instruction block */
211: insn_block_t *insn_block_create(cpu_mips_t *cpu,m_uint64_t vaddr)
212: {
213: insn_block_t *block = NULL;
214:
215: if (!(block = malloc(sizeof(*block))))
216: goto error;
217:
218: memset(block,0,sizeof(*block));
219: block->start_pc = vaddr;
220: block->jit_bufsize = MIPS_JIT_BUFSIZE;
221:
222: if (!(block->jit_buffer = malloc(block->jit_bufsize)))
223: goto error;
224:
225: block->jit_ptr = block->jit_buffer;
226: block->mips_code = cpu->mem_op_lookup(cpu,block->start_pc);
227:
228: if (!block->mips_code) {
229: fprintf(stderr,"%% No memory map for code execution at 0x%llx\n",
230: block->start_pc);
231: goto error;
232: }
233:
234: #if DEBUG_BLOCK_TIMESTAMP
235: block->tm_first_use = block->tm_last_use = jit_jiffies;
236: #endif
237: return block;
238:
239: error:
240: free(block);
241: fprintf(stderr,"%% Unable to create instruction block for vaddr=0x%llx\n",
242: vaddr);
243: return NULL;
244: }
245:
246: /* Record a patch to apply in a compiled block */
247: int insn_block_record_patch(insn_block_t *block,u_char *jit_ptr,
248: m_uint64_t vaddr)
249: {
250: struct insn_patch_table *ipt = block->patch_table;
251: struct insn_patch *patch;
252:
253: /* pc must be 32-bit aligned */
254: if (vaddr & 0x03) {
255: fprintf(stderr,"Block 0x%8.8llx: trying to record an invalid PC "
256: "(0x%8.8llx) - mips_trans_pos=%d.\n",
257: block->start_pc,vaddr,block->mips_trans_pos);
258: return(-1);
259: }
260:
261: if (!ipt || (ipt->cur_patch >= INSN_PATCH_TABLE_SIZE))
262: {
263: /* full table or no table, create a new one */
264: ipt = malloc(sizeof(*ipt));
265: if (!ipt) {
266: fprintf(stderr,"%% Unable to create patch table.\n");
267: return(-1);
268: }
269:
270: memset(ipt,0,sizeof(*ipt));
271: ipt->next = block->patch_table;
272: block->patch_table = ipt;
273: }
274:
275: #if DEBUG_BLOCK_PATCH
276: printf("Block 0x%8.8llx: recording patch [JIT:%p->mips:0x%8.8llx], "
277: "MTP=%d\n",block->start_pc,jit_ptr,vaddr,block->mips_trans_pos);
278: #endif
279:
280: patch = &ipt->patches[ipt->cur_patch];
281: patch->jit_insn = jit_ptr;
282: patch->mips_pc = vaddr;
283: ipt->cur_patch++;
284: return(0);
285: }
286:
287: /* Apply all patches */
288: int insn_block_apply_patches(cpu_mips_t *cpu,insn_block_t *block)
289: {
290: struct insn_patch_table *ipt;
291: struct insn_patch *patch;
292: u_char *jit_dst;
293: int i;
294:
295: for(ipt=block->patch_table;ipt;ipt=ipt->next)
296: for(i=0;i<ipt->cur_patch;i++)
297: {
298: patch = &ipt->patches[i];
299: jit_dst = insn_block_get_jit_ptr(block,patch->mips_pc);
300:
301: if (!jit_dst) {
302: #if DEBUG_BLOCK_PATCH
303: printf("Block 0x%8.8llx: trying to apply a null patch "
304: "(PC=0x%llx)\n",
305: block->start_pc,patch->mips_pc);
306: #endif
307: jit_dst = insn_special_emit(cpu,block,patch->mips_pc);
308: }
309:
310: insn_block_set_patch(patch->jit_insn,jit_dst);
311:
312: #if DEBUG_BLOCK_PATCH
313: printf("Block 0x%8.8llx: applying patch "
314: "[JIT:%p->mips:0x%8.8llx=JIT:%p]\n",
315: block->start_pc,patch->jit_insn,patch->mips_pc,jit_dst);
316: #endif
317: }
318:
319: return(0);
320: }
321:
322: /* Adjust the JIT buffer if its size is not sufficient */
323: int insn_block_adjust_jit_buffer(insn_block_t *block)
324: {
325: u_char *new_ptr;
326:
327: if ((block->jit_ptr - block->jit_buffer) <= (block->jit_bufsize - 512))
328: return(0);
329:
330: #if DEBUG_BLOCK_CHUNK
331: printf("Block 0x%llx: adjusting JIT buffer...\n",block->start_pc);
332: #endif
333:
334: if (block->jit_chunk_pos >= INSN_MAX_CHUNKS) {
335: fprintf(stderr,"Block 0x%llx: too many JIT chunks.\n",block->start_pc);
336: return(-1);
337: }
338:
339: /* save the current JIT block */
340: block->jit_chunks[block->jit_chunk_pos++] = block->jit_buffer;
341:
342: if (!(new_ptr = malloc(block->jit_bufsize)))
343: return(-1);
344:
345: /* jump to the new block */
346: insn_block_set_jump(block->jit_ptr,new_ptr);
347: block->jit_ptr = block->jit_buffer = new_ptr;
348: return(0);
349: }
350:
351: /* Scan an instruction block to determine its length */
352: int insn_block_scan(insn_block_t *block)
353: {
354: struct insn_jump *jump;
355: m_uint64_t cur_pc,last_jrra_pc;
356: m_uint64_t next_pc,max_next_pc;
357: m_int64_t offset;
358: mips_insn_t code;
359: size_t len;
360:
361: last_jrra_pc = max_next_pc = 0;
362:
363: for(block->mips_code_len = 0, cur_pc = block->start_pc;
364: block->mips_code_len < MIPS_MAX_BLOCK_INSN;
365: block->mips_code_len++, cur_pc += 4)
366: {
367: code = vmtoh32(block->mips_code[block->mips_code_len]);
368:
369: #if DEBUG_BLOCK_SCAN
370: printf("insn_block_scan: cur_pc = 0x%llx, instruction = 0x%8.8x\n",
371: cur_pc,code);
372: #endif
373:
374: /* jr ra = jump to return address */
375: if (code == MIPS_INSN_JR_RA) {
376: last_jrra_pc = cur_pc;
377:
378: if (last_jrra_pc >= max_next_pc) {
379: block->mips_code_len++; /* account delay slot */
380: break;
381: }
382:
383: continue;
384: }
385:
386: /* check if we have a jump instruction */
387: if (!(jump = insn_jump_find(code)))
388: continue;
389:
390: #if DEBUG_BLOCK_SCAN
391: printf("insn_block_scan: jump \"%s\" detected (max_next_pc=0x%llx)\n",
392: jump->name, max_next_pc);
393: #endif
394:
395: /* we have a jump, compute next pc */
396: offset = (code & ((1 << jump->offset_bits) - 1)) << 2;
397:
398: if (jump->relative) {
399: next_pc = cur_pc + 4 + sign_extend(offset,jump->offset_bits+2);
400: } else {
401: next_pc = (cur_pc & ~((1 << (jump->offset_bits + 2)) - 1)) | offset;
402: }
403:
404: if (next_pc > max_next_pc)
405: max_next_pc = next_pc;
406:
407: #if DEBUG_BLOCK_SCAN
408: printf("insn_block_scan: next_pc=0x%llx, max_next_pc=0x%llx\n",
409: next_pc,max_next_pc);
410: #endif
411: }
412:
413: block->mips_code_len++;
414: block->end_pc = block->start_pc + (block->mips_code_len * 4);
415:
416: len = block->mips_code_len * sizeof(u_char *);
417:
418: if (!(block->jit_insn_ptr = malloc(len))) {
419: fprintf(stderr,"insn_block_scan: unable to create JIT/mips mapping.\n");
420: return(-1);
421: }
422:
423: memset(block->jit_insn_ptr,0,len);
424:
425: #if DEBUG_BLOCK_SCAN
426: printf("insn_block_scan: start_pc = 0x%llx, end_pc = 0x%llx\n",
427: block->start_pc, block->end_pc);
428: #endif
429:
430: return(0);
431: }
432:
433: /* Compile a MIPS instruction block */
434: static inline int insn_block_compile(cpu_mips_t *cpu,insn_block_t *block)
435: {
436: struct insn_tag *tag;
437:
438: block->mips_trans_pos = 0;
439:
440: while(block->mips_trans_pos < block->mips_code_len)
441: {
442: if (!(tag = insn_fetch_and_emit(cpu,block,0)))
443: return(-1);
444:
445: #if DEBUG_BLOCK_COMPILE
446: printf("Block 0x%8.8llx: emitted tag 0x%8.8x/0x%8.8x\n",
447: block->start_pc,tag->mask,tag->value);
448: #endif
449:
450: insn_block_adjust_jit_buffer(block);
451: }
452:
453: insn_block_add_end(block);
454: insn_block_apply_patches(cpu,block);
455: return(0);
456: }
457:
458: /* Compile a MIPS instruction block */
459: static insn_block_t *insn_block_scan_and_compile(cpu_mips_t *cpu,
460: m_uint64_t vaddr)
461: {
462: insn_block_t *block;
463:
464: if (!(block = insn_block_create(cpu,vaddr)))
465: return NULL;
466:
467: if ((insn_block_scan(block) == -1) || (insn_block_compile(cpu,block) == -1))
468: return NULL;
469:
470: rbtree_insert(cpu->insn_block_tree,block,block);
471: return block;
472: }
473:
474: /* Run a compiled MIPS instruction block */
475: static inline void insn_block_run(cpu_mips_t *cpu,insn_block_t *block)
476: {
477: #if DEBUG_SYM_TREE
478: struct symbol *sym = NULL;
479: int mark = FALSE;
480: #endif
481:
482: if (unlikely(cpu->pc & 0x03)) {
483: fprintf(stderr,"insn_block_run: Invalid PC 0x%llx.\n",cpu->pc);
484: mips64_dump_regs(cpu);
485: tlb_dump(cpu);
486: exit(EXIT_FAILURE);
487: }
488:
489: #if DEBUG_SYM_TREE
490: if (sym_tree)
491: {
492: if ((sym = sym_lookup(cpu->pc)) != NULL) {
493: fprintf(log_file,
494: "function_run: %s (PC=0x%llx) "
495: "RA = 0x%llx\na0=0x%llx, "
496: "a1=0x%llx, a2=0x%llx, a3=0x%llx\n",
497: sym->name, cpu->pc, cpu->gpr[MIPS_GPR_RA],
498: cpu->gpr[MIPS_GPR_A0], cpu->gpr[MIPS_GPR_A1],
499: cpu->gpr[MIPS_GPR_A2], cpu->gpr[MIPS_GPR_A3]);
500: mark = TRUE;
501: }
502: }
503: #endif
504:
505: #if DEBUG_INSN_ITRACE
506: if (insn_itrace) {
507: fprintf(log_file,
508: "block_run(S): PC = 0x%llx [start_pc=0x%llx,end_pc=0x%llx] "
509: "RA = 0x%llx\na0=0x%llx, a1=0x%llx, a2=0x%llx, a3=0x%llx\n",
510: cpu->pc,block->start_pc,block->end_pc,
511: cpu->gpr[MIPS_GPR_RA],
512: cpu->gpr[MIPS_GPR_A0],
513: cpu->gpr[MIPS_GPR_A1],
514: cpu->gpr[MIPS_GPR_A2],
515: cpu->gpr[MIPS_GPR_A3]);
516: }
517: #endif
518:
519: /* Execute JIT compiled code */
520: insn_block_exec_jit_code(cpu,block);
521:
522: #if DEBUG_SYM_TREE
523: if (mark) {
524: fprintf(log_file,"function_end: %s, v0 = 0x%llx\n",
525: sym->name,cpu->gpr[MIPS_GPR_V0]);
526: }
527: #endif
528:
529: #if DEBUG_INSN_ITRACE
530: if (insn_itrace) {
531: fprintf(log_file,"block_run(E): PC = 0x%llx, v0 = 0x%llx\n",
532: cpu->pc, cpu->gpr[MIPS_GPR_V0]);
533: }
534: #endif
535: }
536:
537: /* Tree comparison function */
538: int insn_block_cmp(m_uint64_t *vaddr,insn_block_t *b)
539: {
540: if (*vaddr < b->start_pc)
541: return(-1);
542:
543: if (*vaddr >= b->end_pc)
544: return(1);
545:
546: return(0);
547: }
548:
549: /* Check if the specified address belongs to the specified block */
550: int insn_block_local_addr(insn_block_t *block,m_uint64_t vaddr,
551: u_char **jit_addr)
552: {
553: if ((vaddr >= block->start_pc) && (vaddr < block->end_pc)) {
554: *jit_addr = insn_block_get_jit_ptr(block,vaddr);
555: return(1);
556: }
557: return(0);
558: }
559:
560: /* Execute a compiled MIPS code */
561: void *insn_block_execute(cpu_mips_t *cpu)
562: {
563: insn_block_t *block;
564:
565: start_cpu:
566: for(;;) {
567: if (unlikely(!cpu->pc) || unlikely(cpu->state != MIPS_CPU_RUNNING))
568: break;
569:
570: block = insn_block_locate_fast(cpu,cpu->pc);
571:
572: if (!block) {
573: block = insn_block_scan_and_compile(cpu,cpu->pc);
574: if (unlikely(!block)) {
575: cpu->pc = 0;
576: break;
577: }
578: }
579:
580: #if DEBUG_BLOCK_TIMESTAMP
581: block->tm_last_use = jit_jiffies++;
582: #endif
583: block->acc_count++;
584: insn_block_run(cpu,block);
585: }
586:
587: if (!cpu->pc) {
588: cpu_stop(cpu);
589: m_log("CPU","CPU%u has PC=0, halting CPU.\n",cpu->id);
590: }
591:
592: /* check regularly if the CPU has been restarted */
593: do {
594: if (cpu->state == MIPS_CPU_RUNNING)
595: goto start_cpu;
596:
597: usleep(200000);
598: }while(1);
599:
600: return NULL;
601: }
602:
603: /* Dump an instruction block */
604: void insn_block_dump_tree_node(insn_block_t *b,void *empty,m_tmcnt_t *ct)
605: {
606: #if DEBUG_BLOCK_TIMESTAMP
607: m_uint64_t deltaT = jit_jiffies - b->tm_last_use;
608:
609: m_log("RBTREE","Block 0x%llx (end=0x%llx): count=%10llu, deltaT=%12llu\n",
610: b->start_pc,b->end_pc,b->acc_count,deltaT);
611: #else
612: m_log("RBTREE","Block 0x%llx (end=0x%llx): count=%llu\n",
613: b->start_pc,b->end_pc,b->acc_count);
614: #endif
615: }
616:
617: /* Dump the instruction block tree */
618: void insn_block_dump_tree(cpu_mips_t *cpu)
619: {
620: m_log("RBTREE","Height: %d\n",rbtree_height(cpu->insn_block_tree));
621:
622: rbtree_foreach(cpu->insn_block_tree,
623: (tree_fforeach)insn_block_dump_tree_node,
624: NULL);
625: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.