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