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