|
|
1.1.1.3 root 1: /*
2: * compiler/compemu_support.cpp - Core dynamic translation engine
3: *
4: * Copyright (c) 2001-2009 Milan Jurik of ARAnyM dev team (see AUTHORS)
5: *
6: * Inspired by Christian Bauer's Basilisk II
7: *
8: * This file is part of the ARAnyM project which builds a new and powerful
9: * TOS/FreeMiNT compatible virtual machine running on almost any hardware.
10: *
11: * JIT compiler m68k -> IA-32 and AMD64 / ARM
12: *
13: * Original 68040 JIT compiler for UAE, copyright 2000-2002 Bernd Meyer
14: * Adaptation for Basilisk II and improvements, copyright 2000-2004 Gwenole Beauchesne
15: * Portions related to CPU detection come from linux/arch/i386/kernel/setup.c
16: *
17: * ARAnyM is free software; you can redistribute it and/or modify
18: * it under the terms of the GNU General Public License as published by
19: * the Free Software Foundation; either version 2 of the License, or
20: * (at your option) any later version.
21: *
22: * ARAnyM is distributed in the hope that it will be useful,
23: * but WITHOUT ANY WARRANTY; without even the implied warranty of
24: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25: * GNU General Public License for more details.
26: *
27: * You should have received a copy of the GNU General Public License
1.1.1.4 ! root 28: * along with ARAnyM; if not, write to the Free Software Foundation,
! 29: * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
1.1.1.3 root 30: */
31:
32: #ifdef UAE
1.1 root 33:
34: #define writemem_special writemem
35: #define readmem_special readmem
36:
1.1.1.3 root 37: #else
38: #if !FIXED_ADDRESSING
39: #error "Only Fixed Addressing is supported with the JIT Compiler"
40: #endif
41:
42: #if defined(X86_ASSEMBLY) && !SAHF_SETO_PROFITABLE
43: #error "Only [LS]AHF scheme to [gs]et flags is supported with the JIT Compiler"
44: #endif
45:
46: /* NOTE: support for AMD64 assumes translation cache and other code
47: * buffers are allocated into a 32-bit address space because (i) B2/JIT
48: * code is not 64-bit clean and (ii) it's faster to resolve branches
49: * that way.
50: */
51: #if !defined(CPU_i386) && !defined(CPU_x86_64) && !defined(CPU_arm)
52: #error "Only IA-32, X86-64 and ARM v6 targets are supported with the JIT Compiler"
53: #endif
54: #endif
55:
56: #define USE_MATCH 0
57:
58: /* kludge for Brian, so he can compile under MSVC++ */
59: #define USE_NORMAL_CALLING_CONVENTION 0
60:
1.1 root 61: #include "sysconfig.h"
62: #include "sysdeps.h"
63:
1.1.1.3 root 64: #ifdef JIT
1.1 root 65:
1.1.1.3 root 66: #ifdef UAE
1.1 root 67: #include "options.h"
68: #include "events.h"
1.1.1.3 root 69: #include "memory.h"
1.1 root 70: #include "custom.h"
1.1.1.3 root 71: #else
72: #include "sysdeps.h"
73: #include "cpu_emulation.h"
74: #include "main.h"
75: #include "vm_alloc.h"
76:
77: #include "m68k.h"
78: #include "memory-uae.h"
79: #include "readcpu.h"
80: #endif
1.1 root 81: #include "newcpu.h"
82: #include "comptbl.h"
1.1.1.3 root 83: #ifdef UAE
1.1 root 84: #include "compemu.h"
1.1.1.3 root 85: #else
86: #include "compiler/compemu.h"
87: #include "fpu/fpu.h"
88: #include "fpu/flags.h"
89: #include "parameters.h"
90: #endif
91:
92: #ifdef UAE
93: #include "uae/log.h"
94:
95: #include "uae/vm.h"
96: #define VM_PAGE_READ UAE_VM_READ
97: #define VM_PAGE_WRITE UAE_VM_WRITE
98: #define VM_PAGE_EXECUTE UAE_VM_EXECUTE
99: #define VM_MAP_FAILED UAE_VM_ALLOC_FAILED
100: #define VM_MAP_DEFAULT 1
101: #define VM_MAP_32BIT 1
102: #define vm_protect(address, size, protect) uae_vm_protect(address, size, protect)
103: #define vm_release(address, size) uae_vm_free(address, size)
104:
105: static inline void *vm_acquire(size_t size, int options = VM_MAP_DEFAULT)
106: {
107: assert(options == (VM_MAP_DEFAULT | VM_MAP_32BIT));
108: return uae_vm_alloc(size, UAE_VM_32BIT, UAE_VM_READ_WRITE);
109: }
110:
111: #define UNUSED(x)
112: #include "uae.h"
113: #include "uae/log.h"
114: #define jit_log(format, ...) \
115: uae_log("JIT: " format "\n", ##__VA_ARGS__);
116: #define jit_log2(format, ...)
1.1 root 117:
1.1.1.3 root 118: #define MEMBaseDiff uae_p32(NATMEM_OFFSET)
1.1 root 119:
1.1.1.3 root 120: #ifdef NATMEM_OFFSET
121: #define FIXED_ADDRESSING 1
122: #endif
123:
124: #define SAHF_SETO_PROFITABLE
1.1 root 125:
126: // %%% BRIAN KING WAS HERE %%%
127: extern bool canbang;
1.1.1.3 root 128:
129: #include "compemu_prefs.cpp"
130:
131: #define uint32 uae_u32
132: #define uint8 uae_u8
133:
134: static inline int distrust_check(int value)
135: {
136: #ifdef JIT_ALWAYS_DISTRUST
137: return 1;
138: #else
139: int distrust = value;
140: return distrust;
141: #endif
142: }
143:
144: static inline int distrust_byte(void)
145: {
146: return distrust_check(currprefs.comptrustbyte);
147: }
148:
149: static inline int distrust_word(void)
150: {
151: return distrust_check(currprefs.comptrustword);
152: }
153:
154: static inline int distrust_long(void)
155: {
156: return distrust_check(currprefs.comptrustlong);
157: }
158:
159: static inline int distrust_addr(void)
160: {
161: return distrust_check(currprefs.comptrustnaddr);
162: }
163:
164: #else
165: #define DEBUG 0
166: #include "debug.h"
167:
168: #if DEBUG
169: #define PROFILE_COMPILE_TIME 1
170: #define PROFILE_UNTRANSLATED_INSNS 1
171: #endif
172: #endif
173:
174: # include <csignal>
175: # include <cstdlib>
176: # include <cerrno>
177: # include <cassert>
178:
179: #if defined(CPU_x86_64) && 0
180: #define RECORD_REGISTER_USAGE 1
181: #endif
182:
183: #ifdef JIT_DEBUG
184: #undef abort
185: #define abort() do { \
186: fprintf(stderr, "Abort in file %s at line %d\n", __FILE__, __LINE__); \
187: compiler_dumpstate(); \
188: exit(EXIT_FAILURE); \
189: } while (0)
190: #endif
191:
192: #ifdef RECORD_REGISTER_USAGE
193: static uint64 reg_count[16];
194: static int reg_count_local[16];
195:
196: static int reg_count_compare(const void *ap, const void *bp)
197: {
198: const int a = *((int *)ap);
199: const int b = *((int *)bp);
200: return reg_count[b] - reg_count[a];
201: }
202: #endif
203:
204: #ifdef PROFILE_COMPILE_TIME
205: #include <time.h>
206: static uae_u32 compile_count = 0;
207: static clock_t compile_time = 0;
208: static clock_t emul_start_time = 0;
209: static clock_t emul_end_time = 0;
210: #endif
211:
212: #ifdef PROFILE_UNTRANSLATED_INSNS
213: static const int untranslated_top_ten = 20;
214: static uae_u32 raw_cputbl_count[65536] = { 0, };
215: static uae_u16 opcode_nums[65536];
216:
217:
218: static int untranslated_compfn(const void *e1, const void *e2)
219: {
220: return raw_cputbl_count[*(const uae_u16 *)e1] < raw_cputbl_count[*(const uae_u16 *)e2];
221: }
222: #endif
223:
224: static compop_func *compfunctbl[65536];
225: static compop_func *nfcompfunctbl[65536];
1.1 root 226: #ifdef NOFLAGS_SUPPORT
1.1.1.3 root 227: static cpuop_func *nfcpufunctbl[65536];
1.1 root 228: #endif
229: uae_u8* comp_pc_p;
230:
1.1.1.3 root 231: #ifdef UAE
232: /* defined in uae.h */
233: #else
234: // External variables
235: // newcpu.cpp
236: extern int quit_program;
237: #endif
238:
239: // gb-- Extra data for Basilisk II/JIT
240: #ifdef JIT_DEBUG
241: static bool JITDebug = false; // Enable runtime disassemblers through mon?
242: #else
243: const bool JITDebug = false; // Don't use JIT debug mode at all
244: #endif
245: #if USE_INLINING
246: #ifdef UAE
247: #define follow_const_jumps (currprefs.comp_constjump != 0)
248: #else
249: static bool follow_const_jumps = true; // Flag: translation through constant jumps
250: #endif
251: #else
252: const bool follow_const_jumps = false;
253: #endif
254:
255: const uae_u32 MIN_CACHE_SIZE = 1024; // Minimal translation cache size (1 MB)
256: static uae_u32 cache_size = 0; // Size of total cache allocated for compiled blocks
257: static uae_u32 current_cache_size = 0; // Cache grows upwards: how much has been consumed already
258: static bool lazy_flush = true; // Flag: lazy translation cache invalidation
259: #ifdef UAE
260: #ifdef USE_JIT_FPU
261: #define avoid_fpu (!currprefs.compfpu)
262: #else
263: #define avoid_fpu (true)
264: #endif
265: #else
266: static bool avoid_fpu = true; // Flag: compile FPU instructions ?
267: #endif
268: static bool have_cmov = false; // target has CMOV instructions ?
269: static bool have_lahf_lm = true; // target has LAHF supported in long mode ?
270: static bool have_rat_stall = true; // target has partial register stalls ?
271: const bool tune_alignment = true; // Tune code alignments for running CPU ?
272: const bool tune_nop_fillers = true; // Tune no-op fillers for architecture
273: static bool setzflg_uses_bsf = false; // setzflg virtual instruction can use native BSF instruction correctly?
274: static int align_loops = 32; // Align the start of loops
275: static int align_jumps = 32; // Align the start of jumps
276: static int optcount[10] = {
277: #ifdef UAE
278: 4, // How often a block has to be executed before it is translated
279: #else
280: 10, // How often a block has to be executed before it is translated
281: #endif
282: 0, // How often to use naive translation
283: 0, 0, 0, 0,
284: -1, -1, -1, -1
285: };
286:
287: #ifdef UAE
288: /* FIXME: op_properties is currently in compemu.h */
289:
290: op_properties prop[65536];
291:
292: static inline bool is_const_jump(uae_u32 opcode)
293: {
294: return prop[opcode].is_const_jump != 0;
295: }
296: #else
297: struct op_properties {
298: uae_u8 use_flags;
299: uae_u8 set_flags;
300: uae_u8 is_addx;
301: uae_u8 cflow;
302: };
303: static op_properties prop[65536];
304:
305: static inline int end_block(uae_u32 opcode)
306: {
307: return (prop[opcode].cflow & fl_end_block);
308: }
309:
310: static inline bool is_const_jump(uae_u32 opcode)
311: {
312: return (prop[opcode].cflow == fl_const_jump);
313: }
314:
315: static inline bool may_trap(uae_u32 opcode)
316: {
317: return (prop[opcode].cflow & fl_trap);
318: }
319:
320: #endif
321:
322: static inline unsigned int cft_map (unsigned int f)
323: {
324: #ifdef UAE
325: return f;
326: #else
327: #if !defined(HAVE_GET_WORD_UNSWAPPED) || defined(FULLMMU)
328: return f;
329: #else
330: return ((f >> 8) & 255) | ((f & 255) << 8);
331: #endif
332: #endif
333: }
334:
1.1 root 335: uae_u8* start_pc_p;
336: uae_u32 start_pc;
337: uae_u32 current_block_pc_p;
1.1.1.3 root 338: static uintptr current_block_start_target;
1.1 root 339: uae_u32 needed_flags;
1.1.1.3 root 340: static uintptr next_pc_p;
341: static uintptr taken_pc_p;
1.1 root 342: static int branch_cc;
1.1.1.3 root 343: static int redo_current_block;
344:
1.1 root 345: int segvcount=0;
346: int soft_flush_count=0;
347: int hard_flush_count=0;
348: int checksum_count=0;
349: static uae_u8* current_compile_p=NULL;
350: static uae_u8* max_compile_start;
1.1.1.3 root 351: static uae_u8* compiled_code=NULL;
1.1 root 352: static uae_s32 reg_alloc_run;
1.1.1.3 root 353: const int POPALLSPACE_SIZE = 2048; /* That should be enough space */
354: static uae_u8 *popallspace=NULL;
1.1 root 355:
356: void* pushall_call_handler=NULL;
357: static void* popall_do_nothing=NULL;
358: static void* popall_exec_nostats=NULL;
359: static void* popall_execute_normal=NULL;
360: static void* popall_cache_miss=NULL;
361: static void* popall_recompile_block=NULL;
362: static void* popall_check_checksum=NULL;
363:
364: /* The 68k only ever executes from even addresses. So right now, we
1.1.1.3 root 365: * waste half the entries in this array
366: * UPDATE: We now use those entries to store the start of the linked
367: * lists that we maintain for each hash result.
368: */
369: static
370: cacheline cache_tags[TAGSIZE];
371: int letit=0;
372: static
373: blockinfo* hold_bi[MAX_HOLD_BI];
374: static
375: blockinfo* active;
376: static
377: blockinfo* dormant;
1.1 root 378:
379: #ifdef NOFLAGS_SUPPORT
380: /* 68040 */
1.1.1.3 root 381: extern const struct cputbl op_smalltbl_0_nf[];
1.1 root 382: #endif
383: extern const struct comptbl op_smalltbl_0_comp_nf[];
384: extern const struct comptbl op_smalltbl_0_comp_ff[];
1.1.1.3 root 385:
1.1 root 386: #ifdef NOFLAGS_SUPPORT
387: /* 68020 + 68881 */
388: extern const struct cputbl op_smalltbl_1_nf[];
389: /* 68020 */
390: extern const struct cputbl op_smalltbl_2_nf[];
391: /* 68010 */
392: extern const struct cputbl op_smalltbl_3_nf[];
393: /* 68000 */
394: extern const struct cputbl op_smalltbl_4_nf[];
395: /* 68000 slow but compatible. */
396: extern const struct cputbl op_smalltbl_5_nf[];
397: #endif
398:
1.1.1.3 root 399: static
400: bigstate live;
401: static
402: smallstate empty_ss;
403: static
404: smallstate default_ss;
1.1 root 405: static int optlev;
406:
407: static int writereg(int r, int size);
1.1.1.3 root 408: static void unlock2(int r);
1.1 root 409: static void setlock(int r);
410: static int readreg_specific(int r, int size, int spec);
411: static int writereg_specific(int r, int size, int spec);
412: static void prepare_for_call_1(void);
413: static void prepare_for_call_2(void);
414: static void align_target(uae_u32 a);
415:
1.1.1.3 root 416: static void inline flush_cpu_icache(void *from, void *to);
417: static void inline write_jmp_target(uae_u32 *jmpaddr, cpuop_func* a);
418: static void inline emit_jmp_target(uae_u32 a);
1.1 root 419:
420: uae_u32 m68k_pc_offset;
421:
422: /* Some arithmetic operations can be optimized away if the operands
1.1.1.3 root 423: * are known to be constant. But that's only a good idea when the
424: * side effects they would have on the flags are not important. This
425: * variable indicates whether we need the side effects or not
426: */
1.1 root 427: uae_u32 needflags=0;
428:
429: /* Flag handling is complicated.
1.1.1.3 root 430: *
431: * x86 instructions create flags, which quite often are exactly what we
432: * want. So at times, the "68k" flags are actually in the x86 flags.
433: *
434: * Then again, sometimes we do x86 instructions that clobber the x86
435: * flags, but don't represent a corresponding m68k instruction. In that
436: * case, we have to save them.
437: *
438: * We used to save them to the stack, but now store them back directly
439: * into the regflags.cznv of the traditional emulation. Thus some odd
440: * names.
441: *
442: * So flags can be in either of two places (used to be three; boy were
443: * things complicated back then!); And either place can contain either
444: * valid flags or invalid trash (and on the stack, there was also the
445: * option of "nothing at all", now gone). A couple of variables keep
446: * track of the respective states.
447: *
448: * To make things worse, we might or might not be interested in the flags.
449: * by default, we are, but a call to dont_care_flags can change that
450: * until the next call to live_flags. If we are not, pretty much whatever
451: * is in the register and/or the native flags is seen as valid.
452: */
1.1 root 453:
1.1.1.3 root 454: static inline blockinfo* get_blockinfo(uae_u32 cl)
1.1 root 455: {
456: return cache_tags[cl+1].bi;
457: }
458:
1.1.1.3 root 459: static inline blockinfo* get_blockinfo_addr(void* addr)
1.1 root 460: {
461: blockinfo* bi=get_blockinfo(cacheline(addr));
462:
463: while (bi) {
464: if (bi->pc_p==addr)
465: return bi;
466: bi=bi->next_same_cl;
467: }
468: return NULL;
469: }
470:
471:
472: /*******************************************************************
1.1.1.3 root 473: * All sorts of list related functions for all of the lists *
474: *******************************************************************/
1.1 root 475:
1.1.1.3 root 476: static inline void remove_from_cl_list(blockinfo* bi)
1.1 root 477: {
478: uae_u32 cl=cacheline(bi->pc_p);
479:
480: if (bi->prev_same_cl_p)
481: *(bi->prev_same_cl_p)=bi->next_same_cl;
482: if (bi->next_same_cl)
483: bi->next_same_cl->prev_same_cl_p=bi->prev_same_cl_p;
484: if (cache_tags[cl+1].bi)
485: cache_tags[cl].handler=cache_tags[cl+1].bi->handler_to_use;
486: else
487: cache_tags[cl].handler=(cpuop_func*)popall_execute_normal;
488: }
489:
1.1.1.3 root 490: static inline void remove_from_list(blockinfo* bi)
1.1 root 491: {
492: if (bi->prev_p)
493: *(bi->prev_p)=bi->next;
494: if (bi->next)
495: bi->next->prev_p=bi->prev_p;
496: }
497:
1.1.1.3 root 498: static inline void remove_from_lists(blockinfo* bi)
1.1 root 499: {
500: remove_from_list(bi);
501: remove_from_cl_list(bi);
502: }
503:
1.1.1.3 root 504: static inline void add_to_cl_list(blockinfo* bi)
1.1 root 505: {
506: uae_u32 cl=cacheline(bi->pc_p);
507:
508: if (cache_tags[cl+1].bi)
509: cache_tags[cl+1].bi->prev_same_cl_p=&(bi->next_same_cl);
510: bi->next_same_cl=cache_tags[cl+1].bi;
511:
512: cache_tags[cl+1].bi=bi;
513: bi->prev_same_cl_p=&(cache_tags[cl+1].bi);
514:
515: cache_tags[cl].handler=bi->handler_to_use;
516: }
517:
1.1.1.3 root 518: static inline void raise_in_cl_list(blockinfo* bi)
1.1 root 519: {
520: remove_from_cl_list(bi);
521: add_to_cl_list(bi);
522: }
523:
1.1.1.3 root 524: static inline void add_to_active(blockinfo* bi)
1.1 root 525: {
526: if (active)
527: active->prev_p=&(bi->next);
528: bi->next=active;
529:
530: active=bi;
531: bi->prev_p=&active;
532: }
533:
1.1.1.3 root 534: static inline void add_to_dormant(blockinfo* bi)
1.1 root 535: {
536: if (dormant)
537: dormant->prev_p=&(bi->next);
538: bi->next=dormant;
539:
540: dormant=bi;
541: bi->prev_p=&dormant;
542: }
543:
1.1.1.3 root 544: static inline void remove_dep(dependency* d)
1.1 root 545: {
546: if (d->prev_p)
547: *(d->prev_p)=d->next;
548: if (d->next)
549: d->next->prev_p=d->prev_p;
550: d->prev_p=NULL;
551: d->next=NULL;
552: }
553:
554: /* This block's code is about to be thrown away, so it no longer
1.1.1.3 root 555: depends on anything else */
556: static inline void remove_deps(blockinfo* bi)
1.1 root 557: {
558: remove_dep(&(bi->dep[0]));
559: remove_dep(&(bi->dep[1]));
560: }
561:
1.1.1.3 root 562: static inline void adjust_jmpdep(dependency* d, cpuop_func* a)
1.1 root 563: {
1.1.1.3 root 564: write_jmp_target(d->jmp_off, a);
1.1 root 565: }
566:
567: /********************************************************************
1.1.1.3 root 568: * Soft flush handling support functions *
569: ********************************************************************/
1.1 root 570:
1.1.1.3 root 571: static inline void set_dhtu(blockinfo* bi, cpuop_func *dh)
1.1 root 572: {
1.1.1.3 root 573: jit_log2("bi is %p",bi);
1.1 root 574: if (dh!=bi->direct_handler_to_use) {
575: dependency* x=bi->deplist;
1.1.1.3 root 576: jit_log2("bi->deplist=%p",bi->deplist);
1.1 root 577: while (x) {
1.1.1.3 root 578: jit_log2("x is %p",x);
579: jit_log2("x->next is %p",x->next);
580: jit_log2("x->prev_p is %p",x->prev_p);
1.1 root 581:
582: if (x->jmp_off) {
583: adjust_jmpdep(x,dh);
584: }
585: x=x->next;
586: }
1.1.1.3 root 587: bi->direct_handler_to_use=dh;
1.1 root 588: }
589: }
590:
1.1.1.3 root 591: static inline void invalidate_block(blockinfo* bi)
1.1 root 592: {
593: int i;
594:
595: bi->optlevel=0;
1.1.1.3 root 596: bi->count=optcount[0]-1;
1.1 root 597: bi->handler=NULL;
598: bi->handler_to_use=(cpuop_func*)popall_execute_normal;
599: bi->direct_handler=NULL;
600: set_dhtu(bi,bi->direct_pen);
601: bi->needed_flags=0xff;
1.1.1.3 root 602: bi->status=BI_INVALID;
1.1 root 603: for (i=0;i<2;i++) {
604: bi->dep[i].jmp_off=NULL;
605: bi->dep[i].target=NULL;
606: }
607: remove_deps(bi);
608: }
609:
1.1.1.3 root 610: static inline void create_jmpdep(blockinfo* bi, int i, uae_u32* jmpaddr, uae_u32 target)
1.1 root 611: {
1.1.1.3 root 612: blockinfo* tbi=get_blockinfo_addr((void*)(uintptr)target);
1.1 root 613:
614: Dif(!tbi) {
1.1.1.3 root 615: jit_abort("Could not create jmpdep!");
1.1 root 616: }
617: bi->dep[i].jmp_off=jmpaddr;
1.1.1.3 root 618: bi->dep[i].source=bi;
1.1 root 619: bi->dep[i].target=tbi;
620: bi->dep[i].next=tbi->deplist;
621: if (bi->dep[i].next)
622: bi->dep[i].next->prev_p=&(bi->dep[i].next);
623: bi->dep[i].prev_p=&(tbi->deplist);
624: tbi->deplist=&(bi->dep[i]);
625: }
626:
1.1.1.3 root 627: static inline void block_need_recompile(blockinfo * bi)
1.1 root 628: {
1.1.1.3 root 629: uae_u32 cl = cacheline(bi->pc_p);
1.1 root 630:
1.1.1.3 root 631: set_dhtu(bi, bi->direct_pen);
632: bi->direct_handler = bi->direct_pen;
1.1 root 633:
1.1.1.3 root 634: bi->handler_to_use = (cpuop_func *)popall_execute_normal;
635: bi->handler = (cpuop_func *)popall_execute_normal;
636: if (bi == cache_tags[cl + 1].bi)
637: cache_tags[cl].handler = (cpuop_func *)popall_execute_normal;
638: bi->status = BI_NEED_RECOMP;
639: }
640:
641: static inline void mark_callers_recompile(blockinfo * bi)
642: {
643: dependency *x = bi->deplist;
644:
645: while (x) {
646: dependency *next = x->next; /* This disappears when we mark for
647: * recompilation and thus remove the
648: * blocks from the lists */
649: if (x->jmp_off) {
650: blockinfo *cbi = x->source;
651:
652: Dif(cbi->status == BI_INVALID) {
653: jit_log("invalid block in dependency list"); // FIXME?
654: // abort();
655: }
656: if (cbi->status == BI_ACTIVE || cbi->status == BI_NEED_CHECK) {
657: block_need_recompile(cbi);
658: mark_callers_recompile(cbi);
659: }
660: else if (cbi->status == BI_COMPILING) {
661: redo_current_block = 1;
662: }
663: else if (cbi->status == BI_NEED_RECOMP) {
664: /* nothing */
665: }
666: else {
667: jit_log2("Status %d in mark_callers",cbi->status); // FIXME?
668: }
669: }
670: x = next;
671: }
1.1 root 672: }
673:
1.1.1.3 root 674: static inline blockinfo* get_blockinfo_addr_new(void* addr, int /* setstate */)
1.1 root 675: {
676: blockinfo* bi=get_blockinfo_addr(addr);
677: int i;
678:
679: if (!bi) {
680: for (i=0;i<MAX_HOLD_BI && !bi;i++) {
681: if (hold_bi[i]) {
1.1.1.3 root 682: (void)cacheline(addr);
1.1 root 683:
684: bi=hold_bi[i];
685: hold_bi[i]=NULL;
686: bi->pc_p=(uae_u8*)addr;
687: invalidate_block(bi);
688: add_to_active(bi);
689: add_to_cl_list(bi);
690:
691: }
692: }
693: }
694: if (!bi) {
1.1.1.3 root 695: jit_abort("Looking for blockinfo, can't find free one");
696: }
697: return bi;
698: }
699:
700: static void prepare_block(blockinfo* bi);
701:
1.1.1.4 ! root 702: /* Management of blockinfos.
1.1.1.3 root 703:
704: A blockinfo struct is allocated whenever a new block has to be
705: compiled. If the list of free blockinfos is empty, we allocate a new
706: pool of blockinfos and link the newly created blockinfos altogether
707: into the list of free blockinfos. Otherwise, we simply pop a structure
708: of the free list.
709:
710: Blockinfo are lazily deallocated, i.e. chained altogether in the
711: list of free blockinfos whenvever a translation cache flush (hard or
712: soft) request occurs.
713: */
714:
715: template< class T >
716: class LazyBlockAllocator
717: {
718: enum {
719: kPoolSize = 1 + (16384 - sizeof(T) - sizeof(void *)) / sizeof(T)
720: };
721: struct Pool {
722: T chunk[kPoolSize];
723: Pool * next;
724: };
725: Pool * mPools;
726: T * mChunks;
727: public:
728: LazyBlockAllocator() : mPools(0), mChunks(0) { }
729: #ifdef UAE
730: #else
731: ~LazyBlockAllocator();
732: #endif
733: T * acquire();
734: void release(T * const);
735: };
736:
737: #ifdef UAE
738: /* uae_vm_release may do logging, which isn't safe to do when the application
739: * is shutting down. Better to release memory manually with a function call
740: * to a release_all method on shutdown, or even simpler, just let the OS
741: * handle it (we're shutting down anyway). */
742: #else
743: template< class T >
744: LazyBlockAllocator<T>::~LazyBlockAllocator()
745: {
746: Pool * currentPool = mPools;
747: while (currentPool) {
748: Pool * deadPool = currentPool;
749: currentPool = currentPool->next;
750: vm_release(deadPool, sizeof(Pool));
751: }
752: }
753: #endif
754:
755: template< class T >
756: T * LazyBlockAllocator<T>::acquire()
757: {
758: if (!mChunks) {
759: // There is no chunk left, allocate a new pool and link the
760: // chunks into the free list
761: Pool * newPool = (Pool *)vm_acquire(sizeof(Pool), VM_MAP_DEFAULT | VM_MAP_32BIT);
762: if (newPool == VM_MAP_FAILED) {
763: jit_abort("Could not allocate block pool!");
764: }
765: for (T * chunk = &newPool->chunk[0]; chunk < &newPool->chunk[kPoolSize]; chunk++) {
766: chunk->next = mChunks;
767: mChunks = chunk;
768: }
769: newPool->next = mPools;
770: mPools = newPool;
771: }
772: T * chunk = mChunks;
773: mChunks = chunk->next;
774: return chunk;
775: }
776:
777: template< class T >
778: void LazyBlockAllocator<T>::release(T * const chunk)
779: {
780: chunk->next = mChunks;
781: mChunks = chunk;
782: }
783:
784: template< class T >
785: class HardBlockAllocator
786: {
787: public:
788: T * acquire() {
789: T * data = (T *)current_compile_p;
790: current_compile_p += sizeof(T);
791: return data;
792: }
793:
794: void release(T * const chunk) {
795: // Deallocated on invalidation
1.1 root 796: }
1.1.1.3 root 797: };
798:
799: #if USE_SEPARATE_BIA
800: static LazyBlockAllocator<blockinfo> BlockInfoAllocator;
801: static LazyBlockAllocator<checksum_info> ChecksumInfoAllocator;
802: #else
803: static HardBlockAllocator<blockinfo> BlockInfoAllocator;
804: static HardBlockAllocator<checksum_info> ChecksumInfoAllocator;
805: #endif
806:
807: static inline checksum_info *alloc_checksum_info(void)
808: {
809: checksum_info *csi = ChecksumInfoAllocator.acquire();
810: csi->next = NULL;
811: return csi;
812: }
813:
814: static inline void free_checksum_info(checksum_info *csi)
815: {
816: csi->next = NULL;
817: ChecksumInfoAllocator.release(csi);
818: }
1.1 root 819:
1.1.1.3 root 820: static inline void free_checksum_info_chain(checksum_info *csi)
821: {
822: while (csi != NULL) {
823: checksum_info *csi2 = csi->next;
824: free_checksum_info(csi);
825: csi = csi2;
1.1 root 826: }
1.1.1.3 root 827: }
828:
829: static inline blockinfo *alloc_blockinfo(void)
830: {
831: blockinfo *bi = BlockInfoAllocator.acquire();
832: #if USE_CHECKSUM_INFO
833: bi->csi = NULL;
1.1 root 834: #endif
835: return bi;
836: }
837:
1.1.1.3 root 838: static inline void free_blockinfo(blockinfo *bi)
839: {
840: #if USE_CHECKSUM_INFO
841: free_checksum_info_chain(bi->csi);
842: bi->csi = NULL;
843: #endif
844: BlockInfoAllocator.release(bi);
845: }
1.1 root 846:
1.1.1.3 root 847: static inline void alloc_blockinfos(void)
1.1 root 848: {
849: int i;
850: blockinfo* bi;
851:
852: for (i=0;i<MAX_HOLD_BI;i++) {
853: if (hold_bi[i])
854: return;
1.1.1.3 root 855: bi=hold_bi[i]=alloc_blockinfo();
1.1 root 856: prepare_block(bi);
857: }
858: }
859:
860: /********************************************************************
1.1.1.3 root 861: * Functions to emit data into memory, and other general support *
862: ********************************************************************/
1.1 root 863:
1.1.1.3 root 864: static uae_u8* target;
865:
866: static inline void emit_byte(uae_u8 x)
867: {
868: *target++=x;
1.1 root 869: }
870:
1.1.1.3 root 871: static inline void emit_word(uae_u16 x)
872: {
873: *((uae_u16*)target)=x;
874: target+=2;
875: }
1.1 root 876:
1.1.1.3 root 877: static inline void emit_long(uae_u32 x)
878: {
879: *((uae_u32*)target)=x;
880: target+=4;
881: }
1.1 root 882:
1.1.1.3 root 883: static inline void skip_n_bytes(int n) {
884: target += n;
885: }
1.1 root 886:
1.1.1.3 root 887: static inline void skip_byte()
888: {
889: target++;
890: }
1.1 root 891:
1.1.1.3 root 892: static inline void skip_word()
1.1 root 893: {
1.1.1.3 root 894: target += 2;
1.1 root 895: }
896:
1.1.1.3 root 897: static inline void skip_long()
1.1 root 898: {
1.1.1.3 root 899: target += 4;
1.1 root 900: }
901:
1.1.1.3 root 902: static inline void skip_quad()
1.1 root 903: {
1.1.1.3 root 904: target += 8;
1.1 root 905: }
906:
1.1.1.3 root 907: static __inline__ void emit_quad(uae_u64 x)
1.1 root 908: {
1.1.1.3 root 909: *((uae_u64*) target) = x;
910: target += 8;
1.1 root 911: }
912:
1.1.1.3 root 913: static inline void emit_block(const uae_u8 *block, uae_u32 blocklen)
1.1 root 914: {
1.1.1.3 root 915: memcpy((uae_u8 *)target,block,blocklen);
916: target+=blocklen;
1.1 root 917: }
918:
1.1.1.3 root 919: #define MAX_COMPILE_PTR max_compile_start
920:
921: static inline uae_u32 reverse32(uae_u32 v)
922: {
923: #if 0
924: // gb-- We have specialized byteswapping functions, just use them
925: return do_byteswap_32(v);
926: #else
927: return ((v>>24)&0xff) | ((v>>8)&0xff00) | ((v<<8)&0xff0000) | ((v<<24)&0xff000000);
928: #endif
929: }
1.1 root 930:
931: void set_target(uae_u8* t)
932: {
933: target=t;
934: }
935:
1.1.1.3 root 936: static inline uae_u8* get_target_noopt(void)
1.1 root 937: {
938: return target;
939: }
940:
1.1.1.3 root 941: inline uae_u8* get_target(void)
1.1 root 942: {
943: return get_target_noopt();
944: }
945:
1.1.1.3 root 946: /********************************************************************
947: * New version of data buffer: interleave data and code *
948: ********************************************************************/
949: #if defined(USE_DATA_BUFFER)
950:
951: #define DATA_BUFFER_SIZE 1024 // Enlarge POPALLSPACE_SIZE if this value is greater than 768
952: #define DATA_BUFFER_MAXOFFSET 4096 - 32 // max range between emit of data and use of data
953: static uae_u8* data_writepos = 0;
954: static uae_u8* data_endpos = 0;
955: #if DEBUG
956: static long data_wasted = 0;
957: #endif
958:
959: static inline void compemu_raw_branch(IMM d);
960:
961: static inline void data_check_end(long n, long codesize)
962: {
963: if(data_writepos + n > data_endpos || get_target_noopt() + codesize - data_writepos > DATA_BUFFER_MAXOFFSET)
964: {
965: // Start new buffer
966: #if DEBUG
967: if(data_writepos < data_endpos)
968: data_wasted += data_endpos - data_writepos;
969: #endif
970: compemu_raw_branch(DATA_BUFFER_SIZE);
971: data_writepos = get_target_noopt();
972: data_endpos = data_writepos + DATA_BUFFER_SIZE;
973: set_target(get_target_noopt() + DATA_BUFFER_SIZE);
974: }
975: }
976:
977: static inline long data_word_offs(uae_u16 x)
978: {
979: data_check_end(4, 4);
980: #ifdef WORDS_BIGENDIAN
981: *((uae_u16*)data_writepos)=x;
982: data_writepos += 2;
983: *((uae_u16*)data_writepos)=0;
984: data_writepos += 2;
985: #else
986: *((uae_u32*)data_writepos)=x;
987: data_writepos += 4;
988: #endif
989: return (long)data_writepos - (long)get_target_noopt() - 12;
990: }
991:
992: static inline long data_long(uae_u32 x, long codesize)
993: {
994: data_check_end(4, codesize);
995: *((uae_u32*)data_writepos)=x;
996: data_writepos += 4;
997: return (long)data_writepos - 4;
998: }
999:
1000: static inline long data_long_offs(uae_u32 x)
1001: {
1002: data_check_end(4, 4);
1003: *((uae_u32*)data_writepos)=x;
1004: data_writepos += 4;
1005: return (long)data_writepos - (long)get_target_noopt() - 12;
1006: }
1007:
1008: static inline long get_data_offset(long t)
1009: {
1010: return t - (long)get_target_noopt() - 8;
1011: }
1012:
1013: static inline void reset_data_buffer(void)
1014: {
1015: data_writepos = 0;
1016: data_endpos = 0;
1017: }
1.1 root 1018:
1.1.1.3 root 1019: #endif
1.1 root 1020: /********************************************************************
1.1.1.3 root 1021: * Getting the information about the target CPU *
1022: ********************************************************************/
1.1 root 1023:
1.1.1.3 root 1024: #if defined(CPU_arm)
1025: #include "codegen_arm.cpp"
1026: #endif
1027: #if defined(CPU_i386) || defined(CPU_x86_64)
1028: #include "codegen_x86.cpp"
1029: #endif
1.1 root 1030:
1031:
1032: /********************************************************************
1.1.1.3 root 1033: * Flags status handling. EMIT TIME! *
1034: ********************************************************************/
1.1 root 1035:
1.1.1.3 root 1036: static void bt_l_ri_noclobber(RR4 r, IMM i);
1.1 root 1037:
1038: static void make_flags_live_internal(void)
1039: {
1040: if (live.flags_in_flags==VALID)
1041: return;
1042: Dif (live.flags_on_stack==TRASH) {
1.1.1.3 root 1043: jit_abort("Want flags, got something on stack, but it is TRASH");
1.1 root 1044: }
1045: if (live.flags_on_stack==VALID) {
1046: int tmp;
1047: tmp=readreg_specific(FLAGTMP,4,FLAG_NREG2);
1048: raw_reg_to_flags(tmp);
1.1.1.3 root 1049: unlock2(tmp);
1.1 root 1050:
1051: live.flags_in_flags=VALID;
1052: return;
1053: }
1.1.1.3 root 1054: jit_abort("Huh? live.flags_in_flags=%d, live.flags_on_stack=%d, but need to make live",
1.1 root 1055: live.flags_in_flags,live.flags_on_stack);
1056: }
1057:
1058: static void flags_to_stack(void)
1059: {
1060: if (live.flags_on_stack==VALID)
1061: return;
1062: if (!live.flags_are_important) {
1063: live.flags_on_stack=VALID;
1064: return;
1065: }
1066: Dif (live.flags_in_flags!=VALID)
1.1.1.3 root 1067: jit_abort("flags_to_stack != VALID");
1.1 root 1068: else {
1069: int tmp;
1070: tmp=writereg_specific(FLAGTMP,4,FLAG_NREG1);
1071: raw_flags_to_reg(tmp);
1.1.1.3 root 1072: unlock2(tmp);
1.1 root 1073: }
1074: live.flags_on_stack=VALID;
1075: }
1076:
1.1.1.3 root 1077: static inline void clobber_flags(void)
1.1 root 1078: {
1079: if (live.flags_in_flags==VALID && live.flags_on_stack!=VALID)
1080: flags_to_stack();
1081: live.flags_in_flags=TRASH;
1082: }
1083:
1084: /* Prepare for leaving the compiled stuff */
1.1.1.3 root 1085: static inline void flush_flags(void)
1.1 root 1086: {
1087: flags_to_stack();
1088: return;
1089: }
1090:
1091: int touchcnt;
1092:
1093: /********************************************************************
1.1.1.3 root 1094: * Partial register flushing for optimized calls *
1095: ********************************************************************/
1.1 root 1096:
1.1.1.3 root 1097: struct regusage {
1098: uae_u16 rmask;
1099: uae_u16 wmask;
1100: };
1.1 root 1101:
1.1.1.3 root 1102: static inline void ru_set(uae_u16 *mask, int reg)
1103: {
1104: #if USE_OPTIMIZED_CALLS
1105: *mask |= 1 << reg;
1106: #else
1107: UNUSED(mask);
1108: UNUSED(reg);
1109: #endif
1110: }
1.1 root 1111:
1.1.1.3 root 1112: static inline bool ru_get(const uae_u16 *mask, int reg)
1.1 root 1113: {
1.1.1.3 root 1114: #if USE_OPTIMIZED_CALLS
1115: return (*mask & (1 << reg));
1116: #else
1117: UNUSED(mask);
1118: UNUSED(reg);
1119: /* Default: instruction reads & write to register */
1120: return true;
1121: #endif
1122: }
1123:
1124: static inline void ru_set_read(regusage *ru, int reg)
1125: {
1126: ru_set(&ru->rmask, reg);
1127: }
1128:
1129: static inline void ru_set_write(regusage *ru, int reg)
1130: {
1131: ru_set(&ru->wmask, reg);
1132: }
1133:
1134: static inline bool ru_read_p(const regusage *ru, int reg)
1135: {
1136: return ru_get(&ru->rmask, reg);
1137: }
1138:
1139: static inline bool ru_write_p(const regusage *ru, int reg)
1140: {
1141: return ru_get(&ru->wmask, reg);
1142: }
1143:
1144: #if 0
1145: static void ru_fill_ea(regusage *ru, int reg, amodes mode,
1146: wordsizes size, int write_mode)
1147: {
1148: switch (mode) {
1149: case Areg:
1150: reg += 8;
1151: /* fall through */
1152: case Dreg:
1153: ru_set(write_mode ? &ru->wmask : &ru->rmask, reg);
1154: break;
1155: case Ad16:
1156: /* skip displacment */
1157: m68k_pc_offset += 2;
1158: case Aind:
1159: case Aipi:
1160: case Apdi:
1161: ru_set_read(ru, reg+8);
1162: break;
1163: case Ad8r:
1164: ru_set_read(ru, reg+8);
1165: /* fall through */
1166: case PC8r: {
1167: uae_u16 dp = comp_get_iword((m68k_pc_offset+=2)-2);
1168: reg = (dp >> 12) & 15;
1169: ru_set_read(ru, reg);
1170: if (dp & 0x100)
1171: m68k_pc_offset += (((dp & 0x30) >> 3) & 7) + ((dp & 3) * 2);
1172: break;
1173: }
1174: case PC16:
1175: case absw:
1176: case imm0:
1177: case imm1:
1178: m68k_pc_offset += 2;
1179: break;
1180: case absl:
1181: case imm2:
1182: m68k_pc_offset += 4;
1183: break;
1184: case immi:
1185: m68k_pc_offset += (size == sz_long) ? 4 : 2;
1186: break;
1187: }
1188: }
1189:
1190: /* TODO: split into a static initialization part and a dynamic one
1191: (instructions depending on extension words) */
1192:
1193: static void ru_fill(regusage *ru, uae_u32 opcode)
1194: {
1195: m68k_pc_offset += 2;
1196:
1197: /* Default: no register is used or written to */
1198: ru->rmask = 0;
1199: ru->wmask = 0;
1200:
1201: uae_u32 real_opcode = cft_map(opcode);
1202: struct instr *dp = &table68k[real_opcode];
1203:
1204: bool rw_dest = true;
1205: bool handled = false;
1206:
1207: /* Handle some instructions specifically */
1208: uae_u16 ext;
1209: switch (dp->mnemo) {
1210: case i_BFCHG:
1211: case i_BFCLR:
1212: case i_BFEXTS:
1213: case i_BFEXTU:
1214: case i_BFFFO:
1215: case i_BFINS:
1216: case i_BFSET:
1217: case i_BFTST:
1218: ext = comp_get_iword((m68k_pc_offset+=2)-2);
1219: if (ext & 0x800) ru_set_read(ru, (ext >> 6) & 7);
1220: if (ext & 0x020) ru_set_read(ru, ext & 7);
1221: ru_fill_ea(ru, dp->dreg, (amodes)dp->dmode, (wordsizes)dp->size, 1);
1222: if (dp->dmode == Dreg)
1223: ru_set_read(ru, dp->dreg);
1224: switch (dp->mnemo) {
1225: case i_BFEXTS:
1226: case i_BFEXTU:
1227: case i_BFFFO:
1228: ru_set_write(ru, (ext >> 12) & 7);
1229: break;
1230: case i_BFINS:
1231: ru_set_read(ru, (ext >> 12) & 7);
1232: /* fall through */
1233: case i_BFCHG:
1234: case i_BFCLR:
1235: case i_BSET:
1236: if (dp->dmode == Dreg)
1237: ru_set_write(ru, dp->dreg);
1238: break;
1239: }
1240: handled = true;
1241: rw_dest = false;
1242: break;
1243:
1244: case i_BTST:
1245: rw_dest = false;
1246: break;
1247:
1248: case i_CAS:
1249: {
1250: ext = comp_get_iword((m68k_pc_offset+=2)-2);
1251: int Du = ext & 7;
1252: ru_set_read(ru, Du);
1253: int Dc = (ext >> 6) & 7;
1254: ru_set_read(ru, Dc);
1255: ru_set_write(ru, Dc);
1256: break;
1257: }
1258: case i_CAS2:
1259: {
1260: int Dc1, Dc2, Du1, Du2, Rn1, Rn2;
1261: ext = comp_get_iword((m68k_pc_offset+=2)-2);
1262: Rn1 = (ext >> 12) & 15;
1263: Du1 = (ext >> 6) & 7;
1264: Dc1 = ext & 7;
1265: ru_set_read(ru, Rn1);
1266: ru_set_read(ru, Du1);
1267: ru_set_read(ru, Dc1);
1268: ru_set_write(ru, Dc1);
1269: ext = comp_get_iword((m68k_pc_offset+=2)-2);
1270: Rn2 = (ext >> 12) & 15;
1271: Du2 = (ext >> 6) & 7;
1272: Dc2 = ext & 7;
1273: ru_set_read(ru, Rn2);
1274: ru_set_read(ru, Du2);
1275: ru_set_write(ru, Dc2);
1276: break;
1277: }
1278: case i_DIVL: case i_MULL:
1279: m68k_pc_offset += 2;
1280: break;
1281: case i_LEA:
1282: case i_MOVE: case i_MOVEA: case i_MOVE16:
1283: rw_dest = false;
1284: break;
1285: case i_PACK: case i_UNPK:
1286: rw_dest = false;
1287: m68k_pc_offset += 2;
1288: break;
1289: case i_TRAPcc:
1290: m68k_pc_offset += (dp->size == sz_long) ? 4 : 2;
1291: break;
1292: case i_RTR:
1293: /* do nothing, just for coverage debugging */
1294: break;
1295: /* TODO: handle EXG instruction */
1296: }
1297:
1298: /* Handle A-Traps better */
1299: if ((real_opcode & 0xf000) == 0xa000) {
1300: handled = true;
1301: }
1302:
1303: /* Handle EmulOps better */
1304: if ((real_opcode & 0xff00) == 0x7100) {
1305: handled = true;
1306: ru->rmask = 0xffff;
1307: ru->wmask = 0;
1308: }
1309:
1310: if (dp->suse && !handled)
1311: ru_fill_ea(ru, dp->sreg, (amodes)dp->smode, (wordsizes)dp->size, 0);
1312:
1313: if (dp->duse && !handled)
1314: ru_fill_ea(ru, dp->dreg, (amodes)dp->dmode, (wordsizes)dp->size, 1);
1315:
1316: if (rw_dest)
1317: ru->rmask |= ru->wmask;
1318:
1319: handled = handled || dp->suse || dp->duse;
1320:
1321: /* Mark all registers as used/written if the instruction may trap */
1322: if (may_trap(opcode)) {
1323: handled = true;
1324: ru->rmask = 0xffff;
1325: ru->wmask = 0xffff;
1326: }
1327:
1328: if (!handled) {
1329: jit_abort("ru_fill: %04x = { %04x, %04x }",
1330: real_opcode, ru->rmask, ru->wmask);
1331: }
1332: }
1333: #endif
1334:
1335: /********************************************************************
1336: * register allocation per block logging *
1337: ********************************************************************/
1338:
1339: static uae_s8 vstate[VREGS];
1340: static uae_s8 vwritten[VREGS];
1341: static uae_s8 nstate[N_REGS];
1342:
1343: #define L_UNKNOWN -127
1344: #define L_UNAVAIL -1
1345: #define L_NEEDED -2
1346: #define L_UNNEEDED -3
1347:
1348: static inline void big_to_small_state(bigstate * /* b */, smallstate * s)
1349: {
1350: int i;
1351:
1352: for (i = 0; i < VREGS; i++)
1353: s->virt[i] = vstate[i];
1354: for (i = 0; i < N_REGS; i++)
1355: s->nat[i] = nstate[i];
1356: }
1357:
1358: static inline int callers_need_recompile(bigstate * /* b */, smallstate * s)
1359: {
1360: int i;
1361: int reverse = 0;
1362:
1363: for (i = 0; i < VREGS; i++) {
1364: if (vstate[i] != L_UNNEEDED && s->virt[i] == L_UNNEEDED)
1365: return 1;
1366: if (vstate[i] == L_UNNEEDED && s->virt[i] != L_UNNEEDED)
1367: reverse++;
1368: }
1369: for (i = 0; i < N_REGS; i++) {
1370: if (nstate[i] >= 0 && nstate[i] != s->nat[i])
1371: return 1;
1372: if (nstate[i] < 0 && s->nat[i] >= 0)
1373: reverse++;
1374: }
1375: if (reverse >= 2 && USE_MATCH)
1376: return 1; /* In this case, it might be worth recompiling the
1377: * callers */
1378: return 0;
1379: }
1380:
1381: static inline void log_startblock(void)
1382: {
1383: int i;
1384:
1385: for (i = 0; i < VREGS; i++) {
1.1 root 1386: vstate[i]=L_UNKNOWN;
1.1.1.3 root 1387: vwritten[i] = 0;
1388: }
1.1 root 1389: for (i=0;i<N_REGS;i++)
1390: nstate[i]=L_UNKNOWN;
1391: }
1392:
1.1.1.3 root 1393: /* Using an n-reg for a temp variable */
1394: static inline void log_isused(int n)
1.1 root 1395: {
1396: if (nstate[n]==L_UNKNOWN)
1397: nstate[n]=L_UNAVAIL;
1398: }
1399:
1.1.1.3 root 1400: static inline void log_visused(int r)
1.1 root 1401: {
1.1.1.3 root 1402: if (vstate[r] == L_UNKNOWN)
1403: vstate[r] = L_NEEDED;
1404: }
1405:
1406: static inline void do_load_reg(int n, int r)
1407: {
1408: if (r == FLAGTMP)
1409: raw_load_flagreg(n, r);
1410: else if (r == FLAGX)
1411: raw_load_flagx(n, r);
1412: else
1413: compemu_raw_mov_l_rm(n, (uintptr) live.state[r].mem);
1414: }
1415:
1416: static inline void check_load_reg(int n, int r)
1417: {
1418: compemu_raw_mov_l_rm(n, (uintptr) live.state[r].mem);
1419: }
1420:
1421: static inline void log_vwrite(int r)
1422: {
1423: vwritten[r] = 1;
1424: }
1425:
1426: /* Using an n-reg to hold a v-reg */
1427: static inline void log_isreg(int n, int r)
1428: {
1429: if (nstate[n] == L_UNKNOWN && r < 16 && !vwritten[r] && USE_MATCH)
1.1 root 1430: nstate[n]=r;
1.1.1.3 root 1431: else {
1432: do_load_reg(n, r);
1433: if (nstate[n] == L_UNKNOWN)
1434: nstate[n] = L_UNAVAIL;
1435: }
1.1 root 1436: if (vstate[r]==L_UNKNOWN)
1437: vstate[r]=L_NEEDED;
1438: }
1439:
1.1.1.3 root 1440: static inline void log_clobberreg(int r)
1.1 root 1441: {
1442: if (vstate[r]==L_UNKNOWN)
1443: vstate[r]=L_UNNEEDED;
1444: }
1445:
1446: /* This ends all possibility of clever register allocation */
1447:
1.1.1.3 root 1448: static inline void log_flush(void)
1.1 root 1449: {
1450: int i;
1.1.1.3 root 1451:
1.1 root 1452: for (i=0;i<VREGS;i++)
1453: if (vstate[i]==L_UNKNOWN)
1454: vstate[i]=L_NEEDED;
1455: for (i=0;i<N_REGS;i++)
1456: if (nstate[i]==L_UNKNOWN)
1457: nstate[i]=L_UNAVAIL;
1458: }
1459:
1.1.1.3 root 1460: static inline void log_dump(void)
1.1 root 1461: {
1462: int i;
1463:
1464: return;
1465:
1.1.1.3 root 1466: jit_log("----------------------");
1.1 root 1467: for (i=0;i<N_REGS;i++) {
1468: switch(nstate[i]) {
1.1.1.3 root 1469: case L_UNKNOWN:
1470: jit_log("Nat %d : UNKNOWN",i);
1471: break;
1472: case L_UNAVAIL:
1473: jit_log("Nat %d : UNAVAIL",i);
1474: break;
1475: default:
1476: jit_log("Nat %d : %d",i,nstate[i]);
1477: break;
1.1 root 1478: }
1479: }
1480: for (i=0;i<VREGS;i++) {
1.1.1.3 root 1481: if (vstate[i] == L_UNNEEDED) {
1482: jit_log("Virt %d: UNNEEDED",i);
1483: }
1.1 root 1484: }
1485: }
1486:
1487: /********************************************************************
1.1.1.3 root 1488: * register status handling. EMIT TIME! *
1489: ********************************************************************/
1.1 root 1490:
1.1.1.3 root 1491: static inline void set_status(int r, int status)
1.1 root 1492: {
1493: if (status==ISCONST)
1494: log_clobberreg(r);
1495: live.state[r].status=status;
1496: }
1497:
1.1.1.3 root 1498: static inline int isinreg(int r)
1.1 root 1499: {
1500: return live.state[r].status==CLEAN || live.state[r].status==DIRTY;
1501: }
1502:
1.1.1.3 root 1503: static inline void adjust_nreg(int r, uae_u32 val)
1.1 root 1504: {
1505: if (!val)
1506: return;
1.1.1.3 root 1507: compemu_raw_lea_l_brr(r,r,val);
1.1 root 1508: }
1509:
1510: static void tomem(int r)
1511: {
1512: int rr=live.state[r].realreg;
1513:
1514: if (isinreg(r)) {
1.1.1.3 root 1515: if (live.state[r].val && live.nat[rr].nholds==1
1516: && !live.nat[rr].locked) {
1517: jit_log2("RemovingA offset %x from reg %d (%d) at %p", live.state[r].val,r,rr,target);
1.1 root 1518: adjust_nreg(rr,live.state[r].val);
1519: live.state[r].val=0;
1520: live.state[r].dirtysize=4;
1521: set_status(r,DIRTY);
1522: }
1523: }
1524:
1525: if (live.state[r].status==DIRTY) {
1526: switch (live.state[r].dirtysize) {
1.1.1.3 root 1527: case 1: compemu_raw_mov_b_mr((uintptr)live.state[r].mem,rr); break;
1528: case 2: compemu_raw_mov_w_mr((uintptr)live.state[r].mem,rr); break;
1529: case 4: compemu_raw_mov_l_mr((uintptr)live.state[r].mem,rr); break;
1.1 root 1530: default: abort();
1531: }
1.1.1.3 root 1532: log_vwrite(r);
1.1 root 1533: set_status(r,CLEAN);
1534: live.state[r].dirtysize=0;
1535: }
1536: }
1537:
1.1.1.3 root 1538: static inline int isconst(int r)
1.1 root 1539: {
1540: return live.state[r].status==ISCONST;
1541: }
1542:
1543: int is_const(int r)
1544: {
1545: return isconst(r);
1546: }
1547:
1.1.1.3 root 1548: static inline void writeback_const(int r)
1.1 root 1549: {
1550: if (!isconst(r))
1551: return;
1552: Dif (live.state[r].needflush==NF_HANDLER) {
1.1.1.3 root 1553: jit_abort("Trying to write back constant NF_HANDLER!");
1.1 root 1554: }
1555:
1.1.1.3 root 1556: compemu_raw_mov_l_mi((uintptr)live.state[r].mem,live.state[r].val);
1557: log_vwrite(r);
1.1 root 1558: live.state[r].val=0;
1559: set_status(r,INMEM);
1560: }
1561:
1.1.1.3 root 1562: static inline void tomem_c(int r)
1.1 root 1563: {
1564: if (isconst(r)) {
1565: writeback_const(r);
1566: }
1567: else
1568: tomem(r);
1569: }
1570:
1571: static void evict(int r)
1572: {
1573: int rr;
1574:
1575: if (!isinreg(r))
1576: return;
1577: tomem(r);
1578: rr=live.state[r].realreg;
1579:
1580: Dif (live.nat[rr].locked &&
1581: live.nat[rr].nholds==1) {
1.1.1.3 root 1582: jit_abort("register %d in nreg %d is locked!",r,live.state[r].realreg);
1.1 root 1583: }
1584:
1585: live.nat[rr].nholds--;
1586: if (live.nat[rr].nholds!=live.state[r].realind) { /* Was not last */
1587: int topreg=live.nat[rr].holds[live.nat[rr].nholds];
1588: int thisind=live.state[r].realind;
1.1.1.3 root 1589:
1.1 root 1590: live.nat[rr].holds[thisind]=topreg;
1591: live.state[topreg].realind=thisind;
1592: }
1593: live.state[r].realreg=-1;
1594: set_status(r,INMEM);
1595: }
1596:
1.1.1.3 root 1597: static inline void free_nreg(int r)
1.1 root 1598: {
1599: int i=live.nat[r].nholds;
1600:
1601: while (i) {
1602: int vr;
1603:
1604: --i;
1605: vr=live.nat[r].holds[i];
1606: evict(vr);
1607: }
1608: Dif (live.nat[r].nholds!=0) {
1.1.1.3 root 1609: jit_abort("Failed to free nreg %d, nholds is %d",r,live.nat[r].nholds);
1.1 root 1610: }
1611: }
1612:
1613: /* Use with care! */
1.1.1.3 root 1614: static inline void isclean(int r)
1.1 root 1615: {
1616: if (!isinreg(r))
1617: return;
1618: live.state[r].validsize=4;
1619: live.state[r].dirtysize=0;
1620: live.state[r].val=0;
1621: set_status(r,CLEAN);
1622: }
1623:
1.1.1.3 root 1624: static inline void disassociate(int r)
1.1 root 1625: {
1626: isclean(r);
1627: evict(r);
1628: }
1629:
1.1.1.3 root 1630: static inline void set_const(int r, uae_u32 val)
1.1 root 1631: {
1632: disassociate(r);
1633: live.state[r].val=val;
1634: set_status(r,ISCONST);
1635: }
1636:
1.1.1.3 root 1637: static inline uae_u32 get_offset(int r)
1.1 root 1638: {
1639: return live.state[r].val;
1640: }
1641:
1642: static int alloc_reg_hinted(int r, int size, int willclobber, int hint)
1643: {
1644: int bestreg;
1645: uae_s32 when;
1646: int i;
1647: uae_s32 badness=0; /* to shut up gcc */
1648: bestreg=-1;
1649: when=2000000000;
1650:
1.1.1.3 root 1651: /* XXX use a regalloc_order table? */
1652: for (i=0;i<N_REGS;i++) {
1.1 root 1653: badness=live.nat[i].touched;
1654: if (live.nat[i].nholds==0)
1655: badness=0;
1656: if (i==hint)
1657: badness-=200000000;
1658: if (!live.nat[i].locked && badness<when) {
1659: if ((size==1 && live.nat[i].canbyte) ||
1660: (size==2 && live.nat[i].canword) ||
1661: (size==4)) {
1662: bestreg=i;
1663: when=badness;
1664: if (live.nat[i].nholds==0 && hint<0)
1665: break;
1666: if (i==hint)
1667: break;
1668: }
1669: }
1670: }
1671: Dif (bestreg==-1)
1.1.1.3 root 1672: jit_abort("alloc_reg_hinted bestreg=-1");
1.1 root 1673:
1674: if (live.nat[bestreg].nholds>0) {
1675: free_nreg(bestreg);
1676: }
1677: if (isinreg(r)) {
1678: int rr=live.state[r].realreg;
1679: /* This will happen if we read a partially dirty register at a
1680: bigger size */
1681: Dif (willclobber || live.state[r].validsize>=size)
1.1.1.3 root 1682: jit_abort("willclobber || live.state[r].validsize>=size");
1.1 root 1683: Dif (live.nat[rr].nholds!=1)
1.1.1.3 root 1684: jit_abort("live.nat[rr].nholds!=1");
1.1 root 1685: if (size==4 && live.state[r].validsize==2) {
1686: log_isused(bestreg);
1.1.1.3 root 1687: log_visused(r);
1688: compemu_raw_mov_l_rm(bestreg,(uintptr)live.state[r].mem);
1689: compemu_raw_bswap_32(bestreg);
1690: compemu_raw_zero_extend_16_rr(rr,rr);
1691: compemu_raw_zero_extend_16_rr(bestreg,bestreg);
1692: compemu_raw_bswap_32(bestreg);
1693: compemu_raw_lea_l_rr_indexed(rr, rr, bestreg, 1);
1.1 root 1694: live.state[r].validsize=4;
1695: live.nat[rr].touched=touchcnt++;
1696: return rr;
1697: }
1698: if (live.state[r].validsize==1) {
1699: /* Nothing yet */
1700: }
1701: evict(r);
1702: }
1703:
1704: if (!willclobber) {
1705: if (live.state[r].status!=UNDEF) {
1706: if (isconst(r)) {
1.1.1.3 root 1707: compemu_raw_mov_l_ri(bestreg,live.state[r].val);
1.1 root 1708: live.state[r].val=0;
1709: live.state[r].dirtysize=4;
1710: set_status(r,DIRTY);
1711: log_isused(bestreg);
1712: }
1713: else {
1.1.1.3 root 1714: log_isreg(bestreg, r); /* This will also load it! */
1.1 root 1715: live.state[r].dirtysize=0;
1716: set_status(r,CLEAN);
1717: }
1718: }
1719: else {
1720: live.state[r].val=0;
1721: live.state[r].dirtysize=0;
1722: set_status(r,CLEAN);
1723: log_isused(bestreg);
1724: }
1725: live.state[r].validsize=4;
1726: }
1727: else { /* this is the easiest way, but not optimal. FIXME! */
1728: /* Now it's trickier, but hopefully still OK */
1729: if (!isconst(r) || size==4) {
1730: live.state[r].validsize=size;
1731: live.state[r].dirtysize=size;
1732: live.state[r].val=0;
1733: set_status(r,DIRTY);
1.1.1.3 root 1734: if (size == 4) {
1735: log_clobberreg(r);
1.1 root 1736: log_isused(bestreg);
1.1.1.3 root 1737: }
1738: else {
1739: log_visused(r);
1740: log_isused(bestreg);
1741: }
1.1 root 1742: }
1743: else {
1744: if (live.state[r].status!=UNDEF)
1.1.1.3 root 1745: compemu_raw_mov_l_ri(bestreg,live.state[r].val);
1.1 root 1746: live.state[r].val=0;
1747: live.state[r].validsize=4;
1748: live.state[r].dirtysize=4;
1749: set_status(r,DIRTY);
1750: log_isused(bestreg);
1751: }
1752: }
1753: live.state[r].realreg=bestreg;
1754: live.state[r].realind=live.nat[bestreg].nholds;
1755: live.nat[bestreg].touched=touchcnt++;
1756: live.nat[bestreg].holds[live.nat[bestreg].nholds]=r;
1757: live.nat[bestreg].nholds++;
1758:
1759: return bestreg;
1760: }
1761:
1.1.1.3 root 1762: /*
1.1 root 1763: static int alloc_reg(int r, int size, int willclobber)
1764: {
1765: return alloc_reg_hinted(r,size,willclobber,-1);
1766: }
1.1.1.3 root 1767: */
1.1 root 1768:
1.1.1.3 root 1769: static void unlock2(int r)
1.1 root 1770: {
1771: Dif (!live.nat[r].locked)
1.1.1.3 root 1772: jit_abort("unlock2 %d not locked", r);
1.1 root 1773: live.nat[r].locked--;
1774: }
1775:
1776: static void setlock(int r)
1777: {
1778: live.nat[r].locked++;
1779: }
1780:
1781:
1782: static void mov_nregs(int d, int s)
1783: {
1784: int nd=live.nat[d].nholds;
1785: int i;
1786:
1787: if (s==d)
1788: return;
1789:
1790: if (nd>0)
1791: free_nreg(d);
1792:
1793: log_isused(d);
1.1.1.3 root 1794: compemu_raw_mov_l_rr(d,s);
1.1 root 1795:
1796: for (i=0;i<live.nat[s].nholds;i++) {
1797: int vs=live.nat[s].holds[i];
1798:
1799: live.state[vs].realreg=d;
1800: live.state[vs].realind=i;
1801: live.nat[d].holds[i]=vs;
1802: }
1803: live.nat[d].nholds=live.nat[s].nholds;
1804:
1805: live.nat[s].nholds=0;
1806: }
1807:
1808:
1.1.1.3 root 1809: static inline void make_exclusive(int r, int size, int spec)
1.1 root 1810: {
1811: reg_status oldstate;
1812: int rr=live.state[r].realreg;
1813: int nr;
1814: int nind;
1815: int ndirt=0;
1816: int i;
1817:
1818: if (!isinreg(r))
1819: return;
1820: if (live.nat[rr].nholds==1)
1821: return;
1822: for (i=0;i<live.nat[rr].nholds;i++) {
1823: int vr=live.nat[rr].holds[i];
1824: if (vr!=r &&
1825: (live.state[vr].status==DIRTY || live.state[vr].val))
1826: ndirt++;
1827: }
1828: if (!ndirt && size<live.state[r].validsize && !live.nat[rr].locked) {
1829: /* Everything else is clean, so let's keep this register */
1830: for (i=0;i<live.nat[rr].nholds;i++) {
1831: int vr=live.nat[rr].holds[i];
1832: if (vr!=r) {
1833: evict(vr);
1834: i--; /* Try that index again! */
1835: }
1836: }
1837: Dif (live.nat[rr].nholds!=1) {
1.1.1.3 root 1838: jit_abort("natreg %d holds %d vregs, %d not exclusive",
1.1 root 1839: rr,live.nat[rr].nholds,r);
1840: }
1841: return;
1842: }
1843:
1844: /* We have to split the register */
1845: oldstate=live.state[r];
1846:
1847: setlock(rr); /* Make sure this doesn't go away */
1848: /* Forget about r being in the register rr */
1849: disassociate(r);
1850: /* Get a new register, that we will clobber completely */
1851: if (oldstate.status==DIRTY) {
1852: /* If dirtysize is <4, we need a register that can handle the
1853: eventual smaller memory store! Thanks to Quake68k for exposing
1854: this detail ;-) */
1855: nr=alloc_reg_hinted(r,oldstate.dirtysize,1,spec);
1856: }
1857: else {
1858: nr=alloc_reg_hinted(r,4,1,spec);
1859: }
1860: nind=live.state[r].realind;
1861: live.state[r]=oldstate; /* Keep all the old state info */
1862: live.state[r].realreg=nr;
1863: live.state[r].realind=nind;
1864:
1865: if (size<live.state[r].validsize) {
1866: if (live.state[r].val) {
1867: /* Might as well compensate for the offset now */
1.1.1.3 root 1868: compemu_raw_lea_l_brr(nr,rr,oldstate.val);
1.1 root 1869: live.state[r].val=0;
1870: live.state[r].dirtysize=4;
1871: set_status(r,DIRTY);
1872: }
1873: else
1.1.1.3 root 1874: compemu_raw_mov_l_rr(nr,rr); /* Make another copy */
1.1 root 1875: }
1.1.1.3 root 1876: unlock2(rr);
1.1 root 1877: }
1878:
1.1.1.3 root 1879: static inline void add_offset(int r, uae_u32 off)
1.1 root 1880: {
1881: live.state[r].val+=off;
1882: }
1883:
1.1.1.3 root 1884: static inline void remove_offset(int r, int spec)
1.1 root 1885: {
1886: int rr;
1887:
1888: if (isconst(r))
1889: return;
1890: if (live.state[r].val==0)
1891: return;
1892: if (isinreg(r) && live.state[r].validsize<4)
1893: evict(r);
1894:
1895: if (!isinreg(r))
1896: alloc_reg_hinted(r,4,0,spec);
1897:
1898: Dif (live.state[r].validsize!=4) {
1.1.1.3 root 1899: jit_abort("Validsize=%d in remove_offset",live.state[r].validsize);
1.1 root 1900: }
1901: make_exclusive(r,0,-1);
1902: /* make_exclusive might have done the job already */
1903: if (live.state[r].val==0)
1904: return;
1905:
1906: rr=live.state[r].realreg;
1907:
1908: if (live.nat[rr].nholds==1) {
1.1.1.3 root 1909: jit_log2("RemovingB offset %x from reg %d (%d) at %p", live.state[r].val,r,rr,target);
1.1 root 1910: adjust_nreg(rr,live.state[r].val);
1911: live.state[r].dirtysize=4;
1912: live.state[r].val=0;
1913: set_status(r,DIRTY);
1914: return;
1915: }
1.1.1.3 root 1916: jit_abort("Failed in remove_offset");
1.1 root 1917: }
1918:
1.1.1.3 root 1919: static inline void remove_all_offsets(void)
1.1 root 1920: {
1921: int i;
1922:
1923: for (i=0;i<VREGS;i++)
1924: remove_offset(i,-1);
1925: }
1926:
1.1.1.3 root 1927: static inline void flush_reg_count(void)
1928: {
1929: #ifdef RECORD_REGISTER_USAGE
1930: for (int r = 0; r < 16; r++)
1931: if (reg_count_local[r])
1932: ADDQim(reg_count_local[r], ((uintptr)reg_count) + (8 * r), X86_NOREG, X86_NOREG, 1);
1933: #endif
1934: }
1935:
1936: static inline void record_register(int r)
1937: {
1938: #ifdef RECORD_REGISTER_USAGE
1939: if (r < 16)
1940: reg_count_local[r]++;
1941: #else
1942: UNUSED(r);
1943: #endif
1944: }
1945:
1946: static inline int readreg_general(int r, int size, int spec, int can_offset)
1.1 root 1947: {
1948: int n;
1949: int answer=-1;
1950:
1.1.1.3 root 1951: record_register(r);
1.1 root 1952: if (live.state[r].status==UNDEF) {
1.1.1.3 root 1953: jit_log("WARNING: Unexpected read of undefined register %d",r);
1.1 root 1954: }
1955: if (!can_offset)
1956: remove_offset(r,spec);
1957:
1958: if (isinreg(r) && live.state[r].validsize>=size) {
1959: n=live.state[r].realreg;
1960: switch(size) {
1961: case 1:
1962: if (live.nat[n].canbyte || spec>=0) {
1963: answer=n;
1964: }
1965: break;
1966: case 2:
1967: if (live.nat[n].canword || spec>=0) {
1968: answer=n;
1969: }
1970: break;
1971: case 4:
1972: answer=n;
1973: break;
1974: default: abort();
1975: }
1976: if (answer<0)
1977: evict(r);
1978: }
1979: /* either the value was in memory to start with, or it was evicted and
1980: is in memory now */
1981: if (answer<0) {
1982: answer=alloc_reg_hinted(r,spec>=0?4:size,0,spec);
1983: }
1984:
1985: if (spec>=0 && spec!=answer) {
1986: /* Too bad */
1987: mov_nregs(spec,answer);
1988: answer=spec;
1989: }
1990: live.nat[answer].locked++;
1991: live.nat[answer].touched=touchcnt++;
1992: return answer;
1993: }
1994:
1995:
1996:
1997: static int readreg(int r, int size)
1998: {
1999: return readreg_general(r,size,-1,0);
2000: }
2001:
2002: static int readreg_specific(int r, int size, int spec)
2003: {
2004: return readreg_general(r,size,spec,0);
2005: }
2006:
2007: static int readreg_offset(int r, int size)
2008: {
2009: return readreg_general(r,size,-1,1);
2010: }
2011:
1.1.1.3 root 2012: /* writereg_general(r, size, spec)
2013: *
2014: * INPUT
2015: * - r : mid-layer register
2016: * - size : requested size (1/2/4)
2017: * - spec : -1 if find or make a register free, otherwise specifies
2018: * the physical register to use in any case
2019: *
2020: * OUTPUT
2021: * - hard (physical, x86 here) register allocated to virtual register r
2022: */
2023: static inline int writereg_general(int r, int size, int spec)
1.1 root 2024: {
2025: int n;
2026: int answer=-1;
2027:
1.1.1.3 root 2028: record_register(r);
1.1 root 2029: if (size<4) {
2030: remove_offset(r,spec);
2031: }
2032:
2033: make_exclusive(r,size,spec);
2034: if (isinreg(r)) {
2035: int nvsize=size>live.state[r].validsize?size:live.state[r].validsize;
2036: int ndsize=size>live.state[r].dirtysize?size:live.state[r].dirtysize;
2037: n=live.state[r].realreg;
2038:
2039: Dif (live.nat[n].nholds!=1)
1.1.1.3 root 2040: jit_abort("live.nat[%d].nholds!=1", n);
1.1 root 2041: switch(size) {
2042: case 1:
2043: if (live.nat[n].canbyte || spec>=0) {
2044: live.state[r].dirtysize=ndsize;
2045: live.state[r].validsize=nvsize;
2046: answer=n;
2047: }
2048: break;
2049: case 2:
2050: if (live.nat[n].canword || spec>=0) {
2051: live.state[r].dirtysize=ndsize;
2052: live.state[r].validsize=nvsize;
2053: answer=n;
2054: }
2055: break;
2056: case 4:
2057: live.state[r].dirtysize=ndsize;
2058: live.state[r].validsize=nvsize;
2059: answer=n;
2060: break;
2061: default: abort();
2062: }
2063: if (answer<0)
2064: evict(r);
2065: }
2066: /* either the value was in memory to start with, or it was evicted and
2067: is in memory now */
2068: if (answer<0) {
2069: answer=alloc_reg_hinted(r,size,1,spec);
2070: }
2071: if (spec>=0 && spec!=answer) {
2072: mov_nregs(spec,answer);
2073: answer=spec;
2074: }
2075: if (live.state[r].status==UNDEF)
2076: live.state[r].validsize=4;
2077: live.state[r].dirtysize=size>live.state[r].dirtysize?size:live.state[r].dirtysize;
2078: live.state[r].validsize=size>live.state[r].validsize?size:live.state[r].validsize;
2079:
2080: live.nat[answer].locked++;
2081: live.nat[answer].touched=touchcnt++;
2082: if (size==4) {
2083: live.state[r].val=0;
2084: }
2085: else {
2086: Dif (live.state[r].val) {
1.1.1.3 root 2087: jit_abort("Problem with val");
1.1 root 2088: }
2089: }
2090: set_status(r,DIRTY);
2091: return answer;
2092: }
2093:
2094: static int writereg(int r, int size)
2095: {
2096: return writereg_general(r,size,-1);
2097: }
2098:
2099: static int writereg_specific(int r, int size, int spec)
2100: {
2101: return writereg_general(r,size,spec);
2102: }
2103:
1.1.1.3 root 2104: static inline int rmw_general(int r, int wsize, int rsize, int spec)
1.1 root 2105: {
2106: int n;
2107: int answer=-1;
2108:
1.1.1.3 root 2109: record_register(r);
1.1 root 2110: if (live.state[r].status==UNDEF) {
1.1.1.3 root 2111: jit_log("WARNING: Unexpected read of undefined register %d",r);
1.1 root 2112: }
2113: remove_offset(r,spec);
2114: make_exclusive(r,0,spec);
2115:
2116: Dif (wsize<rsize) {
1.1.1.3 root 2117: jit_abort("Cannot handle wsize<rsize in rmw_general()");
1.1 root 2118: }
2119: if (isinreg(r) && live.state[r].validsize>=rsize) {
2120: n=live.state[r].realreg;
2121: Dif (live.nat[n].nholds!=1)
1.1.1.3 root 2122: jit_abort("live.nat[n].nholds!=1", n);
1.1 root 2123:
2124: switch(rsize) {
2125: case 1:
2126: if (live.nat[n].canbyte || spec>=0) {
2127: answer=n;
2128: }
2129: break;
2130: case 2:
2131: if (live.nat[n].canword || spec>=0) {
2132: answer=n;
2133: }
2134: break;
2135: case 4:
2136: answer=n;
2137: break;
2138: default: abort();
2139: }
2140: if (answer<0)
2141: evict(r);
2142: }
2143: /* either the value was in memory to start with, or it was evicted and
2144: is in memory now */
2145: if (answer<0) {
2146: answer=alloc_reg_hinted(r,spec>=0?4:rsize,0,spec);
2147: }
2148:
2149: if (spec>=0 && spec!=answer) {
2150: /* Too bad */
2151: mov_nregs(spec,answer);
2152: answer=spec;
2153: }
2154: if (wsize>live.state[r].dirtysize)
2155: live.state[r].dirtysize=wsize;
2156: if (wsize>live.state[r].validsize)
2157: live.state[r].validsize=wsize;
2158: set_status(r,DIRTY);
2159:
2160: live.nat[answer].locked++;
2161: live.nat[answer].touched=touchcnt++;
2162:
2163: Dif (live.state[r].val) {
1.1.1.3 root 2164: jit_abort("Problem with val(rmw)");
1.1 root 2165: }
2166: return answer;
2167: }
2168:
2169: static int rmw(int r, int wsize, int rsize)
2170: {
2171: return rmw_general(r,wsize,rsize,-1);
2172: }
2173:
2174: static int rmw_specific(int r, int wsize, int rsize, int spec)
2175: {
2176: return rmw_general(r,wsize,rsize,spec);
2177: }
2178:
2179:
2180: /* needed for restoring the carry flag on non-P6 cores */
1.1.1.3 root 2181: static void bt_l_ri_noclobber(RR4 r, IMM i)
1.1 root 2182: {
2183: int size=4;
2184: if (i<16)
2185: size=2;
2186: r=readreg(r,size);
1.1.1.3 root 2187: compemu_raw_bt_l_ri(r,i);
2188: unlock2(r);
1.1 root 2189: }
2190:
2191: /********************************************************************
1.1.1.3 root 2192: * FPU register status handling. EMIT TIME! *
2193: ********************************************************************/
1.1 root 2194:
2195: static void f_tomem(int r)
2196: {
2197: if (live.fate[r].status==DIRTY) {
1.1.1.3 root 2198: #if defined(USE_LONG_DOUBLE)
2199: raw_fmov_ext_mr((uintptr)live.fate[r].mem,live.fate[r].realreg);
1.1 root 2200: #else
1.1.1.3 root 2201: raw_fmov_mr((uintptr)live.fate[r].mem,live.fate[r].realreg);
1.1 root 2202: #endif
2203: live.fate[r].status=CLEAN;
2204: }
2205: }
2206:
2207: static void f_tomem_drop(int r)
2208: {
2209: if (live.fate[r].status==DIRTY) {
1.1.1.3 root 2210: #if defined(USE_LONG_DOUBLE)
2211: raw_fmov_ext_mr_drop((uintptr)live.fate[r].mem,live.fate[r].realreg);
1.1 root 2212: #else
1.1.1.3 root 2213: raw_fmov_mr_drop((uintptr)live.fate[r].mem,live.fate[r].realreg);
1.1 root 2214: #endif
2215: live.fate[r].status=INMEM;
2216: }
2217: }
2218:
2219:
1.1.1.3 root 2220: static inline int f_isinreg(int r)
1.1 root 2221: {
2222: return live.fate[r].status==CLEAN || live.fate[r].status==DIRTY;
2223: }
2224:
2225: static void f_evict(int r)
2226: {
2227: int rr;
2228:
2229: if (!f_isinreg(r))
2230: return;
2231: rr=live.fate[r].realreg;
2232: if (live.fat[rr].nholds==1)
2233: f_tomem_drop(r);
2234: else
2235: f_tomem(r);
2236:
2237: Dif (live.fat[rr].locked &&
2238: live.fat[rr].nholds==1) {
1.1.1.3 root 2239: jit_abort("FPU register %d in nreg %d is locked!",r,live.fate[r].realreg);
1.1 root 2240: }
2241:
2242: live.fat[rr].nholds--;
2243: if (live.fat[rr].nholds!=live.fate[r].realind) { /* Was not last */
2244: int topreg=live.fat[rr].holds[live.fat[rr].nholds];
2245: int thisind=live.fate[r].realind;
2246: live.fat[rr].holds[thisind]=topreg;
2247: live.fate[topreg].realind=thisind;
2248: }
2249: live.fate[r].status=INMEM;
2250: live.fate[r].realreg=-1;
2251: }
2252:
1.1.1.3 root 2253: static inline void f_free_nreg(int r)
1.1 root 2254: {
2255: int i=live.fat[r].nholds;
2256:
2257: while (i) {
2258: int vr;
2259:
2260: --i;
2261: vr=live.fat[r].holds[i];
2262: f_evict(vr);
2263: }
2264: Dif (live.fat[r].nholds!=0) {
1.1.1.3 root 2265: jit_abort("Failed to free nreg %d, nholds is %d",r,live.fat[r].nholds);
1.1 root 2266: }
2267: }
2268:
2269:
2270: /* Use with care! */
1.1.1.3 root 2271: static inline void f_isclean(int r)
1.1 root 2272: {
2273: if (!f_isinreg(r))
2274: return;
2275: live.fate[r].status=CLEAN;
2276: }
2277:
1.1.1.3 root 2278: static inline void f_disassociate(int r)
1.1 root 2279: {
2280: f_isclean(r);
2281: f_evict(r);
2282: }
2283:
2284:
2285:
2286: static int f_alloc_reg(int r, int willclobber)
2287: {
2288: int bestreg;
2289: uae_s32 when;
2290: int i;
2291: uae_s32 badness;
2292: bestreg=-1;
2293: when=2000000000;
2294: for (i=N_FREGS;i--;) {
2295: badness=live.fat[i].touched;
2296: if (live.fat[i].nholds==0)
2297: badness=0;
2298:
2299: if (!live.fat[i].locked && badness<when) {
2300: bestreg=i;
2301: when=badness;
2302: if (live.fat[i].nholds==0)
2303: break;
2304: }
2305: }
2306: Dif (bestreg==-1)
2307: abort();
2308:
2309: if (live.fat[bestreg].nholds>0) {
2310: f_free_nreg(bestreg);
2311: }
2312: if (f_isinreg(r)) {
2313: f_evict(r);
2314: }
2315:
2316: if (!willclobber) {
2317: if (live.fate[r].status!=UNDEF) {
1.1.1.3 root 2318: #if defined(USE_LONG_DOUBLE)
2319: raw_fmov_ext_rm(bestreg,(uintptr)live.fate[r].mem);
1.1 root 2320: #else
1.1.1.3 root 2321: raw_fmov_rm(bestreg,(uintptr)live.fate[r].mem);
1.1 root 2322: #endif
2323: }
2324: live.fate[r].status=CLEAN;
2325: }
2326: else {
2327: live.fate[r].status=DIRTY;
2328: }
2329: live.fate[r].realreg=bestreg;
2330: live.fate[r].realind=live.fat[bestreg].nholds;
2331: live.fat[bestreg].touched=touchcnt++;
2332: live.fat[bestreg].holds[live.fat[bestreg].nholds]=r;
2333: live.fat[bestreg].nholds++;
2334:
2335: return bestreg;
2336: }
2337:
2338: static void f_unlock(int r)
2339: {
2340: Dif (!live.fat[r].locked)
1.1.1.2 root 2341: jit_abort (_T("unlock %d"), r);
1.1 root 2342: live.fat[r].locked--;
2343: }
2344:
2345: static void f_setlock(int r)
2346: {
2347: live.fat[r].locked++;
2348: }
2349:
1.1.1.3 root 2350: static inline int f_readreg(int r)
1.1 root 2351: {
2352: int n;
2353: int answer=-1;
2354:
2355: if (f_isinreg(r)) {
2356: n=live.fate[r].realreg;
2357: answer=n;
2358: }
2359: /* either the value was in memory to start with, or it was evicted and
2360: is in memory now */
2361: if (answer<0)
2362: answer=f_alloc_reg(r,0);
2363:
2364: live.fat[answer].locked++;
2365: live.fat[answer].touched=touchcnt++;
2366: return answer;
2367: }
2368:
1.1.1.3 root 2369: static inline void f_make_exclusive(int r, int clobber)
1.1 root 2370: {
2371: freg_status oldstate;
2372: int rr=live.fate[r].realreg;
2373: int nr;
2374: int nind;
2375: int ndirt=0;
2376: int i;
2377:
2378: if (!f_isinreg(r))
2379: return;
2380: if (live.fat[rr].nholds==1)
2381: return;
2382: for (i=0;i<live.fat[rr].nholds;i++) {
2383: int vr=live.fat[rr].holds[i];
2384: if (vr!=r && live.fate[vr].status==DIRTY)
2385: ndirt++;
2386: }
2387: if (!ndirt && !live.fat[rr].locked) {
2388: /* Everything else is clean, so let's keep this register */
2389: for (i=0;i<live.fat[rr].nholds;i++) {
2390: int vr=live.fat[rr].holds[i];
2391: if (vr!=r) {
2392: f_evict(vr);
2393: i--; /* Try that index again! */
2394: }
2395: }
2396: Dif (live.fat[rr].nholds!=1) {
1.1.1.3 root 2397: jit_log("realreg %d holds %d (",rr,live.fat[rr].nholds);
1.1 root 2398: for (i=0;i<live.fat[rr].nholds;i++) {
1.1.1.3 root 2399: jit_log(" %d(%d,%d)",live.fat[rr].holds[i],
1.1 root 2400: live.fate[live.fat[rr].holds[i]].realreg,
2401: live.fate[live.fat[rr].holds[i]].realind);
2402: }
1.1.1.3 root 2403: jit_log("");
2404: jit_abort("x");
1.1 root 2405: }
2406: return;
2407: }
2408:
2409: /* We have to split the register */
2410: oldstate=live.fate[r];
2411:
2412: f_setlock(rr); /* Make sure this doesn't go away */
2413: /* Forget about r being in the register rr */
2414: f_disassociate(r);
2415: /* Get a new register, that we will clobber completely */
2416: nr=f_alloc_reg(r,1);
2417: nind=live.fate[r].realind;
2418: if (!clobber)
2419: raw_fmov_rr(nr,rr); /* Make another copy */
2420: live.fate[r]=oldstate; /* Keep all the old state info */
2421: live.fate[r].realreg=nr;
2422: live.fate[r].realind=nind;
2423: f_unlock(rr);
2424: }
2425:
2426:
1.1.1.3 root 2427: static inline int f_writereg(int r)
1.1 root 2428: {
2429: int n;
2430: int answer=-1;
2431:
2432: f_make_exclusive(r,1);
2433: if (f_isinreg(r)) {
2434: n=live.fate[r].realreg;
2435: answer=n;
2436: }
2437: if (answer<0) {
2438: answer=f_alloc_reg(r,1);
2439: }
2440: live.fate[r].status=DIRTY;
2441: live.fat[answer].locked++;
2442: live.fat[answer].touched=touchcnt++;
2443: return answer;
2444: }
2445:
2446: static int f_rmw(int r)
2447: {
2448: int n;
2449:
2450: f_make_exclusive(r,0);
2451: if (f_isinreg(r)) {
2452: n=live.fate[r].realreg;
2453: }
2454: else
2455: n=f_alloc_reg(r,0);
2456: live.fate[r].status=DIRTY;
2457: live.fat[n].locked++;
2458: live.fat[n].touched=touchcnt++;
2459: return n;
2460: }
2461:
2462: static void fflags_into_flags_internal(uae_u32 tmp)
2463: {
2464: int r;
2465:
2466: clobber_flags();
2467: r=f_readreg(FP_RESULT);
1.1.1.3 root 2468: if (FFLAG_NREG_CLOBBER_CONDITION) {
2469: int tmp2=tmp;
2470: tmp=writereg_specific(tmp,4,FFLAG_NREG);
2471: raw_fflags_into_flags(r);
2472: unlock2(tmp);
2473: forget_about(tmp2);
2474: }
2475: else
2476: raw_fflags_into_flags(r);
1.1 root 2477: f_unlock(r);
1.1.1.3 root 2478: live_flags();
1.1 root 2479: }
2480:
2481:
1.1.1.3 root 2482: #if defined(CPU_arm)
2483: #include "compemu_midfunc_arm.cpp"
1.1 root 2484:
1.1.1.3 root 2485: #if defined(USE_JIT2)
2486: #include "compemu_midfunc_arm2.cpp"
2487: #endif
2488: #endif
1.1 root 2489:
1.1.1.3 root 2490: #if defined(CPU_i386) || defined(CPU_x86_64)
2491: #include "compemu_midfunc_x86.cpp"
2492: #endif
1.1 root 2493:
2494:
1.1.1.3 root 2495: /********************************************************************
2496: * Support functions exposed to gencomp. CREATE time *
2497: ********************************************************************/
1.1 root 2498:
1.1.1.3 root 2499: void set_zero(int r, int tmp)
1.1 root 2500: {
1.1.1.3 root 2501: if (setzflg_uses_bsf)
2502: bsf_l_rr(r,r);
2503: else
2504: simulate_bsf(tmp,r);
1.1 root 2505: }
2506:
1.1.1.3 root 2507: int kill_rodent(int r)
1.1 root 2508: {
1.1.1.3 root 2509: return KILLTHERAT &&
2510: have_rat_stall &&
2511: (live.state[r].status==INMEM ||
2512: live.state[r].status==CLEAN ||
2513: live.state[r].status==ISCONST ||
2514: live.state[r].dirtysize==4);
1.1 root 2515: }
2516:
1.1.1.3 root 2517: uae_u32 get_const(int r)
1.1 root 2518: {
1.1.1.3 root 2519: Dif (!isconst(r)) {
2520: jit_abort("Register %d should be constant, but isn't",r);
1.1 root 2521: }
1.1.1.3 root 2522: return live.state[r].val;
1.1 root 2523: }
2524:
1.1.1.3 root 2525: void sync_m68k_pc(void)
1.1 root 2526: {
1.1.1.3 root 2527: if (m68k_pc_offset) {
2528: add_l_ri(PC_P,m68k_pc_offset);
2529: comp_pc_p+=m68k_pc_offset;
2530: m68k_pc_offset=0;
2531: }
1.1 root 2532: }
2533:
1.1.1.3 root 2534: /********************************************************************
2535: * Scratch registers management *
2536: ********************************************************************/
1.1 root 2537:
1.1.1.3 root 2538: struct scratch_t {
2539: uae_u32 regs[VREGS];
2540: fpu_register fregs[VFREGS];
2541: };
1.1 root 2542:
1.1.1.3 root 2543: static scratch_t scratch;
1.1 root 2544:
1.1.1.3 root 2545: /********************************************************************
2546: * Support functions exposed to newcpu *
2547: ********************************************************************/
1.1 root 2548:
1.1.1.3 root 2549: static inline const char *str_on_off(bool b)
1.1 root 2550: {
1.1.1.3 root 2551: return b ? "on" : "off";
1.1 root 2552: }
2553:
1.1.1.3 root 2554: #ifdef UAE
2555: static
2556: #endif
2557: void compiler_init(void)
1.1 root 2558: {
1.1.1.3 root 2559: static bool initialized = false;
2560: if (initialized)
2561: return;
1.1 root 2562:
1.1.1.3 root 2563: #ifdef UAE
2564: #else
2565: #ifdef JIT_DEBUG
2566: // JIT debug mode ?
2567: JITDebug = bx_options.startup.debugger;
2568: #endif
2569: jit_log("<JIT compiler> : enable runtime disassemblers : %s", JITDebug ? "yes" : "no");
2570:
2571: #ifdef USE_JIT_FPU
2572: // Use JIT compiler for FPU instructions ?
2573: avoid_fpu = !bx_options.jit.jitfpu;
2574: #else
2575: // JIT FPU is always disabled
2576: avoid_fpu = true;
2577: #endif
2578: jit_log("<JIT compiler> : compile FPU instructions : %s", !avoid_fpu ? "yes" : "no");
1.1 root 2579:
1.1.1.3 root 2580: // Get size of the translation cache (in KB)
2581: cache_size = bx_options.jit.jitcachesize;
2582: jit_log("<JIT compiler> : requested translation cache size : %d KB", cache_size);
1.1 root 2583:
1.1.1.3 root 2584: // Initialize target CPU (check for features, e.g. CMOV, rat stalls)
2585: raw_init_cpu();
2586: setzflg_uses_bsf = target_check_bsf();
2587: jit_log("<JIT compiler> : target processor has CMOV instructions : %s", have_cmov ? "yes" : "no");
2588: jit_log("<JIT compiler> : target processor can suffer from partial register stalls : %s", have_rat_stall ? "yes" : "no");
2589: jit_log("<JIT compiler> : alignment for loops, jumps are %d, %d", align_loops, align_jumps);
2590: #if defined(CPU_i386) || defined(CPU_x86_64)
2591: jit_log("<JIT compiler> : target processor has SSE2 instructions : %s", cpuinfo.x86_has_xmm2 ? "yes" : "no");
2592: jit_log("<JIT compiler> : cache linesize is %lu", (unsigned long)cpuinfo.x86_clflush_size);
2593: #endif
1.1 root 2594:
1.1.1.3 root 2595: // Translation cache flush mechanism
2596: lazy_flush = (bx_options.jit.jitlazyflush == 0) ? false : true;
2597: jit_log("<JIT compiler> : lazy translation cache invalidation : %s", str_on_off(lazy_flush));
2598: flush_icache = lazy_flush ? flush_icache_lazy : flush_icache_hard;
1.1 root 2599:
1.1.1.3 root 2600: // Compiler features
2601: jit_log("<JIT compiler> : register aliasing : %s", str_on_off(1));
2602: jit_log("<JIT compiler> : FP register aliasing : %s", str_on_off(USE_F_ALIAS));
2603: jit_log("<JIT compiler> : lazy constant offsetting : %s", str_on_off(USE_OFFSET));
2604: #if USE_INLINING
2605: follow_const_jumps = bx_options.jit.jitinline;
2606: #endif
2607: jit_log("<JIT compiler> : block inlining : %s", str_on_off(follow_const_jumps));
2608: jit_log("<JIT compiler> : separate blockinfo allocation : %s", str_on_off(USE_SEPARATE_BIA));
1.1 root 2609:
1.1.1.3 root 2610: // Build compiler tables
2611: build_comp();
2612: #endif
1.1 root 2613:
1.1.1.3 root 2614: initialized = true;
1.1 root 2615:
1.1.1.3 root 2616: #ifdef PROFILE_UNTRANSLATED_INSNS
2617: jit_log("<JIT compiler> : gather statistics on untranslated insns count");
2618: #endif
1.1 root 2619:
1.1.1.3 root 2620: #ifdef PROFILE_COMPILE_TIME
2621: jit_log("<JIT compiler> : gather statistics on translation time");
2622: emul_start_time = clock();
2623: #endif
1.1 root 2624: }
2625:
1.1.1.3 root 2626: #ifdef UAE
2627: static
2628: #endif
2629: void compiler_exit(void)
1.1 root 2630: {
1.1.1.3 root 2631: #ifdef PROFILE_COMPILE_TIME
2632: emul_end_time = clock();
2633: #endif
1.1 root 2634:
1.1.1.3 root 2635: #ifdef UAE
2636: #else
2637: #if DEBUG
2638: #if defined(USE_DATA_BUFFER)
2639: printf("data_wasted = %d bytes\n", data_wasted);
2640: #endif
2641: #endif
2642: // Deallocate translation cache
2643: if (compiled_code) {
2644: vm_release(compiled_code, cache_size * 1024);
2645: compiled_code = 0;
1.1 root 2646: }
2647:
1.1.1.3 root 2648: // Deallocate popallspace
2649: if (popallspace) {
2650: vm_release(popallspace, POPALLSPACE_SIZE);
2651: popallspace = 0;
1.1 root 2652: }
1.1.1.3 root 2653: #endif
1.1 root 2654:
1.1.1.3 root 2655: #ifdef PROFILE_COMPILE_TIME
2656: jit_log("### Compile Block statistics");
2657: jit_log("Number of calls to compile_block : %d", compile_count);
2658: uae_u32 emul_time = emul_end_time - emul_start_time;
2659: jit_log("Total emulation time : %.1f sec", double(emul_time)/double(CLOCKS_PER_SEC));
2660: jit_log("Total compilation time : %.1f sec (%.1f%%)", double(compile_time)/double(CLOCKS_PER_SEC), 100.0*double(compile_time)/double(emul_time));
2661: #endif
2662:
2663: #ifdef PROFILE_UNTRANSLATED_INSNS
2664: uae_u64 untranslated_count = 0;
2665: for (int i = 0; i < 65536; i++) {
2666: opcode_nums[i] = i;
2667: untranslated_count += raw_cputbl_count[i];
2668: }
2669: jit_log("Sorting out untranslated instructions count...");
2670: qsort(opcode_nums, 65536, sizeof(uae_u16), untranslated_compfn);
2671: jit_log("Rank Opc Count Name");
2672: for (int i = 0; i < untranslated_top_ten; i++) {
2673: uae_u32 count = raw_cputbl_count[opcode_nums[i]];
2674: struct instr *dp;
2675: struct mnemolookup *lookup;
2676: if (!count)
2677: break;
2678: dp = table68k + opcode_nums[i];
2679: for (lookup = lookuptab; lookup->mnemo != (instrmnem)dp->mnemo; lookup++)
2680: ;
2681: jit_log("%03d: %04x %10u %s", i, opcode_nums[i], count, lookup->name);
1.1 root 2682: }
1.1.1.3 root 2683: #endif
1.1 root 2684:
1.1.1.3 root 2685: #ifdef RECORD_REGISTER_USAGE
2686: int reg_count_ids[16];
2687: uint64 tot_reg_count = 0;
2688: for (int i = 0; i < 16; i++) {
2689: reg_count_ids[i] = i;
2690: tot_reg_count += reg_count[i];
2691: }
2692: qsort(reg_count_ids, 16, sizeof(int), reg_count_compare);
2693: uint64 cum_reg_count = 0;
2694: for (int i = 0; i < 16; i++) {
2695: int r = reg_count_ids[i];
2696: cum_reg_count += reg_count[r];
2697: jit_log("%c%d : %16ld %2.1f%% [%2.1f]", r < 8 ? 'D' : 'A', r % 8,
2698: reg_count[r],
2699: 100.0*double(reg_count[r])/double(tot_reg_count),
2700: 100.0*double(cum_reg_count)/double(tot_reg_count));
1.1 root 2701: }
1.1.1.3 root 2702: #endif
1.1 root 2703: }
2704:
1.1.1.3 root 2705: #ifdef UAE
2706: #else
2707: bool compiler_use_jit(void)
1.1 root 2708: {
1.1.1.3 root 2709: // Check for the "jit" prefs item
2710: if (!bx_options.jit.jit)
2711: return false;
2712:
2713: // Don't use JIT if translation cache size is less then MIN_CACHE_SIZE KB
2714: if (bx_options.jit.jitcachesize < MIN_CACHE_SIZE) {
2715: panicbug("<JIT compiler> : translation cache size is less than %d KB. Disabling JIT.\n", MIN_CACHE_SIZE);
2716: return false;
1.1 root 2717: }
1.1.1.3 root 2718:
2719: return true;
1.1 root 2720: }
1.1.1.3 root 2721: #endif
1.1 root 2722:
1.1.1.3 root 2723: void init_comp(void)
2724: {
2725: int i;
2726: uae_s8* cb=can_byte;
2727: uae_s8* cw=can_word;
2728: uae_s8* au=always_used;
2729:
2730: #ifdef RECORD_REGISTER_USAGE
2731: for (i=0;i<16;i++)
2732: reg_count_local[i] = 0;
2733: #endif
1.1 root 2734:
1.1.1.3 root 2735: for (i=0;i<VREGS;i++) {
2736: live.state[i].realreg=-1;
2737: live.state[i].needflush=NF_SCRATCH;
2738: live.state[i].val=0;
2739: set_status(i,UNDEF);
1.1 root 2740: }
2741:
1.1.1.3 root 2742: for (i=0;i<VFREGS;i++) {
2743: live.fate[i].status=UNDEF;
2744: live.fate[i].realreg=-1;
2745: live.fate[i].needflush=NF_SCRATCH;
1.1 root 2746: }
2747:
1.1.1.3 root 2748: for (i=0;i<VREGS;i++) {
2749: if (i<16) { /* First 16 registers map to 68k registers */
2750: #ifdef UAE
2751: live.state[i].mem=®s.regs[i];
2752: #else
2753: live.state[i].mem=((uae_u32*)®s)+i;
2754: #endif
2755: live.state[i].needflush=NF_TOMEM;
2756: set_status(i,INMEM);
2757: }
2758: else
2759: live.state[i].mem=scratch.regs+i;
1.1 root 2760: }
1.1.1.3 root 2761: live.state[PC_P].mem=(uae_u32*)&(regs.pc_p);
2762: live.state[PC_P].needflush=NF_TOMEM;
2763: set_const(PC_P,(uintptr)comp_pc_p);
1.1 root 2764:
1.1.1.3 root 2765: live.state[FLAGX].mem=(uae_u32*)&(regflags.x);
2766: live.state[FLAGX].needflush=NF_TOMEM;
2767: set_status(FLAGX,INMEM);
1.1 root 2768:
1.1.1.3 root 2769: #if defined(CPU_arm)
2770: live.state[FLAGTMP].mem=(uae_u32*)&(regflags.nzcv);
2771: #else
2772: live.state[FLAGTMP].mem=(uae_u32*)&(regflags.cznv);
2773: #endif
2774: live.state[FLAGTMP].needflush=NF_TOMEM;
2775: set_status(FLAGTMP,INMEM);
1.1 root 2776:
2777: live.state[NEXT_HANDLER].needflush=NF_HANDLER;
2778: set_status(NEXT_HANDLER,UNDEF);
2779:
2780: for (i=0;i<VFREGS;i++) {
2781: if (i<8) { /* First 8 registers map to 68k FPU registers */
1.1.1.3 root 2782: #ifdef UAE
1.1.1.2 root 2783: live.fate[i].mem=(uae_u32*)(®s.fp[i].fp);
1.1.1.3 root 2784: #else
2785: live.fate[i].mem=(uae_u32*)fpu_register_address(i);
2786: #endif
1.1 root 2787: live.fate[i].needflush=NF_TOMEM;
2788: live.fate[i].status=INMEM;
2789: }
2790: else if (i==FP_RESULT) {
1.1.1.3 root 2791: #ifdef UAE
1.1.1.4 ! root 2792: live.fate[i].mem=(uae_u32*)(®s.fp_result.fp);
1.1.1.3 root 2793: #else
2794: live.fate[i].mem=(uae_u32*)(&fpu.result);
2795: #endif
1.1 root 2796: live.fate[i].needflush=NF_TOMEM;
1.1.1.3 root 2797: live.fate[i].status=INMEM;
2798: }
2799: else
2800: live.fate[i].mem=(uae_u32*)(&scratch.fregs[i]);
1.1 root 2801: }
2802:
2803:
1.1.1.3 root 2804: for (i=0;i<N_REGS;i++) {
2805: live.nat[i].touched=0;
2806: live.nat[i].nholds=0;
2807: live.nat[i].locked=0;
2808: if (*cb==i) {
2809: live.nat[i].canbyte=1; cb++;
2810: } else live.nat[i].canbyte=0;
2811: if (*cw==i) {
2812: live.nat[i].canword=1; cw++;
2813: } else live.nat[i].canword=0;
2814: if (*au==i) {
2815: live.nat[i].locked=1; au++;
1.1 root 2816: }
2817: }
2818:
1.1.1.3 root 2819: for (i=0;i<N_FREGS;i++) {
2820: live.fat[i].touched=0;
2821: live.fat[i].nholds=0;
2822: live.fat[i].locked=0;
1.1 root 2823: }
1.1.1.3 root 2824:
2825: touchcnt=1;
2826: m68k_pc_offset=0;
2827: live.flags_in_flags=TRASH;
2828: live.flags_on_stack=VALID;
2829: live.flags_are_important=1;
2830:
2831: raw_fp_init();
1.1 root 2832: }
2833:
2834: /* Only do this if you really mean it! The next call should be to init!*/
2835: void flush(int save_regs)
2836: {
2837: int i;
2838:
2839: log_flush();
2840: flush_flags(); /* low level */
2841: sync_m68k_pc(); /* mid level */
2842:
2843: if (save_regs) {
2844: for (i=0;i<VFREGS;i++) {
2845: if (live.fate[i].needflush==NF_SCRATCH ||
2846: live.fate[i].status==CLEAN) {
2847: f_disassociate(i);
2848: }
2849: }
2850: for (i=0;i<VREGS;i++) {
2851: if (live.state[i].needflush==NF_TOMEM) {
2852: switch(live.state[i].status) {
2853: case INMEM:
2854: if (live.state[i].val) {
1.1.1.3 root 2855: compemu_raw_add_l_mi((uintptr)live.state[i].mem,live.state[i].val);
2856: log_vwrite(i);
1.1 root 2857: live.state[i].val=0;
2858: }
2859: break;
2860: case CLEAN:
2861: case DIRTY:
2862: remove_offset(i,-1); tomem(i); break;
2863: case ISCONST:
2864: if (i!=PC_P)
2865: writeback_const(i);
2866: break;
2867: default: break;
2868: }
2869: Dif (live.state[i].val && i!=PC_P) {
1.1.1.3 root 2870: jit_log("Register %d still has val %x", i,live.state[i].val);
1.1 root 2871: }
2872: }
2873: }
2874: for (i=0;i<VFREGS;i++) {
2875: if (live.fate[i].needflush==NF_TOMEM &&
2876: live.fate[i].status==DIRTY) {
2877: f_evict(i);
2878: }
2879: }
2880: raw_fp_cleanup_drop();
2881: }
2882: if (needflags) {
1.1.1.3 root 2883: jit_log("Warning! flush with needflags=1!");
1.1 root 2884: }
2885: }
2886:
1.1.1.3 root 2887: #if 0
1.1 root 2888: static void flush_keepflags(void)
2889: {
2890: int i;
2891:
2892: for (i=0;i<VFREGS;i++) {
2893: if (live.fate[i].needflush==NF_SCRATCH ||
2894: live.fate[i].status==CLEAN) {
2895: f_disassociate(i);
2896: }
2897: }
2898: for (i=0;i<VREGS;i++) {
2899: if (live.state[i].needflush==NF_TOMEM) {
2900: switch(live.state[i].status) {
2901: case INMEM:
2902: /* Can't adjust the offset here --- that needs "add" */
2903: break;
2904: case CLEAN:
2905: case DIRTY:
2906: remove_offset(i,-1); tomem(i); break;
2907: case ISCONST:
2908: if (i!=PC_P)
2909: writeback_const(i);
2910: break;
2911: default: break;
2912: }
2913: }
2914: }
2915: for (i=0;i<VFREGS;i++) {
2916: if (live.fate[i].needflush==NF_TOMEM &&
2917: live.fate[i].status==DIRTY) {
2918: f_evict(i);
2919: }
2920: }
2921: raw_fp_cleanup_drop();
2922: }
1.1.1.3 root 2923: #endif
1.1 root 2924:
2925: void freescratch(void)
2926: {
2927: int i;
2928: for (i=0;i<N_REGS;i++)
1.1.1.3 root 2929: if (live.nat[i].locked && i!=4 && i!= 12) {
2930: jit_log("Warning! %d is locked",i);
2931: }
1.1 root 2932:
2933: for (i=0;i<VREGS;i++)
2934: if (live.state[i].needflush==NF_SCRATCH) {
2935: forget_about(i);
2936: }
2937:
2938: for (i=0;i<VFREGS;i++)
2939: if (live.fate[i].needflush==NF_SCRATCH) {
2940: f_forget_about(i);
2941: }
2942: }
2943:
2944: /********************************************************************
1.1.1.3 root 2945: * Support functions, internal *
2946: ********************************************************************/
1.1 root 2947:
2948:
2949: static void align_target(uae_u32 a)
2950: {
1.1.1.3 root 2951: if (!a)
2952: return;
2953:
2954: if (tune_nop_fillers)
2955: raw_emit_nop_filler(a - (((uintptr)target) & (a - 1)));
2956: else {
2957: /* Fill with NOPs --- makes debugging with gdb easier */
2958: while ((uintptr)target&(a-1))
2959: *target++=0x90; // Attention x86 specific code
2960: }
1.1 root 2961: }
2962:
1.1.1.3 root 2963: static inline int isinrom(uintptr addr)
1.1 root 2964: {
1.1.1.3 root 2965: #ifdef UAE
2966: return (addr >= uae_p32(kickmem_bank.baseaddr) &&
2967: addr < uae_p32(kickmem_bank.baseaddr + 8 * 65536));
2968: #else
2969: return ((addr >= (uintptr)ROMBaseHost) && (addr < (uintptr)ROMBaseHost + ROMSize));
2970: #endif
1.1 root 2971: }
2972:
2973: static void flush_all(void)
2974: {
2975: int i;
2976:
2977: log_flush();
2978: for (i=0;i<VREGS;i++)
2979: if (live.state[i].status==DIRTY) {
2980: if (!call_saved[live.state[i].realreg]) {
2981: tomem(i);
2982: }
2983: }
2984: for (i=0;i<VFREGS;i++)
2985: if (f_isinreg(i))
2986: f_evict(i);
2987: raw_fp_cleanup_drop();
2988: }
2989:
2990: /* Make sure all registers that will get clobbered by a call are
1.1.1.3 root 2991: save and sound in memory */
1.1 root 2992: static void prepare_for_call_1(void)
2993: {
2994: flush_all(); /* If there are registers that don't get clobbered,
2995: * we should be a bit more selective here */
2996: }
2997:
2998: /* We will call a C routine in a moment. That will clobber all registers,
1.1.1.3 root 2999: so we need to disassociate everything */
1.1 root 3000: static void prepare_for_call_2(void)
3001: {
3002: int i;
3003: for (i=0;i<N_REGS;i++)
3004: if (!call_saved[i] && live.nat[i].nholds>0)
3005: free_nreg(i);
3006:
3007: for (i=0;i<N_FREGS;i++)
3008: if (live.fat[i].nholds>0)
3009: f_free_nreg(i);
3010:
3011: live.flags_in_flags=TRASH; /* Note: We assume we already rescued the
3012: flags at the very start of the call_r
3013: functions! */
3014: }
3015:
3016: /********************************************************************
1.1.1.3 root 3017: * Memory access and related functions, CREATE time *
3018: ********************************************************************/
1.1 root 3019:
3020: void register_branch(uae_u32 not_taken, uae_u32 taken, uae_u8 cond)
3021: {
3022: next_pc_p=not_taken;
3023: taken_pc_p=taken;
3024: branch_cc=cond;
3025: }
3026:
1.1.1.3 root 3027: /* Note: get_handler may fail in 64 Bit environments, if direct_handler_to_use is
3028: * outside 32 bit
3029: */
3030: static uintptr get_handler(uintptr addr)
1.1 root 3031: {
1.1.1.3 root 3032: blockinfo* bi=get_blockinfo_addr_new((void*)(uintptr)addr,0);
3033: return (uintptr)bi->direct_handler_to_use;
1.1 root 3034: }
3035:
3036: /* This version assumes that it is writing *real* memory, and *will* fail
3037: * if that assumption is wrong! No branches, no second chances, just
3038: * straight go-for-it attitude */
3039:
1.1.1.3 root 3040: static void writemem_real(int address, int source, int size, int tmp, int clobber)
1.1 root 3041: {
3042: int f=tmp;
3043:
3044: #ifdef NATMEM_OFFSET
3045: if (canbang) { /* Woohoo! go directly at the memory! */
3046: if (clobber)
3047: f=source;
1.1.1.3 root 3048:
1.1 root 3049: switch(size) {
1.1.1.3 root 3050: case 1: mov_b_bRr(address,source,MEMBaseDiff); break;
3051: case 2: mov_w_rr(f,source); mid_bswap_16(f); mov_w_bRr(address,f,MEMBaseDiff); break;
3052: case 4: mov_l_rr(f,source); mid_bswap_32(f); mov_l_bRr(address,f,MEMBaseDiff); break;
1.1 root 3053: }
3054: forget_about(tmp);
3055: forget_about(f);
3056: return;
3057: }
3058: #endif
3059:
3060: mov_l_rr(f,address);
3061: shrl_l_ri(f,16); /* The index into the baseaddr table */
1.1.1.3 root 3062: mov_l_rm_indexed(f,uae_p32(baseaddr),f,SIZEOF_VOID_P); /* FIXME: is SIZEOF_VOID_P correct? */
1.1 root 3063:
3064: if (address==source) { /* IBrowse does this! */
3065: if (size > 1) {
3066: add_l(f,address); /* f now holds the final address */
3067: switch (size) {
1.1.1.3 root 3068: case 2: mid_bswap_16(source); mov_w_Rr(f,source,0);
3069: mid_bswap_16(source); return;
3070: case 4: mid_bswap_32(source); mov_l_Rr(f,source,0);
3071: mid_bswap_32(source); return;
1.1 root 3072: }
3073: }
3074: }
3075: switch (size) { /* f now holds the offset */
1.1.1.3 root 3076: case 1: mov_b_mrr_indexed(address,f,1,source); break;
3077: case 2: mid_bswap_16(source); mov_w_mrr_indexed(address,f,1,source);
3078: mid_bswap_16(source); break; /* base, index, source */
3079: case 4: mid_bswap_32(source); mov_l_mrr_indexed(address,f,1,source);
3080: mid_bswap_32(source); break;
1.1 root 3081: }
3082: }
3083:
1.1.1.3 root 3084: static inline void writemem(int address, int source, int offset, int size, int tmp)
1.1 root 3085: {
3086: int f=tmp;
3087:
3088: mov_l_rr(f,address);
3089: shrl_l_ri(f,16); /* The index into the mem bank table */
1.1.1.3 root 3090: mov_l_rm_indexed(f,uae_p32(mem_banks),f,SIZEOF_VOID_P); /* FIXME: is SIZEOF_VOID_P correct? */
1.1 root 3091: /* Now f holds a pointer to the actual membank */
3092: mov_l_rR(f,f,offset);
3093: /* Now f holds the address of the b/w/lput function */
3094: call_r_02(f,address,source,4,size);
3095: forget_about(tmp);
3096: }
3097:
3098: void writebyte(int address, int source, int tmp)
3099: {
1.1.1.3 root 3100: if ((special_mem & S_WRITE) || distrust_byte())
3101: writemem_special(address, source, 5 * SIZEOF_VOID_P, 1, tmp);
1.1 root 3102: else
1.1.1.3 root 3103: writemem_real(address,source,1,tmp,0);
1.1 root 3104: }
3105:
1.1.1.3 root 3106: static inline void writeword_general(int address, int source, int tmp,
1.1 root 3107: int clobber)
3108: {
1.1.1.3 root 3109: if ((special_mem & S_WRITE) || distrust_word())
3110: writemem_special(address, source, 4 * SIZEOF_VOID_P, 2, tmp);
1.1 root 3111: else
1.1.1.3 root 3112: writemem_real(address,source,2,tmp,clobber);
1.1 root 3113: }
3114:
3115: void writeword_clobber(int address, int source, int tmp)
3116: {
3117: writeword_general(address,source,tmp,1);
3118: }
3119:
3120: void writeword(int address, int source, int tmp)
3121: {
3122: writeword_general(address,source,tmp,0);
3123: }
3124:
1.1.1.3 root 3125: static inline void writelong_general(int address, int source, int tmp,
1.1 root 3126: int clobber)
3127: {
1.1.1.3 root 3128: if ((special_mem & S_WRITE) || distrust_long())
3129: writemem_special(address, source, 3 * SIZEOF_VOID_P, 4, tmp);
1.1 root 3130: else
1.1.1.3 root 3131: writemem_real(address,source,4,tmp,clobber);
1.1 root 3132: }
3133:
3134: void writelong_clobber(int address, int source, int tmp)
3135: {
3136: writelong_general(address,source,tmp,1);
3137: }
3138:
3139: void writelong(int address, int source, int tmp)
3140: {
3141: writelong_general(address,source,tmp,0);
3142: }
3143:
3144:
3145:
3146: /* This version assumes that it is reading *real* memory, and *will* fail
3147: * if that assumption is wrong! No branches, no second chances, just
3148: * straight go-for-it attitude */
3149:
1.1.1.3 root 3150: static void readmem_real(int address, int dest, int size, int tmp)
1.1 root 3151: {
3152: int f=tmp;
3153:
3154: if (size==4 && address!=dest)
3155: f=dest;
3156:
3157: #ifdef NATMEM_OFFSET
3158: if (canbang) { /* Woohoo! go directly at the memory! */
3159: switch(size) {
1.1.1.3 root 3160: case 1: mov_b_brR(dest,address,MEMBaseDiff); break;
3161: case 2: mov_w_brR(dest,address,MEMBaseDiff); mid_bswap_16(dest); break;
3162: case 4: mov_l_brR(dest,address,MEMBaseDiff); mid_bswap_32(dest); break;
1.1 root 3163: }
3164: forget_about(tmp);
1.1.1.3 root 3165: (void) f;
1.1 root 3166: return;
3167: }
3168: #endif
3169:
3170: mov_l_rr(f,address);
3171: shrl_l_ri(f,16); /* The index into the baseaddr table */
1.1.1.3 root 3172: mov_l_rm_indexed(f,uae_p32(baseaddr),f,SIZEOF_VOID_P); /* FIXME: is SIZEOF_VOID_P correct? */
1.1 root 3173: /* f now holds the offset */
3174:
3175: switch(size) {
1.1.1.3 root 3176: case 1: mov_b_rrm_indexed(dest,address,f,1); break;
3177: case 2: mov_w_rrm_indexed(dest,address,f,1); mid_bswap_16(dest); break;
3178: case 4: mov_l_rrm_indexed(dest,address,f,1); mid_bswap_32(dest); break;
1.1 root 3179: }
3180: forget_about(tmp);
3181: }
3182:
3183:
3184:
1.1.1.3 root 3185: static inline void readmem(int address, int dest, int offset, int size, int tmp)
1.1 root 3186: {
3187: int f=tmp;
3188:
3189: mov_l_rr(f,address);
3190: shrl_l_ri(f,16); /* The index into the mem bank table */
1.1.1.3 root 3191: mov_l_rm_indexed(f,uae_p32(mem_banks),f,SIZEOF_VOID_P); /* FIXME: is SIZEOF_VOID_P correct? */
1.1 root 3192: /* Now f holds a pointer to the actual membank */
3193: mov_l_rR(f,f,offset);
3194: /* Now f holds the address of the b/w/lget function */
3195: call_r_11(dest,f,address,size,4);
3196: forget_about(tmp);
3197: }
3198:
3199: void readbyte(int address, int dest, int tmp)
3200: {
1.1.1.3 root 3201: if ((special_mem & S_READ) || distrust_byte())
3202: readmem_special(address, dest, 2 * SIZEOF_VOID_P, 1, tmp);
1.1 root 3203: else
1.1.1.3 root 3204: readmem_real(address,dest,1,tmp);
1.1 root 3205: }
3206:
3207: void readword(int address, int dest, int tmp)
3208: {
1.1.1.3 root 3209: if ((special_mem & S_READ) || distrust_word())
3210: readmem_special(address, dest, 1 * SIZEOF_VOID_P, 2, tmp);
1.1 root 3211: else
1.1.1.3 root 3212: readmem_real(address,dest,2,tmp);
1.1 root 3213: }
3214:
3215: void readlong(int address, int dest, int tmp)
3216: {
1.1.1.3 root 3217: if ((special_mem & S_READ) || distrust_long())
3218: readmem_special(address, dest, 0 * SIZEOF_VOID_P, 4, tmp);
1.1 root 3219: else
1.1.1.3 root 3220: readmem_real(address,dest,4,tmp);
1.1 root 3221: }
3222:
1.1.1.3 root 3223: void get_n_addr(int address, int dest, int tmp)
1.1 root 3224: {
1.1.1.3 root 3225: if (special_mem || distrust_addr()) {
3226: /* This one might appear a bit odd... */
3227: readmem(address, dest, 6 * SIZEOF_VOID_P, 4, tmp);
3228: return;
3229: }
1.1 root 3230:
1.1.1.3 root 3231: // a is the register containing the virtual address
3232: // after the offset had been fetched
3233: int a=tmp;
3234:
3235: // f is the register that will contain the offset
1.1 root 3236: int f=tmp;
1.1.1.3 root 3237:
3238: // a == f == tmp if (address == dest)
3239: if (address!=dest) {
3240: a=address;
1.1 root 3241: f=dest;
1.1.1.3 root 3242: }
1.1 root 3243:
3244: #ifdef NATMEM_OFFSET
3245: if (canbang) {
1.1.1.3 root 3246: #if FIXED_ADDRESSING
3247: lea_l_brr(dest,address,MEMBaseDiff);
3248: #else
1.1.1.4 ! root 3249: # error "Only fixed addressing mode supported"
1.1.1.3 root 3250: #endif
1.1 root 3251: forget_about(tmp);
1.1.1.3 root 3252: (void) f;
3253: (void) a;
1.1 root 3254: return;
3255: }
3256: #endif
3257: mov_l_rr(f,address);
3258: mov_l_rr(dest,address); // gb-- nop if dest==address
3259: shrl_l_ri(f,16);
1.1.1.3 root 3260: mov_l_rm_indexed(f,uae_p32(baseaddr),f,SIZEOF_VOID_P); /* FIXME: is SIZEOF_VOID_P correct? */
1.1 root 3261: add_l(dest,f);
3262: forget_about(tmp);
3263: }
3264:
3265: void get_n_addr_jmp(int address, int dest, int tmp)
3266: {
1.1.1.3 root 3267: #if 0
3268: /* For this, we need to get the same address as the rest of UAE
3269: would --- otherwise we end up translating everything twice */
1.1 root 3270: get_n_addr(address,dest,tmp);
3271: #else
3272: int f=tmp;
3273: if (address!=dest)
3274: f=dest;
3275: mov_l_rr(f,address);
3276: shrl_l_ri(f,16); /* The index into the baseaddr bank table */
1.1.1.3 root 3277: mov_l_rm_indexed(dest,uae_p32(baseaddr),f,SIZEOF_VOID_P); /* FIXME: is SIZEOF_VOID_P correct? */
1.1 root 3278: add_l(dest,address);
3279: and_l_ri (dest, ~1);
3280: forget_about(tmp);
3281: #endif
3282: }
3283:
1.1.1.3 root 3284:
3285: /* base is a register, but dp is an actual value.
3286: target is a register, as is tmp */
1.1 root 3287: void calc_disp_ea_020(int base, uae_u32 dp, int target, int tmp)
3288: {
3289: int reg = (dp >> 12) & 15;
3290: int regd_shift=(dp >> 9) & 3;
3291:
3292: if (dp & 0x100) {
3293: int ignorebase=(dp&0x80);
3294: int ignorereg=(dp&0x40);
3295: int addbase=0;
3296: int outer=0;
3297:
3298: if ((dp & 0x30) == 0x20) addbase = (uae_s32)(uae_s16)comp_get_iword((m68k_pc_offset+=2)-2);
3299: if ((dp & 0x30) == 0x30) addbase = comp_get_ilong((m68k_pc_offset+=4)-4);
3300:
3301: if ((dp & 0x3) == 0x2) outer = (uae_s32)(uae_s16)comp_get_iword((m68k_pc_offset+=2)-2);
3302: if ((dp & 0x3) == 0x3) outer = comp_get_ilong((m68k_pc_offset+=4)-4);
3303:
3304: if ((dp & 0x4) == 0) { /* add regd *before* the get_long */
3305: if (!ignorereg) {
3306: if ((dp & 0x800) == 0)
3307: sign_extend_16_rr(target,reg);
3308: else
3309: mov_l_rr(target,reg);
3310: shll_l_ri(target,regd_shift);
3311: }
3312: else
3313: mov_l_ri(target,0);
3314:
3315: /* target is now regd */
3316: if (!ignorebase)
3317: add_l(target,base);
3318: add_l_ri(target,addbase);
3319: if (dp&0x03) readlong(target,target,tmp);
3320: } else { /* do the getlong first, then add regd */
3321: if (!ignorebase) {
3322: mov_l_rr(target,base);
3323: add_l_ri(target,addbase);
3324: }
3325: else
3326: mov_l_ri(target,addbase);
3327: if (dp&0x03) readlong(target,target,tmp);
3328:
3329: if (!ignorereg) {
3330: if ((dp & 0x800) == 0)
3331: sign_extend_16_rr(tmp,reg);
3332: else
3333: mov_l_rr(tmp,reg);
3334: shll_l_ri(tmp,regd_shift);
3335: /* tmp is now regd */
3336: add_l(target,tmp);
3337: }
3338: }
3339: add_l_ri(target,outer);
3340: }
3341: else { /* 68000 version */
3342: if ((dp & 0x800) == 0) { /* Sign extend */
3343: sign_extend_16_rr(target,reg);
1.1.1.3 root 3344: lea_l_brr_indexed(target,base,target,1<<regd_shift,(uae_s32)((uae_s8)dp));
1.1 root 3345: }
3346: else {
1.1.1.3 root 3347: lea_l_brr_indexed(target,base,reg,1<<regd_shift,(uae_s32)((uae_s8)dp));
1.1 root 3348: }
3349: }
3350: forget_about(tmp);
3351: }
3352:
1.1.1.3 root 3353:
3354:
3355:
1.1 root 3356:
3357: void set_cache_state(int enabled)
3358: {
3359: if (enabled!=letit)
1.1.1.4 ! root 3360: flush_icache_hard(3);
1.1 root 3361: letit=enabled;
3362: }
3363:
3364: int get_cache_state(void)
3365: {
3366: return letit;
3367: }
3368:
3369: uae_u32 get_jitted_size(void)
3370: {
3371: if (compiled_code)
3372: return current_compile_p-compiled_code;
3373: return 0;
3374: }
3375:
1.1.1.3 root 3376: const int CODE_ALLOC_MAX_ATTEMPTS = 10;
3377: const int CODE_ALLOC_BOUNDARIES = 128 * 1024; // 128 KB
3378:
3379: static uint8 *do_alloc_code(uint32 size, int depth)
3380: {
3381: #if defined(__linux__) && 0
3382: /*
3383: This is a really awful hack that is known to work on Linux at
3384: least.
3385:
3386: The trick here is to make sure the allocated cache is nearby
3387: code segment, and more precisely in the positive half of a
3388: 32-bit address space. i.e. addr < 0x80000000. Actually, it
3389: turned out that a 32-bit binary run on AMD64 yields a cache
3390: allocated around 0xa0000000, thus causing some troubles when
3391: translating addresses from m68k to x86.
3392: */
3393: static uint8 * code_base = NULL;
3394: if (code_base == NULL) {
3395: uintptr page_size = getpagesize();
3396: uintptr boundaries = CODE_ALLOC_BOUNDARIES;
3397: if (boundaries < page_size)
3398: boundaries = page_size;
3399: code_base = (uint8 *)sbrk(0);
3400: for (int attempts = 0; attempts < CODE_ALLOC_MAX_ATTEMPTS; attempts++) {
3401: if (vm_acquire_fixed(code_base, size) == 0) {
3402: uint8 *code = code_base;
3403: code_base += size;
3404: return code;
3405: }
3406: code_base += boundaries;
3407: }
3408: return NULL;
3409: }
3410:
3411: if (vm_acquire_fixed(code_base, size) == 0) {
3412: uint8 *code = code_base;
3413: code_base += size;
3414: return code;
3415: }
3416:
3417: if (depth >= CODE_ALLOC_MAX_ATTEMPTS)
3418: return NULL;
3419:
3420: return do_alloc_code(size, depth + 1);
3421: #else
3422: UNUSED(depth);
3423: uint8 *code = (uint8 *)vm_acquire(size, VM_MAP_DEFAULT | VM_MAP_32BIT);
3424: return code == VM_MAP_FAILED ? NULL : code;
3425: #endif
3426: }
3427:
3428: static inline uint8 *alloc_code(uint32 size)
3429: {
3430: uint8 *ptr = do_alloc_code(size, 0);
3431: /* allocated code must fit in 32-bit boundaries */
3432: assert((uintptr)ptr <= 0xffffffff);
3433: return ptr;
3434: }
3435:
1.1 root 3436: void alloc_cache(void)
3437: {
3438: if (compiled_code) {
1.1.1.4 ! root 3439: flush_icache_hard(3);
1.1.1.3 root 3440: vm_release(compiled_code, cache_size * 1024);
3441: compiled_code = 0;
1.1 root 3442: }
1.1.1.3 root 3443:
3444: #ifdef UAE
3445: cache_size = currprefs.cachesize;
3446: #endif
3447: if (cache_size == 0)
1.1 root 3448: return;
3449:
1.1.1.3 root 3450: while (!compiled_code && cache_size) {
3451: if ((compiled_code = alloc_code(cache_size * 1024)) == NULL) {
3452: compiled_code = 0;
3453: cache_size /= 2;
3454: }
1.1 root 3455: }
1.1.1.3 root 3456: vm_protect(compiled_code, cache_size * 1024, VM_PAGE_READ | VM_PAGE_WRITE | VM_PAGE_EXECUTE);
3457:
1.1 root 3458: if (compiled_code) {
1.1.1.3 root 3459: jit_log("Actual translation cache size : %d KB at %p-%p", cache_size, compiled_code, compiled_code + cache_size*1024);
3460: #ifdef USE_DATA_BUFFER
3461: max_compile_start = compiled_code + cache_size*1024 - BYTES_PER_INST - DATA_BUFFER_SIZE;
3462: #else
3463: max_compile_start = compiled_code + cache_size*1024 - BYTES_PER_INST;
3464: #endif
1.1 root 3465: current_compile_p=compiled_code;
1.1.1.3 root 3466: current_cache_size = 0;
3467: #if defined(USE_DATA_BUFFER)
3468: reset_data_buffer();
3469: #endif
1.1 root 3470: }
3471: }
3472:
3473: static void calc_checksum(blockinfo* bi, uae_u32* c1, uae_u32* c2)
3474: {
3475: uae_u32 k1=0;
3476: uae_u32 k2=0;
3477:
1.1.1.3 root 3478: #if USE_CHECKSUM_INFO
3479: checksum_info *csi = bi->csi;
3480: Dif(!csi) abort();
3481: while (csi) {
3482: uae_s32 len = csi->length;
3483: uintptr tmp = (uintptr)csi->start_p;
3484: #else
3485: uae_s32 len=bi->len;
3486: uintptr tmp = (uintptr)bi->min_pcp;
3487: #endif
3488: uae_u32* pos;
1.1 root 3489:
1.1.1.3 root 3490: len+=(tmp&3);
3491: tmp &= ~((uintptr)3);
3492: pos=(uae_u32*)tmp;
3493:
3494: if (len >= 0 && len <= MAX_CHECKSUM_LEN) {
3495: while (len>0) {
3496: k1+=*pos;
3497: k2^=*pos;
3498: pos++;
3499: len-=4;
3500: }
1.1 root 3501: }
1.1.1.3 root 3502:
3503: #if USE_CHECKSUM_INFO
3504: csi = csi->next;
1.1 root 3505: }
1.1.1.3 root 3506: #endif
3507:
3508: *c1 = k1;
3509: *c2 = k2;
1.1 root 3510: }
3511:
1.1.1.3 root 3512: #if 0
3513: static void show_checksum(CSI_TYPE* csi)
1.1 root 3514: {
3515: uae_u32 k1=0;
3516: uae_u32 k2=0;
1.1.1.3 root 3517: uae_s32 len=CSI_LENGTH(csi);
3518: uae_u32 tmp=(uintptr)CSI_START_P(csi);
1.1 root 3519: uae_u32* pos;
3520:
3521: len+=(tmp&3);
3522: tmp&=(~3);
3523: pos=(uae_u32*)tmp;
3524:
3525: if (len<0 || len>MAX_CHECKSUM_LEN) {
3526: return;
3527: }
3528: else {
3529: while (len>0) {
1.1.1.3 root 3530: jit_log("%08x ",*pos);
1.1 root 3531: pos++;
3532: len-=4;
3533: }
1.1.1.3 root 3534: jit_log(" bla");
1.1 root 3535: }
3536: }
1.1.1.3 root 3537: #endif
1.1 root 3538:
3539:
3540: int check_for_cache_miss(void)
3541: {
3542: blockinfo* bi=get_blockinfo_addr(regs.pc_p);
3543:
3544: if (bi) {
3545: int cl=cacheline(regs.pc_p);
3546: if (bi!=cache_tags[cl+1].bi) {
3547: raise_in_cl_list(bi);
3548: return 1;
3549: }
3550: }
3551: return 0;
3552: }
3553:
3554:
3555: static void recompile_block(void)
3556: {
3557: /* An existing block's countdown code has expired. We need to make
3558: sure that execute_normal doesn't refuse to recompile due to a
3559: perceived cache miss... */
3560: blockinfo* bi=get_blockinfo_addr(regs.pc_p);
3561:
3562: Dif (!bi)
1.1.1.3 root 3563: jit_abort("recompile_block");
1.1 root 3564: raise_in_cl_list(bi);
3565: execute_normal();
3566: return;
3567: }
3568: static void cache_miss(void)
3569: {
3570: blockinfo* bi=get_blockinfo_addr(regs.pc_p);
3571: uae_u32 cl=cacheline(regs.pc_p);
3572: blockinfo* bi2=get_blockinfo(cl);
3573:
3574: if (!bi) {
3575: execute_normal(); /* Compile this block now */
3576: return;
3577: }
3578: Dif (!bi2 || bi==bi2) {
1.1.1.3 root 3579: jit_abort("Unexplained cache miss %p %p",bi,bi2);
1.1 root 3580: }
3581: raise_in_cl_list(bi);
3582: return;
3583: }
3584:
1.1.1.3 root 3585: static int called_check_checksum(blockinfo* bi);
1.1 root 3586:
1.1.1.3 root 3587: static inline int block_check_checksum(blockinfo* bi)
3588: {
1.1 root 3589: uae_u32 c1,c2;
1.1.1.3 root 3590: bool isgood;
3591:
3592: if (bi->status!=BI_NEED_CHECK)
3593: return 1; /* This block is in a checked state */
1.1 root 3594:
3595: checksum_count++;
3596:
3597: if (bi->c1 || bi->c2)
3598: calc_checksum(bi,&c1,&c2);
3599: else {
3600: c1=c2=1; /* Make sure it doesn't match */
3601: }
1.1.1.3 root 3602:
3603: isgood=(c1==bi->c1 && c2==bi->c2);
3604:
3605: if (isgood) {
1.1 root 3606: /* This block is still OK. So we reactivate. Of course, that
3607: means we have to move it into the needs-to-be-flushed list */
3608: bi->handler_to_use=bi->handler;
3609: set_dhtu(bi,bi->direct_handler);
1.1.1.3 root 3610: bi->status=BI_CHECKING;
3611: isgood=called_check_checksum(bi) != 0;
3612: }
3613: if (isgood) {
3614: jit_log2("reactivate %p/%p (%x %x/%x %x)",bi,bi->pc_p, c1,c2,bi->c1,bi->c2);
1.1 root 3615: remove_from_list(bi);
3616: add_to_active(bi);
3617: raise_in_cl_list(bi);
1.1.1.3 root 3618: bi->status=BI_ACTIVE;
1.1 root 3619: }
3620: else {
3621: /* This block actually changed. We need to invalidate it,
3622: and set it up to be recompiled */
1.1.1.3 root 3623: jit_log2("discard %p/%p (%x %x/%x %x)",bi,bi->pc_p, c1,c2,bi->c1,bi->c2);
1.1 root 3624: invalidate_block(bi);
3625: raise_in_cl_list(bi);
1.1.1.3 root 3626: }
3627: return isgood;
3628: }
3629:
3630: static int called_check_checksum(blockinfo* bi)
3631: {
3632: int isgood=1;
3633: int i;
3634:
3635: for (i=0;i<2 && isgood;i++) {
3636: if (bi->dep[i].jmp_off) {
3637: isgood=block_check_checksum(bi->dep[i].target);
3638: }
3639: }
3640: return isgood;
3641: }
3642:
3643: static void check_checksum(void)
3644: {
3645: blockinfo* bi=get_blockinfo_addr(regs.pc_p);
3646: uae_u32 cl=cacheline(regs.pc_p);
3647: blockinfo* bi2=get_blockinfo(cl);
3648:
3649: /* These are not the droids you are looking for... */
3650: if (!bi) {
3651: /* Whoever is the primary target is in a dormant state, but
3652: calling it was accidental, and we should just compile this
3653: new block */
1.1 root 3654: execute_normal();
1.1.1.3 root 3655: return;
3656: }
3657: if (bi!=bi2) {
3658: /* The block was hit accidentally, but it does exist. Cache miss */
3659: cache_miss();
3660: return;
1.1 root 3661: }
1.1.1.3 root 3662:
3663: if (!block_check_checksum(bi))
3664: execute_normal();
1.1 root 3665: }
3666:
1.1.1.3 root 3667: static inline void match_states(blockinfo* bi)
3668: {
3669: int i;
3670: smallstate* s=&(bi->env);
3671:
3672: if (bi->status==BI_NEED_CHECK) {
3673: block_check_checksum(bi);
3674: }
3675: if (bi->status==BI_ACTIVE ||
3676: bi->status==BI_FINALIZING) { /* Deal with the *promises* the
3677: block makes (about not using
3678: certain vregs) */
3679: for (i=0;i<16;i++) {
3680: if (s->virt[i]==L_UNNEEDED) {
3681: jit_log2("unneeded reg %d at %p",i,target);
3682: COMPCALL(forget_about)(i); // FIXME
3683: }
3684: }
3685: }
3686: flush(1);
3687:
3688: /* And now deal with the *demands* the block makes */
3689: for (i=0;i<N_REGS;i++) {
3690: int v=s->nat[i];
3691: if (v>=0) {
3692: // printf("Loading reg %d into %d at %p\n",v,i,target);
3693: readreg_specific(v,4,i);
3694: // do_load_reg(i,v);
3695: // setlock(i);
3696: }
3697: }
3698: for (i=0;i<N_REGS;i++) {
3699: int v=s->nat[i];
3700: if (v>=0) {
3701: unlock2(i);
3702: }
3703: }
3704: }
1.1 root 3705:
1.1.1.3 root 3706: static inline void create_popalls(void)
1.1 root 3707: {
3708: int i,r;
3709:
1.1.1.3 root 3710: #ifdef UAE
3711: if (popallspace == NULL) {
3712: #endif
3713: if ((popallspace = alloc_code(POPALLSPACE_SIZE)) == NULL) {
3714: #ifdef UAE
3715: jit_log("WARNING: Could not allocate popallspace!");
3716: if (currprefs.cachesize > 0) {
3717: #endif
3718: jit_abort("Could not allocate popallspace!");
3719: #ifdef UAE
3720: }
3721: /* This is not fatal if JIT is not used. If JIT is
3722: * turned on, it will crash, but it would have crashed
3723: * anyway. */
3724: return;
3725: }
3726: }
3727: #endif
3728: vm_protect(popallspace, POPALLSPACE_SIZE, VM_PAGE_READ | VM_PAGE_WRITE);
3729:
3730: int stack_space = STACK_OFFSET;
1.1 root 3731: for (i=0;i<N_REGS;i++) {
3732: if (need_to_preserve[i])
1.1.1.3 root 3733: stack_space += sizeof(void *);
1.1 root 3734: }
1.1.1.3 root 3735: stack_space %= STACK_ALIGN;
3736: if (stack_space)
3737: stack_space = STACK_ALIGN - stack_space;
3738:
3739: current_compile_p=popallspace;
3740: set_target(current_compile_p);
3741:
3742: #if defined(USE_DATA_BUFFER)
3743: reset_data_buffer();
3744: #endif
1.1 root 3745:
1.1.1.3 root 3746: /* We need to guarantee 16-byte stack alignment on x86 at any point
3747: within the JIT generated code. We have multiple exit points
3748: possible but a single entry. A "jmp" is used so that we don't
3749: have to generate stack alignment in generated code that has to
3750: call external functions (e.g. a generic instruction handler).
3751:
3752: In summary, JIT generated code is not leaf so we have to deal
3753: with it here to maintain correct stack alignment. */
3754: align_target(align_jumps);
3755: current_compile_p=get_target();
3756: pushall_call_handler=get_target();
3757: raw_push_regs_to_preserve();
3758: raw_dec_sp(stack_space);
3759: r=REG_PC_TMP;
3760: compemu_raw_mov_l_rm(r, uae_p32(®s.pc_p));
3761: compemu_raw_and_l_ri(r,TAGMASK);
3762: compemu_raw_jmp_m_indexed(uae_p32(cache_tags), r, SIZEOF_VOID_P);
3763:
3764: /* now the exit points */
3765: align_target(align_jumps);
3766: popall_do_nothing=get_target();
3767: raw_inc_sp(stack_space);
3768: raw_pop_preserved_regs();
3769: compemu_raw_jmp(uae_p32(do_nothing));
3770:
3771: align_target(align_jumps);
1.1 root 3772: popall_execute_normal=get_target();
1.1.1.3 root 3773: raw_inc_sp(stack_space);
3774: raw_pop_preserved_regs();
3775: compemu_raw_jmp(uae_p32(execute_normal));
1.1 root 3776:
1.1.1.3 root 3777: align_target(align_jumps);
1.1 root 3778: popall_cache_miss=get_target();
1.1.1.3 root 3779: raw_inc_sp(stack_space);
3780: raw_pop_preserved_regs();
3781: compemu_raw_jmp(uae_p32(cache_miss));
1.1 root 3782:
1.1.1.3 root 3783: align_target(align_jumps);
1.1 root 3784: popall_recompile_block=get_target();
1.1.1.3 root 3785: raw_inc_sp(stack_space);
3786: raw_pop_preserved_regs();
3787: compemu_raw_jmp(uae_p32(recompile_block));
1.1 root 3788:
1.1.1.3 root 3789: align_target(align_jumps);
1.1 root 3790: popall_exec_nostats=get_target();
1.1.1.3 root 3791: raw_inc_sp(stack_space);
3792: raw_pop_preserved_regs();
3793: compemu_raw_jmp(uae_p32(exec_nostats));
1.1 root 3794:
1.1.1.3 root 3795: align_target(align_jumps);
1.1 root 3796: popall_check_checksum=get_target();
1.1.1.3 root 3797: raw_inc_sp(stack_space);
3798: raw_pop_preserved_regs();
3799: compemu_raw_jmp(uae_p32(check_checksum));
1.1 root 3800:
1.1.1.3 root 3801: #if defined(USE_DATA_BUFFER)
3802: reset_data_buffer();
1.1 root 3803: #endif
3804:
1.1.1.3 root 3805: #ifdef UAE
3806: #ifdef USE_UDIS86
3807: UDISFN(pushall_call_handler, get_target());
1.1 root 3808: #endif
1.1.1.3 root 3809: #endif
3810: // no need to further write into popallspace
3811: vm_protect(popallspace, POPALLSPACE_SIZE, VM_PAGE_READ | VM_PAGE_EXECUTE);
3812: // No need to flush. Initialized and not modified
3813: // flush_cpu_icache((void *)popallspace, (void *)target);
1.1 root 3814: }
3815:
1.1.1.3 root 3816: static inline void reset_lists(void)
1.1 root 3817: {
3818: int i;
3819:
3820: for (i=0;i<MAX_HOLD_BI;i++)
3821: hold_bi[i]=NULL;
3822: active=NULL;
3823: dormant=NULL;
3824: }
3825:
3826: static void prepare_block(blockinfo* bi)
3827: {
3828: int i;
3829:
3830: set_target(current_compile_p);
1.1.1.3 root 3831: align_target(align_jumps);
1.1 root 3832: bi->direct_pen=(cpuop_func*)get_target();
1.1.1.3 root 3833: compemu_raw_mov_l_rm(0,(uintptr)&(bi->pc_p));
3834: compemu_raw_mov_l_mr((uintptr)®s.pc_p,0);
3835: compemu_raw_jmp((uintptr)popall_execute_normal);
1.1 root 3836:
1.1.1.3 root 3837: align_target(align_jumps);
1.1 root 3838: bi->direct_pcc=(cpuop_func*)get_target();
1.1.1.3 root 3839: compemu_raw_mov_l_rm(0,(uintptr)&(bi->pc_p));
3840: compemu_raw_mov_l_mr((uintptr)®s.pc_p,0);
3841: compemu_raw_jmp((uintptr)popall_check_checksum);
3842: flush_cpu_icache((void *)current_compile_p, (void *)target);
1.1 root 3843: current_compile_p=get_target();
3844:
3845: bi->deplist=NULL;
3846: for (i=0;i<2;i++) {
3847: bi->dep[i].prev_p=NULL;
3848: bi->dep[i].next=NULL;
3849: }
3850: bi->env=default_ss;
1.1.1.3 root 3851: bi->status=BI_INVALID;
1.1 root 3852: bi->havestate=0;
3853: //bi->env=empty_ss;
3854: }
3855:
3856: void compemu_reset(void)
3857: {
3858: set_cache_state(0);
3859: }
3860:
1.1.1.3 root 3861: #ifdef UAE
3862: #else
3863: // OPCODE is in big endian format, use cft_map() beforehand, if needed.
3864: #endif
3865: static inline void reset_compop(int opcode)
3866: {
3867: compfunctbl[opcode] = NULL;
3868: nfcompfunctbl[opcode] = NULL;
3869: }
3870:
3871: static int read_opcode(const char *p)
3872: {
3873: int opcode = 0;
3874: for (int i = 0; i < 4; i++) {
3875: int op = p[i];
3876: switch (op) {
3877: case '0': case '1': case '2': case '3': case '4':
3878: case '5': case '6': case '7': case '8': case '9':
3879: opcode = (opcode << 4) | (op - '0');
3880: break;
3881: case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
3882: opcode = (opcode << 4) | ((op - 'a') + 10);
3883: break;
3884: case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
3885: opcode = (opcode << 4) | ((op - 'A') + 10);
3886: break;
3887: default:
3888: return -1;
3889: }
3890: }
3891: return opcode;
3892: }
3893:
3894: static bool merge_blacklist()
3895: {
3896: #ifdef UAE
3897: const char *blacklist = "";
3898: #else
3899: const char *blacklist = bx_options.jit.jitblacklist;
3900: #endif
3901: if (blacklist[0] != '\0') {
3902: const char *p = blacklist;
3903: for (;;) {
3904: if (*p == 0)
3905: return true;
3906:
3907: int opcode1 = read_opcode(p);
3908: if (opcode1 < 0)
3909: return false;
3910: p += 4;
3911:
3912: int opcode2 = opcode1;
3913: if (*p == '-') {
3914: p++;
3915: opcode2 = read_opcode(p);
3916: if (opcode2 < 0)
3917: return false;
3918: p += 4;
3919: }
3920:
3921: if (*p == 0 || *p == ',') {
3922: jit_log("blacklist opcodes : %04x-%04x", opcode1, opcode2);
3923: for (int opcode = opcode1; opcode <= opcode2; opcode++)
3924: reset_compop(cft_map(opcode));
3925:
3926: if (*(p++) == ',')
3927: continue;
3928:
3929: return true;
3930: }
3931:
3932: return false;
3933: }
3934: }
3935: return true;
3936: }
3937:
1.1 root 3938: void build_comp(void)
3939: {
3940: int i;
3941: unsigned long opcode;
3942: const struct comptbl* tbl=op_smalltbl_0_comp_ff;
3943: const struct comptbl* nftbl=op_smalltbl_0_comp_nf;
3944: int count;
3945: #ifdef NOFLAGS_SUPPORT
3946: struct comptbl *nfctbl = (currprefs.cpu_level >= 5 ? op_smalltbl_0_nf
3947: : currprefs.cpu_level == 4 ? op_smalltbl_1_nf
3948: : (currprefs.cpu_level == 2 || currprefs.cpu_level == 3) ? op_smalltbl_2_nf
3949: : currprefs.cpu_level == 1 ? op_smalltbl_3_nf
3950: : ! currprefs.cpu_compatible ? op_smalltbl_4_nf
3951: : op_smalltbl_5_nf);
3952: #endif
3953: raw_init_cpu();
1.1.1.3 root 3954:
1.1 root 3955: #ifdef NATMEM_OFFSET
1.1.1.3 root 3956: #ifdef UAE
3957: #ifdef JIT_EXCEPTION_HANDLER
3958: install_exception_handler();
3959: #endif
3960: #else
3961: signal(SIGSEGV, (sighandler_t)segfault_vec);
3962: D(bug("<JIT compiler> : NATMEM OFFSET handler installed"));
1.1 root 3963: #endif
3964: #endif
1.1.1.3 root 3965:
3966: jit_log("Building compiler function tables");
3967:
1.1 root 3968: for (opcode = 0; opcode < 65536; opcode++) {
1.1.1.3 root 3969: reset_compop(opcode);
1.1 root 3970: #ifdef NOFLAGS_SUPPORT
3971: nfcpufunctbl[opcode] = op_illg;
3972: #endif
3973: prop[opcode].use_flags = 0x1f;
3974: prop[opcode].set_flags = 0x1f;
3975: prop[opcode].is_jump=1;
3976: }
3977:
3978: for (i = 0; tbl[i].opcode < 65536; i++) {
1.1.1.3 root 3979: int isjmp = (tbl[i].specific & COMP_OPCODE_ISJUMP);
3980: int isaddx = (tbl[i].specific & COMP_OPCODE_ISADDX);
3981: int iscjmp = (tbl[i].specific & COMP_OPCODE_ISCJUMP);
3982:
3983: prop[cft_map(tbl[i].opcode)].is_jump = isjmp;
3984: prop[cft_map(tbl[i].opcode)].is_const_jump = iscjmp;
3985: prop[cft_map(tbl[i].opcode)].is_addx = isaddx;
3986:
3987: bool uses_fpu = (tbl[i].specific & COMP_OPCODE_USES_FPU) != 0;
3988: if (uses_fpu && avoid_fpu)
3989: compfunctbl[cft_map(tbl[i].opcode)] = NULL;
3990: else
3991: compfunctbl[cft_map(tbl[i].opcode)] = tbl[i].handler;
1.1 root 3992: }
1.1.1.3 root 3993:
1.1 root 3994: for (i = 0; nftbl[i].opcode < 65536; i++) {
1.1.1.3 root 3995: nfcompfunctbl[cft_map(nftbl[i].opcode)] = nftbl[i].handler;
1.1 root 3996: #ifdef NOFLAGS_SUPPORT
1.1.1.3 root 3997: nfcpufunctbl[cft_map(nftbl[i].opcode)] = nfctbl[i].handler;
1.1 root 3998: #endif
3999: }
4000:
4001: #ifdef NOFLAGS_SUPPORT
4002: for (i = 0; nfctbl[i].handler; i++) {
1.1.1.3 root 4003: nfcpufunctbl[cft_map(nfctbl[i].opcode)] = nfctbl[i].handler;
1.1 root 4004: }
4005: #endif
4006:
4007: for (opcode = 0; opcode < 65536; opcode++) {
4008: compop_func *f;
4009: compop_func *nff;
4010: #ifdef NOFLAGS_SUPPORT
1.1.1.3 root 4011: cpuop_func *nfcf;
1.1 root 4012: #endif
4013: int isjmp,isaddx,iscjmp;
4014:
1.1.1.3 root 4015: int cpu_level = (currprefs.cpu_model - 68000) / 10;
4016: if (cpu_level > 4)
4017: cpu_level--;
4018: if ((instrmnem)table68k[opcode].mnemo == i_ILLG || table68k[opcode].clev > cpu_level)
1.1 root 4019: continue;
4020:
4021: if (table68k[opcode].handler != -1) {
1.1.1.3 root 4022: f = compfunctbl[cft_map(table68k[opcode].handler)];
4023: nff = nfcompfunctbl[cft_map(table68k[opcode].handler)];
1.1 root 4024: #ifdef NOFLAGS_SUPPORT
1.1.1.3 root 4025: nfcf = nfcpufunctbl[cft_map(table68k[opcode].handler)];
1.1 root 4026: #endif
1.1.1.3 root 4027: isjmp = prop[cft_map(table68k[opcode].handler)].is_jump;
4028: iscjmp = prop[cft_map(table68k[opcode].handler)].is_const_jump;
4029: isaddx = prop[cft_map(table68k[opcode].handler)].is_addx;
4030: prop[cft_map(opcode)].is_jump = isjmp;
4031: prop[cft_map(opcode)].is_const_jump = iscjmp;
4032: prop[cft_map(opcode)].is_addx = isaddx;
4033: compfunctbl[cft_map(opcode)] = f;
4034: nfcompfunctbl[cft_map(opcode)] = nff;
1.1 root 4035: #ifdef NOFLAGS_SUPPORT
4036: Dif (nfcf == op_illg)
4037: abort();
1.1.1.3 root 4038: nfcpufunctbl[cft_map(opcode)] = nfcf;
1.1 root 4039: #endif
4040: }
1.1.1.3 root 4041: prop[cft_map(opcode)].set_flags = table68k[opcode].flagdead;
4042: prop[cft_map(opcode)].use_flags = table68k[opcode].flaglive;
1.1 root 4043: /* Unconditional jumps don't evaluate condition codes, so they
1.1.1.3 root 4044: * don't actually use any flags themselves */
4045: if (prop[cft_map(opcode)].is_const_jump)
4046: prop[cft_map(opcode)].use_flags = 0;
1.1 root 4047: }
4048: #ifdef NOFLAGS_SUPPORT
4049: for (i = 0; nfctbl[i].handler != NULL; i++) {
4050: if (nfctbl[i].specific)
1.1.1.3 root 4051: nfcpufunctbl[cft_map(tbl[i].opcode)] = nfctbl[i].handler;
1.1 root 4052: }
4053: #endif
4054:
1.1.1.3 root 4055: /* Merge in blacklist */
4056: if (!merge_blacklist())
4057: jit_log("blacklist merge failure!");
4058:
1.1 root 4059: count=0;
4060: for (opcode = 0; opcode < 65536; opcode++) {
1.1.1.3 root 4061: if (compfunctbl[cft_map(opcode)])
1.1 root 4062: count++;
4063: }
1.1.1.3 root 4064: jit_log("Supposedly %d compileable opcodes!",count);
1.1 root 4065:
4066: /* Initialise state */
4067: create_popalls();
1.1.1.3 root 4068: alloc_cache();
1.1 root 4069: reset_lists();
4070:
4071: for (i=0;i<TAGSIZE;i+=2) {
4072: cache_tags[i].handler=(cpuop_func*)popall_execute_normal;
4073: cache_tags[i+1].bi=NULL;
4074: }
4075: compemu_reset();
4076:
1.1.1.3 root 4077: #if 0
1.1 root 4078: for (i=0;i<N_REGS;i++) {
4079: empty_ss.nat[i].holds=-1;
4080: empty_ss.nat[i].validsize=0;
4081: empty_ss.nat[i].dirtysize=0;
4082: }
4083: #endif
1.1.1.3 root 4084: for (i=0;i<VREGS;i++) {
4085: empty_ss.virt[i]=L_NEEDED;
4086: }
4087: for (i=0;i<N_REGS;i++) {
4088: empty_ss.nat[i]=L_UNKNOWN;
4089: }
4090: default_ss=empty_ss;
1.1 root 4091: }
4092:
4093:
1.1.1.3 root 4094: static void flush_icache_none(int)
1.1 root 4095: {
1.1.1.3 root 4096: /* Nothing to do. */
4097: }
4098:
1.1.1.4 ! root 4099: void flush_icache_hard(int n)
1.1.1.3 root 4100: {
4101: blockinfo* bi, *dbi;
1.1 root 4102:
4103: hard_flush_count++;
4104: #if 0
1.1.1.2 root 4105: write_log (_T("JIT: Flush Icache_hard(%d/%x/%p), %u instruction bytes\n"),
1.1 root 4106: n,regs.pc,regs.pc_p,current_compile_p-compiled_code);
4107: #endif
4108: bi=active;
4109: while(bi) {
4110: cache_tags[cacheline(bi->pc_p)].handler=(cpuop_func*)popall_execute_normal;
4111: cache_tags[cacheline(bi->pc_p)+1].bi=NULL;
1.1.1.3 root 4112: dbi=bi; bi=bi->next;
4113: free_blockinfo(dbi);
1.1 root 4114: }
4115: bi=dormant;
4116: while(bi) {
4117: cache_tags[cacheline(bi->pc_p)].handler=(cpuop_func*)popall_execute_normal;
4118: cache_tags[cacheline(bi->pc_p)+1].bi=NULL;
1.1.1.3 root 4119: dbi=bi; bi=bi->next;
4120: free_blockinfo(dbi);
1.1 root 4121: }
4122:
4123: reset_lists();
4124: if (!compiled_code)
4125: return;
4126: current_compile_p=compiled_code;
4127: set_special(0); /* To get out of compiled code */
4128: }
4129:
4130:
4131: /* "Soft flushing" --- instead of actually throwing everything away,
1.1.1.3 root 4132: we simply mark everything as "needs to be checked".
1.1 root 4133: */
4134:
1.1.1.4 ! root 4135: void flush_icache(int n)
1.1 root 4136: {
4137: blockinfo* bi;
4138: blockinfo* bi2;
4139:
4140: if (currprefs.comp_hardflush) {
1.1.1.4 ! root 4141: flush_icache_hard(n);
1.1 root 4142: return;
4143: }
4144: soft_flush_count++;
4145: if (!active)
4146: return;
4147:
4148: bi=active;
4149: while (bi) {
4150: uae_u32 cl=cacheline(bi->pc_p);
1.1.1.3 root 4151: if (bi->status==BI_INVALID ||
4152: bi->status==BI_NEED_RECOMP) {
1.1 root 4153: if (bi==cache_tags[cl+1].bi)
4154: cache_tags[cl].handler=(cpuop_func*)popall_execute_normal;
4155: bi->handler_to_use=(cpuop_func*)popall_execute_normal;
4156: set_dhtu(bi,bi->direct_pen);
1.1.1.3 root 4157: bi->status=BI_INVALID;
4158: }
4159: else {
1.1 root 4160: if (bi==cache_tags[cl+1].bi)
4161: cache_tags[cl].handler=(cpuop_func*)popall_check_checksum;
4162: bi->handler_to_use=(cpuop_func*)popall_check_checksum;
4163: set_dhtu(bi,bi->direct_pcc);
1.1.1.3 root 4164: bi->status=BI_NEED_CHECK;
1.1 root 4165: }
4166: bi2=bi;
4167: bi=bi->next;
4168: }
4169: /* bi2 is now the last entry in the active list */
4170: bi2->next=dormant;
4171: if (dormant)
4172: dormant->prev_p=&(bi2->next);
4173:
4174: dormant=active;
4175: active->prev_p=&dormant;
4176: active=NULL;
4177: }
4178:
1.1.1.3 root 4179: #ifdef UAE
4180: static
4181: #endif
4182: void flush_icache_range(uae_u32 start, uae_u32 length)
4183: {
4184: if (!active)
4185: return;
4186:
4187: #if LAZY_FLUSH_ICACHE_RANGE
4188: uae_u8 *start_p = get_real_address(start);
4189: blockinfo *bi = active;
4190: while (bi) {
4191: #if USE_CHECKSUM_INFO
4192: bool invalidate = false;
4193: for (checksum_info *csi = bi->csi; csi && !invalidate; csi = csi->next)
4194: invalidate = (((start_p - csi->start_p) < csi->length) ||
4195: ((csi->start_p - start_p) < length));
4196: #else
4197: // Assume system is consistent and would invalidate the right range
4198: const bool invalidate = (bi->pc_p - start_p) < length;
4199: #endif
4200: if (invalidate) {
4201: uae_u32 cl = cacheline(bi->pc_p);
4202: if (bi == cache_tags[cl + 1].bi)
4203: cache_tags[cl].handler = (cpuop_func *)popall_execute_normal;
4204: bi->handler_to_use = (cpuop_func *)popall_execute_normal;
4205: set_dhtu(bi, bi->direct_pen);
4206: bi->status = BI_NEED_RECOMP;
4207: }
4208: bi = bi->next;
4209: }
4210: return;
4211: #else
4212: UNUSED(start);
4213: UNUSED(length);
4214: #endif
4215: flush_icache(-1);
4216: }
1.1 root 4217:
1.1.1.3 root 4218: /*
1.1 root 4219: static void catastrophe(void)
4220: {
1.1.1.3 root 4221: jit_abort("catastprophe");
1.1 root 4222: }
1.1.1.3 root 4223: */
1.1 root 4224:
4225: int failure;
4226:
1.1.1.3 root 4227: #define TARGET_M68K 0
4228: #define TARGET_POWERPC 1
4229: #define TARGET_X86 2
4230: #define TARGET_X86_64 3
4231: #define TARGET_ARM 4
4232: #if defined(CPU_i386)
4233: #define TARGET_NATIVE TARGET_X86
4234: #endif
4235: #if defined(CPU_powerpc)
4236: #define TARGET_NATIVE TARGET_POWERPC
4237: #endif
4238: #if defined(CPU_x86_64)
4239: #define TARGET_NATIVE TARGET_X86_64
4240: #endif
4241: #if defined(CPU_arm)
4242: #define TARGET_NATIVE TARGET_ARM
4243: #endif
4244:
4245: #ifdef UAE
4246: static
4247: #endif
4248: void disasm_block(int /* target */, uint8 * /* start */, size_t /* length */)
4249: {
4250: if (!JITDebug)
4251: return;
4252: }
4253:
4254: static inline void disasm_native_block(uint8 *start, size_t length)
4255: {
4256: disasm_block(TARGET_NATIVE, start, length);
4257: }
4258:
4259: static inline void disasm_m68k_block(uint8 *start, size_t length)
4260: {
4261: disasm_block(TARGET_M68K, start, length);
4262: }
4263:
4264: #ifdef UAE
4265: static inline unsigned int get_opcode_cft_map(unsigned int f)
4266: {
4267: return ((f >> 8) & 255) | ((f & 255) << 8);
4268: }
4269: #define DO_GET_OPCODE(a) (get_opcode_cft_map((uae_u16)*(a)))
4270: #else
4271: #if defined(HAVE_GET_WORD_UNSWAPPED) && !defined(FULLMMU)
4272: # define DO_GET_OPCODE(a) (do_get_mem_word_unswapped((uae_u16 *)(a)))
4273: #else
4274: # define DO_GET_OPCODE(a) (do_get_mem_word((uae_u16 *)(a)))
4275: #endif
4276: #endif
4277:
4278: #ifdef JIT_DEBUG
4279: static uae_u8 *last_regs_pc_p = 0;
4280: static uae_u8 *last_compiled_block_addr = 0;
4281:
4282: void compiler_dumpstate(void)
4283: {
4284: if (!JITDebug)
4285: return;
4286:
4287: bug("### Host addresses");
4288: bug("MEM_BASE : %x", MEMBaseDiff);
4289: bug("PC_P : %p", ®s.pc_p);
4290: bug("SPCFLAGS : %p", ®s.spcflags);
4291: bug("D0-D7 : %p-%p", ®s.regs[0], ®s.regs[7]);
4292: bug("A0-A7 : %p-%p", ®s.regs[8], ®s.regs[15]);
4293: bug("");
4294:
4295: bug("### M68k processor state");
4296: m68k_dumpstate(stderr, 0);
4297: bug("");
4298:
4299: bug("### Block in Atari address space");
4300: bug("M68K block : %p",
4301: (void *)(uintptr)last_regs_pc_p);
4302: if (last_regs_pc_p != 0) {
4303: bug("Native block : %p (%d bytes)",
4304: (void *)last_compiled_block_addr,
4305: get_blockinfo_addr(last_regs_pc_p)->direct_handler_size);
4306: }
4307: bug("");
4308: }
4309: #endif
1.1 root 4310:
1.1.1.3 root 4311: #ifdef UAE
4312: void compile_block(cpu_history *pc_hist, int blocklen, int totcycles)
4313: {
4314: if (letit && compiled_code && currprefs.cpu_model >= 68020) {
4315: #else
4316: static void compile_block(cpu_history* pc_hist, int blocklen)
1.1 root 4317: {
1.1.1.3 root 4318: if (letit && compiled_code) {
4319: #endif
4320: #ifdef PROFILE_COMPILE_TIME
4321: compile_count++;
4322: clock_t start_time = clock();
4323: #endif
4324: #ifdef JIT_DEBUG
4325: bool disasm_block = false;
4326: #endif
1.1 root 4327:
4328: /* OK, here we need to 'compile' a block */
4329: int i;
4330: int r;
4331: int was_comp=0;
4332: uae_u8 liveflags[MAXRUN+1];
1.1.1.3 root 4333: #if USE_CHECKSUM_INFO
4334: bool trace_in_rom = isinrom((uintptr)pc_hist[0].location) != 0;
4335: uintptr max_pcp=(uintptr)pc_hist[blocklen - 1].location;
4336: uintptr min_pcp=max_pcp;
4337: #else
4338: uintptr max_pcp=(uintptr)pc_hist[0].location;
4339: uintptr min_pcp=max_pcp;
4340: #endif
1.1 root 4341: uae_u32 cl=cacheline(pc_hist[0].location);
4342: void* specflags=(void*)®s.spcflags;
4343: blockinfo* bi=NULL;
4344: blockinfo* bi2;
4345: int extra_len=0;
4346:
1.1.1.3 root 4347: redo_current_block=0;
4348: if (current_compile_p >= MAX_COMPILE_PTR)
1.1.1.4 ! root 4349: flush_icache_hard(3);
1.1 root 4350:
4351: alloc_blockinfos();
4352:
4353: bi=get_blockinfo_addr_new(pc_hist[0].location,0);
4354: bi2=get_blockinfo(cl);
4355:
4356: optlev=bi->optlevel;
1.1.1.3 root 4357: if (bi->status!=BI_INVALID) {
1.1 root 4358: Dif (bi!=bi2) {
4359: /* I don't think it can happen anymore. Shouldn't, in
4360: any case. So let's make sure... */
1.1.1.3 root 4361: jit_abort("WOOOWOO count=%d, ol=%d %p %p", bi->count,bi->optlevel,bi->handler_to_use, cache_tags[cl].handler);
1.1 root 4362: }
4363:
1.1.1.3 root 4364: Dif (bi->count!=-1 && bi->status!=BI_NEED_RECOMP) {
4365: jit_abort("bi->count=%d, bi->status=%d,bi->optlevel=%d",bi->count,bi->status,bi->optlevel);
1.1 root 4366: /* What the heck? We are not supposed to be here! */
4367: }
4368: }
4369: if (bi->count==-1) {
4370: optlev++;
1.1.1.3 root 4371: while (!optcount[optlev])
1.1 root 4372: optlev++;
1.1.1.3 root 4373: bi->count=optcount[optlev]-1;
1.1 root 4374: }
1.1.1.3 root 4375: current_block_pc_p=(uintptr)pc_hist[0].location;
1.1 root 4376:
4377: remove_deps(bi); /* We are about to create new code */
4378: bi->optlevel=optlev;
4379: bi->pc_p=(uae_u8*)pc_hist[0].location;
1.1.1.3 root 4380: #if USE_CHECKSUM_INFO
4381: free_checksum_info_chain(bi->csi);
4382: bi->csi = NULL;
4383: #endif
1.1 root 4384:
4385: liveflags[blocklen]=0x1f; /* All flags needed afterwards */
4386: i=blocklen;
4387: while (i--) {
4388: uae_u16* currpcp=pc_hist[i].location;
1.1.1.3 root 4389: uae_u32 op=DO_GET_OPCODE(currpcp);
1.1 root 4390:
1.1.1.3 root 4391: #if USE_CHECKSUM_INFO
4392: trace_in_rom = trace_in_rom && isinrom((uintptr)currpcp);
4393: if (follow_const_jumps && is_const_jump(op)) {
4394: checksum_info *csi = alloc_checksum_info();
4395: csi->start_p = (uae_u8 *)min_pcp;
4396: csi->length = max_pcp - min_pcp + LONGEST_68K_INST;
4397: csi->next = bi->csi;
4398: bi->csi = csi;
4399: max_pcp = (uintptr)currpcp;
4400: }
4401: min_pcp = (uintptr)currpcp;
4402: #else
4403: if ((uintptr)currpcp<min_pcp)
4404: min_pcp=(uintptr)currpcp;
4405: if ((uintptr)currpcp>max_pcp)
4406: max_pcp=(uintptr)currpcp;
4407: #endif
1.1 root 4408:
1.1.1.3 root 4409: #ifdef UAE
1.1 root 4410: if (currprefs.compnf) {
1.1.1.3 root 4411: #endif
1.1 root 4412: liveflags[i]=((liveflags[i+1]&
4413: (~prop[op].set_flags))|
4414: prop[op].use_flags);
4415: if (prop[op].is_addx && (liveflags[i+1]&FLAG_Z)==0)
4416: liveflags[i]&= ~FLAG_Z;
1.1.1.3 root 4417: #ifdef UAE
1.1 root 4418: }
4419: else {
4420: liveflags[i]=0x1f;
4421: }
1.1.1.3 root 4422: #endif
1.1 root 4423: }
4424:
1.1.1.3 root 4425: #if USE_CHECKSUM_INFO
4426: checksum_info *csi = alloc_checksum_info();
4427: csi->start_p = (uae_u8 *)min_pcp;
4428: csi->length = max_pcp - min_pcp + LONGEST_68K_INST;
4429: csi->next = bi->csi;
4430: bi->csi = csi;
4431: #endif
1.1 root 4432:
1.1.1.3 root 4433: bi->needed_flags=liveflags[0];
1.1 root 4434:
1.1.1.3 root 4435: align_target(align_loops);
1.1 root 4436: was_comp=0;
4437:
4438: bi->direct_handler=(cpuop_func*)get_target();
4439: set_dhtu(bi,bi->direct_handler);
1.1.1.3 root 4440: bi->status=BI_COMPILING;
4441: current_block_start_target=(uintptr)get_target();
4442:
4443: log_startblock();
1.1 root 4444:
4445: if (bi->count>=0) { /* Need to generate countdown code */
1.1.1.3 root 4446: compemu_raw_mov_l_mi((uintptr)®s.pc_p,(uintptr)pc_hist[0].location);
4447: compemu_raw_sub_l_mi((uintptr)&(bi->count),1);
4448: compemu_raw_jl((uintptr)popall_recompile_block);
1.1 root 4449: }
4450: if (optlev==0) { /* No need to actually translate */
4451: /* Execute normally without keeping stats */
1.1.1.3 root 4452: compemu_raw_mov_l_mi((uintptr)®s.pc_p,(uintptr)pc_hist[0].location);
4453: compemu_raw_jmp((uintptr)popall_exec_nostats);
1.1 root 4454: }
4455: else {
4456: reg_alloc_run=0;
4457: next_pc_p=0;
4458: taken_pc_p=0;
1.1.1.3 root 4459: branch_cc=0; // Only to be initialized. Will be set together with next_pc_p
1.1 root 4460:
1.1.1.3 root 4461: comp_pc_p=(uae_u8*)pc_hist[0].location;
4462: init_comp();
4463: was_comp=1;
4464:
4465: #ifdef USE_CPU_EMUL_SERVICES
4466: compemu_raw_sub_l_mi((uintptr)&emulated_ticks,blocklen);
4467: compemu_raw_jcc_b_oponly(NATIVE_CC_GT);
4468: uae_s8 *branchadd=(uae_s8*)get_target();
4469: skip_byte();
4470: raw_dec_sp(STACK_SHADOW_SPACE);
4471: compemu_raw_call((uintptr)cpu_do_check_ticks);
4472: raw_inc_sp(STACK_SHADOW_SPACE);
4473: *branchadd=(uintptr)get_target()-((uintptr)branchadd+1);
4474: #endif
4475:
4476: #ifdef JIT_DEBUG
4477: if (JITDebug) {
4478: compemu_raw_mov_l_mi((uintptr)&last_regs_pc_p,(uintptr)pc_hist[0].location);
4479: compemu_raw_mov_l_mi((uintptr)&last_compiled_block_addr,current_block_start_target);
4480: }
4481: #endif
4482:
1.1 root 4483: for (i=0;i<blocklen &&
1.1.1.3 root 4484: get_target_noopt() < MAX_COMPILE_PTR;i++) {
1.1 root 4485: cpuop_func **cputbl;
4486: compop_func **comptbl;
1.1.1.3 root 4487: uae_u32 opcode=DO_GET_OPCODE(pc_hist[i].location);
1.1 root 4488: needed_flags=(liveflags[i+1] & prop[opcode].set_flags);
1.1.1.3 root 4489: #ifdef UAE
4490: special_mem=pc_hist[i].specmem;
1.1 root 4491: if (!needed_flags && currprefs.compnf) {
1.1.1.3 root 4492: #else
4493: if (!needed_flags) {
4494: #endif
1.1 root 4495: #ifdef NOFLAGS_SUPPORT
4496: cputbl=nfcpufunctbl;
4497: #else
4498: cputbl=cpufunctbl;
4499: #endif
4500: comptbl=nfcompfunctbl;
4501: }
4502: else {
4503: cputbl=cpufunctbl;
4504: comptbl=compfunctbl;
4505: }
4506:
1.1.1.3 root 4507: #ifdef FLIGHT_RECORDER
4508: {
4509: mov_l_ri(S1, ((uintptr)(pc_hist[i].location)) | 1);
4510: /* store also opcode to second register */
4511: clobber_flags();
4512: remove_all_offsets();
4513: int arg = readreg_specific(S1,4,REG_PAR1);
4514: prepare_for_call_1();
4515: unlock2(arg);
4516: prepare_for_call_2();
4517: raw_dec_sp(STACK_SHADOW_SPACE);
4518: compemu_raw_call((uintptr)m68k_record_step);
4519: raw_inc_sp(STACK_SHADOW_SPACE);
4520: }
4521: #endif
4522:
4523: failure = 1; // gb-- defaults to failure state
1.1 root 4524: if (comptbl[opcode] && optlev>1) {
4525: failure=0;
4526: if (!was_comp) {
4527: comp_pc_p=(uae_u8*)pc_hist[i].location;
4528: init_comp();
4529: }
1.1.1.3 root 4530: was_comp=1;
1.1 root 4531:
4532: comptbl[opcode](opcode);
4533: freescratch();
4534: if (!(liveflags[i+1] & FLAG_CZNV)) {
4535: /* We can forget about flags */
4536: dont_care_flags();
4537: }
4538: #if INDIVIDUAL_INST
4539: flush(1);
4540: nop();
4541: flush(1);
4542: was_comp=0;
4543: #endif
4544: }
1.1.1.3 root 4545:
1.1 root 4546: if (failure) {
4547: if (was_comp) {
4548: flush(1);
4549: was_comp=0;
4550: }
1.1.1.3 root 4551: compemu_raw_mov_l_ri(REG_PAR1,(uae_u32)opcode);
1.1 root 4552: #if USE_NORMAL_CALLING_CONVENTION
4553: raw_push_l_r(REG_PAR1);
4554: #endif
1.1.1.3 root 4555: compemu_raw_mov_l_mi((uintptr)®s.pc_p,
4556: (uintptr)pc_hist[i].location);
4557: raw_dec_sp(STACK_SHADOW_SPACE);
4558: compemu_raw_call((uintptr)cputbl[opcode]);
4559: raw_inc_sp(STACK_SHADOW_SPACE);
4560: #ifdef PROFILE_UNTRANSLATED_INSNS
4561: // raw_cputbl_count[] is indexed with plain opcode (in m68k order)
4562: compemu_raw_add_l_mi((uintptr)&raw_cputbl_count[cft_map(opcode)],1);
4563: #endif
1.1 root 4564: #if USE_NORMAL_CALLING_CONVENTION
1.1.1.3 root 4565: raw_inc_sp(4);
1.1 root 4566: #endif
4567:
4568: if (i<blocklen-1) {
4569: uae_s8* branchadd;
4570:
1.1.1.3 root 4571: compemu_raw_mov_l_rm(0,(uintptr)specflags);
4572: compemu_raw_test_l_rr(0,0);
4573: #if defined(USE_DATA_BUFFER)
4574: data_check_end(8, 64); // just a pessimistic guess...
4575: #endif
4576: compemu_raw_jz_b_oponly();
1.1 root 4577: branchadd=(uae_s8*)get_target();
1.1.1.3 root 4578: skip_byte();
4579: #ifdef UAE
4580: raw_sub_l_mi(uae_p32(&countdown),scaled_cycles(totcycles));
4581: #endif
4582: compemu_raw_jmp((uintptr)popall_do_nothing);
4583: *branchadd=(uintptr)get_target()-(uintptr)branchadd-1;
1.1 root 4584: }
4585: }
4586: }
1.1.1.3 root 4587: #if 1 /* This isn't completely kosher yet; It really needs to be
1.1 root 4588: be integrated into a general inter-block-dependency scheme */
4589: if (next_pc_p && taken_pc_p &&
4590: was_comp && taken_pc_p==current_block_pc_p) {
4591: blockinfo* bi1=get_blockinfo_addr_new((void*)next_pc_p,0);
4592: blockinfo* bi2=get_blockinfo_addr_new((void*)taken_pc_p,0);
4593: uae_u8 x=bi1->needed_flags;
4594:
4595: if (x==0xff || 1) { /* To be on the safe side */
4596: uae_u16* next=(uae_u16*)next_pc_p;
1.1.1.3 root 4597: uae_u32 op=DO_GET_OPCODE(next);
1.1 root 4598:
4599: x=0x1f;
4600: x&=(~prop[op].set_flags);
4601: x|=prop[op].use_flags;
4602: }
4603:
4604: x|=bi2->needed_flags;
4605: if (!(x & FLAG_CZNV)) {
4606: /* We can forget about flags */
4607: dont_care_flags();
4608: extra_len+=2; /* The next instruction now is part of this
4609: block */
4610: }
4611:
4612: }
4613: #endif
1.1.1.3 root 4614: log_flush();
1.1 root 4615:
4616: if (next_pc_p) { /* A branch was registered */
1.1.1.3 root 4617: uintptr t1=next_pc_p;
4618: uintptr t2=taken_pc_p;
1.1 root 4619: int cc=branch_cc;
4620:
4621: uae_u32* branchadd;
4622: uae_u32* tba;
4623: bigstate tmp;
4624: blockinfo* tbi;
4625:
4626: if (taken_pc_p<next_pc_p) {
4627: /* backward branch. Optimize for the "taken" case ---
4628: which means the raw_jcc should fall through when
4629: the 68k branch is taken. */
4630: t1=taken_pc_p;
4631: t2=next_pc_p;
4632: cc=branch_cc^1;
4633: }
4634:
4635: tmp=live; /* ouch! This is big... */
1.1.1.3 root 4636: #if defined(USE_DATA_BUFFER)
4637: data_check_end(32, 128); // just a pessimistic guess...
4638: #endif
4639: compemu_raw_jcc_l_oponly(cc);
1.1 root 4640: branchadd=(uae_u32*)get_target();
1.1.1.3 root 4641: skip_long();
4642:
1.1 root 4643: /* predicted outcome */
4644: tbi=get_blockinfo_addr_new((void*)t1,1);
1.1.1.3 root 4645: match_states(tbi);
4646: #ifdef UAE
4647: raw_sub_l_mi(uae_p32(&countdown),scaled_cycles(totcycles));
1.1 root 4648: raw_jcc_l_oponly(9);
1.1.1.3 root 4649: #else
4650: compemu_raw_cmp_l_mi8((uintptr)specflags,0);
4651: compemu_raw_jcc_l_oponly(NATIVE_CC_EQ);
4652: #endif
1.1 root 4653: tba=(uae_u32*)get_target();
1.1.1.3 root 4654: emit_jmp_target(get_handler(t1));
4655: compemu_raw_mov_l_mi((uintptr)®s.pc_p,t1);
4656: flush_reg_count();
4657: compemu_raw_jmp((uintptr)popall_do_nothing);
1.1 root 4658: create_jmpdep(bi,0,tba,t1);
4659:
1.1.1.3 root 4660: align_target(align_jumps);
1.1 root 4661: /* not-predicted outcome */
1.1.1.3 root 4662: write_jmp_target(branchadd, (cpuop_func *)get_target());
1.1 root 4663: live=tmp; /* Ouch again */
4664: tbi=get_blockinfo_addr_new((void*)t2,1);
1.1.1.3 root 4665: match_states(tbi);
1.1 root 4666:
4667: //flush(1); /* Can only get here if was_comp==1 */
1.1.1.3 root 4668: #ifdef UAE
4669: raw_sub_l_mi(uae_p32(&countdown),scaled_cycles(totcycles));
1.1 root 4670: raw_jcc_l_oponly(9);
1.1.1.3 root 4671: #else
4672: compemu_raw_cmp_l_mi8((uintptr)specflags,0);
4673: compemu_raw_jcc_l_oponly(NATIVE_CC_EQ);
4674: #endif
1.1 root 4675: tba=(uae_u32*)get_target();
1.1.1.3 root 4676: emit_jmp_target(get_handler(t2));
4677: compemu_raw_mov_l_mi((uintptr)®s.pc_p,t2);
4678: flush_reg_count();
4679: compemu_raw_jmp((uintptr)popall_do_nothing);
1.1 root 4680: create_jmpdep(bi,1,tba,t2);
4681: }
4682: else
4683: {
4684: if (was_comp) {
4685: flush(1);
4686: }
1.1.1.3 root 4687: flush_reg_count();
1.1 root 4688:
4689: /* Let's find out where next_handler is... */
4690: if (was_comp && isinreg(PC_P)) {
4691: r=live.state[PC_P].realreg;
1.1.1.3 root 4692: compemu_raw_and_l_ri(r,TAGMASK);
4693: int r2 = (r==0) ? 1 : 0;
4694: compemu_raw_mov_l_ri(r2,(uintptr)popall_do_nothing);
4695: #ifdef UAE
4696: raw_sub_l_mi(uae_p32(&countdown),scaled_cycles(totcycles));
4697: raw_cmov_l_rm_indexed(r2,(uintptr)cache_tags,r,SIZEOF_VOID_P,9);
4698: #else
4699: compemu_raw_cmp_l_mi8((uintptr)specflags,0);
4700: compemu_raw_cmov_l_rm_indexed(r2,(uintptr)cache_tags,r,SIZEOF_VOID_P,NATIVE_CC_EQ);
4701: #endif
4702: compemu_raw_jmp_r(r2);
1.1 root 4703: }
4704: else if (was_comp && isconst(PC_P)) {
1.1.1.3 root 4705: uintptr v = live.state[PC_P].val;
1.1 root 4706: uae_u32* tba;
4707: blockinfo* tbi;
4708:
1.1.1.3 root 4709: tbi = get_blockinfo_addr_new((void*) v, 1);
4710: match_states(tbi);
1.1 root 4711:
1.1.1.3 root 4712: #ifdef UAE
4713: raw_sub_l_mi(uae_p32(&countdown),scaled_cycles(totcycles));
1.1 root 4714: raw_jcc_l_oponly(9);
1.1.1.3 root 4715: #else
4716: compemu_raw_cmp_l_mi8((uintptr)specflags,0);
4717: compemu_raw_jcc_l_oponly(NATIVE_CC_EQ);
4718: #endif
1.1 root 4719: tba=(uae_u32*)get_target();
1.1.1.3 root 4720: emit_jmp_target(get_handler(v));
4721: compemu_raw_mov_l_mi((uintptr)®s.pc_p,v);
4722: compemu_raw_jmp((uintptr)popall_do_nothing);
1.1 root 4723: create_jmpdep(bi,0,tba,v);
4724: }
4725: else {
4726: r=REG_PC_TMP;
1.1.1.3 root 4727: compemu_raw_mov_l_rm(r,(uintptr)®s.pc_p);
4728: compemu_raw_and_l_ri(r,TAGMASK);
4729: int r2 = (r==0) ? 1 : 0;
4730: compemu_raw_mov_l_ri(r2,(uintptr)popall_do_nothing);
4731: #ifdef UAE
4732: raw_sub_l_mi(uae_p32(&countdown),scaled_cycles(totcycles));
4733: raw_cmov_l_rm_indexed(r2,(uintptr)cache_tags,r,SIZEOF_VOID_P,9);
4734: #else
4735: compemu_raw_cmp_l_mi8((uintptr)specflags,0);
4736: compemu_raw_cmov_l_rm_indexed(r2,(uintptr)cache_tags,r,SIZEOF_VOID_P,NATIVE_CC_EQ);
4737: #endif
4738: compemu_raw_jmp_r(r2);
1.1 root 4739: }
4740: }
4741: }
4742:
1.1.1.3 root 4743: #if USE_MATCH
4744: if (callers_need_recompile(&live,&(bi->env))) {
4745: mark_callers_recompile(bi);
4746: }
4747:
4748: big_to_small_state(&live,&(bi->env));
4749: #endif
4750:
4751: #if USE_CHECKSUM_INFO
4752: remove_from_list(bi);
4753: if (trace_in_rom) {
4754: // No need to checksum that block trace on cache invalidation
4755: free_checksum_info_chain(bi->csi);
4756: bi->csi = NULL;
4757: add_to_dormant(bi);
4758: }
4759: else {
4760: calc_checksum(bi,&(bi->c1),&(bi->c2));
4761: add_to_active(bi);
4762: }
4763: #else
1.1 root 4764: if (next_pc_p+extra_len>=max_pcp &&
4765: next_pc_p+extra_len<max_pcp+LONGEST_68K_INST)
4766: max_pcp=next_pc_p+extra_len; /* extra_len covers flags magic */
4767: else
4768: max_pcp+=LONGEST_68K_INST;
1.1.1.3 root 4769:
1.1 root 4770: bi->len=max_pcp-min_pcp;
4771: bi->min_pcp=min_pcp;
4772:
4773: remove_from_list(bi);
1.1.1.3 root 4774: if (isinrom(min_pcp) && isinrom(max_pcp)) {
1.1 root 4775: add_to_dormant(bi); /* No need to checksum it on cache flush.
4776: Please don't start changing ROMs in
4777: flight! */
1.1.1.3 root 4778: }
1.1 root 4779: else {
4780: calc_checksum(bi,&(bi->c1),&(bi->c2));
4781: add_to_active(bi);
4782: }
1.1.1.3 root 4783: #endif
4784:
4785: current_cache_size += get_target() - (uae_u8 *)current_compile_p;
4786:
4787: #ifdef JIT_DEBUG
4788: if (JITDebug)
4789: bi->direct_handler_size = get_target() - (uae_u8 *)current_block_start_target;
4790:
4791: if (JITDebug && disasm_block) {
4792: uaecptr block_addr = start_pc + ((char *)pc_hist[0].location - (char *)start_pc_p);
4793: D(bug("M68K block @ 0x%08x (%d insns)\n", block_addr, blocklen));
4794: uae_u32 block_size = ((uae_u8 *)pc_hist[blocklen - 1].location - (uae_u8 *)pc_hist[0].location) + 1;
4795: disasm_m68k_block((uae_u8 *)pc_hist[0].location, block_size);
4796: D(bug("Compiled block @ 0x%08x\n", pc_hist[0].location));
4797: disasm_native_block((uae_u8 *)current_block_start_target, bi->direct_handler_size);
4798: getchar();
4799: }
4800: #endif
1.1 root 4801:
4802: log_dump();
1.1.1.3 root 4803: align_target(align_jumps);
4804:
4805: #ifdef UAE
4806: #ifdef USE_UDIS86
4807: UDISFN(current_block_start_target, target)
4808: #endif
4809: #endif
4810:
4811: /* This is the non-direct handler */
4812: bi->handler=
4813: bi->handler_to_use=(cpuop_func *)get_target();
4814: compemu_raw_cmp_l_mi((uintptr)®s.pc_p,(uintptr)pc_hist[0].location);
4815: compemu_raw_jnz((uintptr)popall_cache_miss);
4816: comp_pc_p=(uae_u8*)pc_hist[0].location;
4817:
4818: bi->status=BI_FINALIZING;
4819: init_comp();
4820: match_states(bi);
4821: flush(1);
1.1 root 4822:
1.1.1.3 root 4823: compemu_raw_jmp((uintptr)bi->direct_handler);
4824:
4825: flush_cpu_icache((void *)current_block_start_target, (void *)target);
4826: current_compile_p=get_target();
1.1 root 4827: raise_in_cl_list(bi);
4828: bi->nexthandler=current_compile_p;
4829:
4830: /* We will flush soon, anyway, so let's do it now */
1.1.1.3 root 4831: if (current_compile_p >= MAX_COMPILE_PTR)
1.1.1.4 ! root 4832: flush_icache_hard(3);
1.1 root 4833:
1.1.1.3 root 4834: bi->status=BI_ACTIVE;
4835: if (redo_current_block)
4836: block_need_recompile(bi);
4837:
4838: #ifdef PROFILE_COMPILE_TIME
4839: compile_time += (clock() - start_time);
4840: #endif
4841: #ifdef UAE
4842: /* Account for compilation time */
4843: do_extra_cycles(totcycles);
4844: }
4845: #else
4846: }
4847:
4848: /* Account for compilation time */
4849: cpu_do_check_ticks();
4850: #endif
4851: }
4852:
4853: #ifdef UAE
4854: /* Slightly different function defined in newcpu.cpp */
4855: #else
4856: void do_nothing(void)
4857: {
4858: /* What did you expect this to do? */
4859: }
4860: #endif
4861:
4862: #ifdef UAE
4863: /* Different implementation in newcpu.cpp */
4864: #else
4865: void exec_nostats(void)
4866: {
4867: for (;;) {
4868: uae_u32 opcode = GET_OPCODE;
4869: #ifdef FLIGHT_RECORDER
4870: m68k_record_step(m68k_getpc(), opcode);
4871: #endif
4872: (*cpufunctbl[opcode])(opcode);
4873: cpu_check_ticks();
4874: if (end_block(opcode) || SPCFLAGS_TEST(SPCFLAG_ALL)) {
4875: return; /* We will deal with the spcflags in the caller */
4876: }
4877: }
4878: }
4879: #endif
4880:
4881: #ifdef UAE
4882: /* FIXME: check differences against UAE execute_normal (newcpu.cpp) */
4883: #else
4884: void execute_normal(void)
4885: {
4886: if (!check_for_cache_miss()) {
4887: cpu_history pc_hist[MAXRUN];
4888: int blocklen = 0;
4889: #if 0 && FIXED_ADDRESSING
4890: start_pc_p = regs.pc_p;
4891: start_pc = get_virtual_address(regs.pc_p);
4892: #else
4893: start_pc_p = regs.pc_oldp;
4894: start_pc = regs.pc;
4895: #endif
4896: for (;;) { /* Take note: This is the do-it-normal loop */
4897: pc_hist[blocklen++].location = (uae_u16 *)regs.pc_p;
4898: uae_u32 opcode = GET_OPCODE;
4899: #ifdef FLIGHT_RECORDER
4900: m68k_record_step(m68k_getpc(), opcode);
4901: #endif
4902: (*cpufunctbl[opcode])(opcode);
4903: cpu_check_ticks();
4904: if (end_block(opcode) || SPCFLAGS_TEST(SPCFLAG_ALL) || blocklen>=MAXRUN) {
4905: compile_block(pc_hist, blocklen);
4906: return; /* We will deal with the spcflags in the caller */
4907: }
4908: /* No need to check regs.spcflags, because if they were set,
4909: we'd have ended up inside that "if" */
4910: }
4911: }
4912: }
4913: #endif
4914:
4915: typedef void (*compiled_handler)(void);
4916:
4917: #ifdef UAE
4918: /* FIXME: check differences against UAE m68k_do_compile_execute */
4919: #else
4920: void m68k_do_compile_execute(void)
4921: {
4922: for (;;) {
4923: ((compiled_handler)(pushall_call_handler))();
4924: /* Whenever we return from that, we should check spcflags */
4925: if (SPCFLAGS_TEST(SPCFLAG_ALL)) {
4926: if (m68k_do_specialties ())
4927: return;
4928: }
1.1 root 4929: }
4930: }
1.1.1.3 root 4931: #endif
1.1 root 4932:
1.1.1.3 root 4933: #ifdef UAE
4934: /* FIXME: check differences against UAE m68k_compile_execute */
4935: #else
4936: void m68k_compile_execute (void)
4937: {
4938: setjmpagain:
4939: TRY(prb) {
4940: for (;;) {
4941: if (quit_program > 0) {
4942: if (quit_program == 1) {
4943: #ifdef FLIGHT_RECORDER
4944: dump_flight_recorder();
4945: #endif
4946: break;
4947: }
4948: quit_program = 0;
4949: m68k_reset ();
4950: }
4951: m68k_do_compile_execute();
4952: }
4953: }
4954: CATCH(prb) {
4955: flush_icache(0);
4956: Exception(prb, 0);
4957: goto setjmpagain;
4958: }
4959: }
1.1 root 4960: #endif
1.1.1.3 root 4961:
4962: #endif /* JIT */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.