Search
lxdream.org :: lxdream/src/sh4/ia32abi.h
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/ia32abi.h
changeset 907:5ecafd8d7923
prev906:268ea359f884
next926:68f3e0fe02f1
author nkeynes
date Thu Dec 11 23:26:03 2008 +0000 (15 years ago)
permissions -rw-r--r--
last change Disable the generational translation cache - I've got no evidence that it
actually helps performance, and it simplifies things to get rid of it (in
particular, translated code doesn't have to worry about being moved now).
view annotate diff log raw
     1 /**
     2  * $Id$
     3  * 
     4  * Provides the implementation for the ia32 ABI (eg prologue, epilogue, and
     5  * calling conventions)
     6  *
     7  * Copyright (c) 2007 Nathan Keynes.
     8  *
     9  * This program is free software; you can redistribute it and/or modify
    10  * it under the terms of the GNU General Public License as published by
    11  * the Free Software Foundation; either version 2 of the License, or
    12  * (at your option) any later version.
    13  *
    14  * This program is distributed in the hope that it will be useful,
    15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    17  * GNU General Public License for more details.
    18  */
    20 #ifndef lxdream_ia32abi_H
    21 #define lxdream_ia32abi_H 1
    23 #define load_ptr( reg, ptr ) load_imm32( reg, (uint32_t)ptr );
    25 /**
    26  * Note: clobbers EAX to make the indirect call - this isn't usually
    27  * a problem since the callee will usually clobber it anyway.
    28  */
    29 static inline void call_func0( void *ptr )
    30 {
    31     load_imm32(R_ECX, (uint32_t)ptr);
    32     CALL_r32(R_ECX);
    33 }
    35 #ifdef HAVE_FASTCALL
    36 static inline void call_func1( void *ptr, int arg1 )
    37 {
    38     if( arg1 != R_EAX ) {
    39         MOV_r32_r32( arg1, R_EAX );
    40     }
    41     load_imm32(R_ECX, (uint32_t)ptr);
    42     CALL_r32(R_ECX);
    43 }
    45 static inline void call_func2( void *ptr, int arg1, int arg2 )
    46 {
    47     if( arg2 != R_EDX ) {
    48         MOV_r32_r32( arg2, R_EDX );
    49     }
    50     if( arg1 != R_EAX ) {
    51         MOV_r32_r32( arg1, R_EAX );
    52     }
    53     load_imm32(R_ECX, (uint32_t)ptr);
    54     CALL_r32(R_ECX);
    55 }
    57 /**
    58  * Write a double (64-bit) value into memory, with the first word in arg2a, and
    59  * the second in arg2b
    60  */
    61 static inline void MEM_WRITE_DOUBLE( int addr, int arg2a, int arg2b )
    62 {
    63     PUSH_r32(arg2b);
    64     PUSH_r32(addr);
    65     call_func2(sh4_write_long, addr, arg2a);
    66     POP_r32(R_EAX);
    67     POP_r32(R_EDX);
    68     ADD_imm8s_r32(4, R_EAX);
    69     call_func0(sh4_write_long);
    70 }
    72 /**
    73  * Read a double (64-bit) value from memory, writing the first word into arg2a
    74  * and the second into arg2b. The addr must not be in EAX
    75  */
    76 static inline void MEM_READ_DOUBLE( int addr, int arg2a, int arg2b )
    77 {
    78     PUSH_r32(addr);
    79     call_func1(sh4_read_long, addr);
    80     POP_r32(R_ECX);
    81     PUSH_r32(R_EAX);
    82     MOV_r32_r32(R_ECX, R_EAX);
    83     ADD_imm8s_r32(4, R_EAX);
    84     call_func0(sh4_read_long);
    85     if( arg2b != R_EAX ) {
    86         MOV_r32_r32(R_EAX, arg2b);
    87     }
    88     POP_r32(arg2a);
    89 }
    90 #else
    91 static inline void call_func1( void *ptr, int arg1 )
    92 {
    93     PUSH_r32(arg1);
    94     call_func0(ptr);
    95     ADD_imm8s_r32( 4, R_ESP );
    96 }
    98 static inline void call_func2( void *ptr, int arg1, int arg2 )
    99 {
   100     PUSH_r32(arg2);
   101     PUSH_r32(arg1);
   102     call_func0(ptr);
   103     ADD_imm8s_r32( 8, R_ESP );
   104 }
   106 /**
   107  * Write a double (64-bit) value into memory, with the first word in arg2a, and
   108  * the second in arg2b
   109  */
   110 static inline void MEM_WRITE_DOUBLE( int addr, int arg2a, int arg2b )
   111 {
   112     ADD_imm8s_r32( 4, addr );
   113     PUSH_r32(arg2b);
   114     PUSH_r32(addr);
   115     ADD_imm8s_r32( -4, addr );
   116     PUSH_r32(arg2a);
   117     PUSH_r32(addr);
   118     call_func0(sh4_write_long);
   119     ADD_imm8s_r32( 8, R_ESP );
   120     call_func0(sh4_write_long);
   121     ADD_imm8s_r32( 8, R_ESP );
   122 }
   124 /**
   125  * Read a double (64-bit) value from memory, writing the first word into arg2a
   126  * and the second into arg2b. The addr must not be in EAX
   127  */
   128 static inline void MEM_READ_DOUBLE( int addr, int arg2a, int arg2b )
   129 {
   130     PUSH_r32(addr);
   131     call_func0(sh4_read_long);
   132     POP_r32(R_ECX);
   133     PUSH_r32(R_EAX);
   134     ADD_imm8s_r32( 4, R_ECX );
   135     PUSH_r32(R_ECX);
   136     call_func0(sh4_read_long);
   137     ADD_imm8s_r32( 4, R_ESP );
   138     MOV_r32_r32( R_EAX, arg2b );
   139     POP_r32(arg2a);
   140 }
   141 #endif
   143 /**
   144  * Emit the 'start of block' assembly. Sets up the stack frame and save
   145  * SI/DI as required
   146  */
   147 void enter_block( ) 
   148 {
   149     PUSH_r32(R_EBP);
   150     /* mov &sh4r, ebp */
   151     load_ptr( R_EBP, ((uint8_t *)&sh4r) + 128 );
   153 #ifdef STACK_ALIGN
   154     sh4_x86.stack_posn = 8;
   155 #endif
   156 }
   158 /**
   159  * Exit the block with sh4r.pc already written
   160  */
   161 void exit_block_pcset( sh4addr_t pc )
   162 {
   163     load_imm32( R_ECX, ((pc - sh4_x86.block_start_pc)>>1)*sh4_cpu_period ); // 5
   164     ADD_r32_sh4r( R_ECX, REG_OFFSET(slice_cycle) );    // 6
   165     load_spreg( R_EAX, R_PC );
   166     if( sh4_x86.tlb_on ) {
   167         call_func1(xlat_get_code_by_vma,R_EAX);
   168     } else {
   169         call_func1(xlat_get_code,R_EAX);
   170     } 
   171     POP_r32(R_EBP);
   172     RET();
   173 }
   175 /**
   176  * Exit the block with sh4r.new_pc written with the target pc
   177  */
   178 void exit_block_newpcset( sh4addr_t pc )
   179 {
   180     load_imm32( R_ECX, ((pc - sh4_x86.block_start_pc)>>1)*sh4_cpu_period ); // 5
   181     ADD_r32_sh4r( R_ECX, REG_OFFSET(slice_cycle) );    // 6
   182     load_spreg( R_EAX, R_NEW_PC );
   183     store_spreg( R_EAX, R_PC );
   184     if( sh4_x86.tlb_on ) {
   185         call_func1(xlat_get_code_by_vma,R_EAX);
   186     } else {
   187         call_func1(xlat_get_code,R_EAX);
   188     } 
   189     POP_r32(R_EBP);
   190     RET();
   191 }
   193 #define EXIT_BLOCK_SIZE(pc)  (24 + (IS_IN_ICACHE(pc)?5:CALL_FUNC1_SIZE))
   196 /**
   197  * Exit the block to an absolute PC
   198  */
   199 void exit_block( sh4addr_t pc, sh4addr_t endpc )
   200 {
   201     load_imm32( R_ECX, pc );                            // 5
   202     store_spreg( R_ECX, REG_OFFSET(pc) );               // 3
   203     if( IS_IN_ICACHE(pc) ) {
   204         MOV_moff32_EAX( xlat_get_lut_entry(GET_ICACHE_PHYS(pc)) ); // 5
   205     } else if( sh4_x86.tlb_on ) {
   206         call_func1(xlat_get_code_by_vma,R_ECX);
   207     } else {
   208         call_func1(xlat_get_code,R_ECX);
   209     }
   210     AND_imm8s_r32( 0xFC, R_EAX ); // 3
   211     load_imm32( R_ECX, ((endpc - sh4_x86.block_start_pc)>>1)*sh4_cpu_period ); // 5
   212     ADD_r32_sh4r( R_ECX, REG_OFFSET(slice_cycle) );     // 6
   213     POP_r32(R_EBP);
   214     RET();
   215 }
   217 #define EXIT_BLOCK_REL_SIZE(pc)  (27 + (IS_IN_ICACHE(pc)?5:CALL_FUNC1_SIZE))
   219 /**
   220  * Exit the block to a relative PC
   221  */
   222 void exit_block_rel( sh4addr_t pc, sh4addr_t endpc )
   223 {
   224     load_imm32( R_ECX, pc - sh4_x86.block_start_pc );   // 5
   225     ADD_sh4r_r32( R_PC, R_ECX );
   226     store_spreg( R_ECX, REG_OFFSET(pc) );               // 3
   227     if( IS_IN_ICACHE(pc) ) {
   228         MOV_moff32_EAX( xlat_get_lut_entry(GET_ICACHE_PHYS(pc)) ); // 5
   229     } else if( sh4_x86.tlb_on ) {
   230         call_func1(xlat_get_code_by_vma,R_ECX);
   231     } else {
   232         call_func1(xlat_get_code,R_ECX);
   233     }
   234     AND_imm8s_r32( 0xFC, R_EAX ); // 3
   235     load_imm32( R_ECX, ((endpc - sh4_x86.block_start_pc)>>1)*sh4_cpu_period ); // 5
   236     ADD_r32_sh4r( R_ECX, REG_OFFSET(slice_cycle) );     // 6
   237     POP_r32(R_EBP);
   238     RET();
   239 }
   241 /**
   242  * Write the block trailer (exception handling block)
   243  */
   244 void sh4_translate_end_block( sh4addr_t pc ) {
   245     if( sh4_x86.branch_taken == FALSE ) {
   246         // Didn't exit unconditionally already, so write the termination here
   247         exit_block_rel( pc, pc );
   248     }
   249     if( sh4_x86.backpatch_posn != 0 ) {
   250         unsigned int i;
   251         // Raise exception
   252         uint8_t *end_ptr = xlat_output;
   253         MOV_r32_r32( R_EDX, R_ECX );
   254         ADD_r32_r32( R_EDX, R_ECX );
   255         ADD_r32_sh4r( R_ECX, R_PC );
   256         MOV_moff32_EAX( &sh4_cpu_period );
   257         MUL_r32( R_EDX );
   258         ADD_r32_sh4r( R_EAX, REG_OFFSET(slice_cycle) );
   260         POP_r32( R_EAX );
   261         call_func1( sh4_raise_exception, R_EAX );
   262         load_spreg( R_EAX, R_PC );
   263         if( sh4_x86.tlb_on ) {
   264             call_func1(xlat_get_code_by_vma,R_EAX);
   265         } else {
   266             call_func1(xlat_get_code,R_EAX);
   267         }
   268         POP_r32(R_EBP);
   269         RET();
   271         // Exception already raised - just cleanup
   272         uint8_t *preexc_ptr = xlat_output;
   273         MOV_r32_r32( R_EDX, R_ECX );
   274         ADD_r32_r32( R_EDX, R_ECX );
   275         ADD_r32_sh4r( R_ECX, R_SPC );
   276         MOV_moff32_EAX( &sh4_cpu_period );
   277         MUL_r32( R_EDX );
   278         ADD_r32_sh4r( R_EAX, REG_OFFSET(slice_cycle) );
   279         load_spreg( R_EAX, R_PC );
   280         if( sh4_x86.tlb_on ) {
   281             call_func1(xlat_get_code_by_vma,R_EAX);
   282         } else {
   283             call_func1(xlat_get_code,R_EAX);
   284         }
   285         POP_r32(R_EBP);
   286         RET();
   288         for( i=0; i< sh4_x86.backpatch_posn; i++ ) {
   289             uint32_t *fixup_addr = (uint32_t *)&xlat_current_block->code[sh4_x86.backpatch_list[i].fixup_offset];
   290             *fixup_addr = xlat_output - (uint8_t *)&xlat_current_block->code[sh4_x86.backpatch_list[i].fixup_offset] - 4;
   291             if( sh4_x86.backpatch_list[i].exc_code < 0 ) {
   292                 load_imm32( R_EDX, sh4_x86.backpatch_list[i].fixup_icount );
   293                 int stack_adj = -1 - sh4_x86.backpatch_list[i].exc_code;
   294                 if( stack_adj > 0 ) { 
   295                     ADD_imm8s_r32( stack_adj, R_ESP );
   296                 }
   297                 int rel = preexc_ptr - xlat_output;
   298                 JMP_rel(rel);
   299             } else {
   300                 PUSH_imm32( sh4_x86.backpatch_list[i].exc_code );
   301                 load_imm32( R_EDX, sh4_x86.backpatch_list[i].fixup_icount );
   302                 int rel = end_ptr - xlat_output;
   303                 JMP_rel(rel);
   304             }
   305         }
   306     }
   307 }
   309 /**
   310  * The unwind methods only work if we compiled with DWARF2 frame information
   311  * (ie -fexceptions), otherwise we have to use the direct frame scan.
   312  */
   313 #ifdef HAVE_EXCEPTIONS
   314 #include <unwind.h>
   316 struct UnwindInfo {
   317 	int have_result;
   318 	void *pc;
   319 };
   321 _Unwind_Reason_Code xlat_check_frame( struct _Unwind_Context *context, void *arg )
   322 {
   323     void *ebp = (void *)_Unwind_GetGR(context, 5);
   324     void *expect = (((uint8_t *)&sh4r) + 128 );
   325 	struct UnwindInfo *info = arg;
   326     if( ebp == expect ) { 
   327         info->have_result = 1;
   328         info->pc = (void *)_Unwind_GetIP(context);
   329     } else if( info->have_result ) {
   330         return _URC_NORMAL_STOP;
   331     }
   333     return _URC_NO_REASON;
   334 }
   336 void *xlat_get_native_pc( void *code, uint32_t code_size )
   337 {
   338     struct _Unwind_Exception exc;
   339     struct UnwindInfo info;
   341     info.have_result = 0;
   342     void *result = NULL;
   343     _Unwind_Backtrace( xlat_check_frame, &info );
   344     if( info.have_result )
   345     	return info.pc;
   346     return NULL;
   347 }
   348 #else 
   349 void *xlat_get_native_pc( void *code, uint32_t code_size )
   350 {
   351     void *result = NULL;
   352     asm(
   353         "mov %%ebp, %%eax\n\t"
   354         "mov $0x8, %%ecx\n\t"
   355         "mov %1, %%edx\n"
   356         "frame_loop: test %%eax, %%eax\n\t"
   357         "je frame_not_found\n\t"
   358         "cmp (%%eax), %%edx\n\t"
   359         "je frame_found\n\t"
   360         "sub $0x1, %%ecx\n\t"
   361         "je frame_not_found\n\t"
   362         "movl (%%eax), %%eax\n\t"
   363         "jmp frame_loop\n"
   364         "frame_found: movl 0x4(%%eax), %0\n"
   365         "frame_not_found:"
   366         : "=r" (result)
   367         : "r" (((uint8_t *)&sh4r) + 128 )
   368         : "eax", "ecx", "edx" );
   369     return result;
   370 }
   371 #endif
   373 #endif /* !lxdream_ia32abi_H */
.