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