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 Fri Oct 31 03:24:49 2008 +0000 (15 years ago)
permissions -rw-r--r--
last change Remove FASTCALL from mem_copy_*, not really helping atm (and sometimes hurting)
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 */
.