Search
lxdream.org :: lxdream/src/sh4/ia64abi.h
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/ia64abi.h
changeset 930:07e5b11419db
prev927:17b6b9e245d8
next939:6f2302afeb89
author nkeynes
date Sat Dec 27 02:59:35 2008 +0000 (15 years ago)
branchlxdream-mem
permissions -rw-r--r--
last change Replace fpscr_mask/fpscr flags in xlat_cache_block with a single xlat_sh4_mode,
which tracks the field of the same name in sh4r - actually a little faster this way.
Now depends on SR.MD, FPSCR.PR and FPSCR.SZ (although it doesn't benefit from the SR
flag yet).

Also fixed the failure to check the flags in the common case (code address returned
by previous block) which took away the performance benefits, but oh well.
view annotate diff log raw
     1 /**
     2  * $Id$
     3  * 
     4  * Provides the implementation for the AMD64 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_ia64abi_H
    21 #define lxdream_ia64abi_H 1
    23 #include <unwind.h>
    25 #define load_ptr( reg, ptr ) load_imm64( reg, (uint64_t)ptr );
    27 static inline decode_address( int addr_reg )
    28 {
    29     MOV_r32_r32( addr_reg, R_ECX ); 
    30     SHR_imm8_r32( 12, R_ECX ); 
    31     load_ptr( R_EDI, sh4_address_space );
    32     REXW(); OP(0x8B); OP(0x0C); OP(0xCF);   // mov.q [%rdi + %rcx*8], %rcx
    33 }
    35 /**
    36  * Note: clobbers EAX to make the indirect call - this isn't usually
    37  * a problem since the callee will usually clobber it anyway.
    38  * Size: 12 bytes
    39  */
    40 #define CALL_FUNC0_SIZE 12
    41 static inline void call_func0( void *ptr )
    42 {
    43     load_imm64(R_EAX, (uint64_t)ptr);
    44     CALL_r32(R_EAX);
    45 }
    47 #define CALL_FUNC1_SIZE 14
    48 static inline void call_func1( void *ptr, int arg1 )
    49 {
    50     REXW(); MOV_r32_r32(arg1, R_EDI);
    51     call_func0(ptr);
    52 }
    54 static inline void call_func1_exc( void *ptr, int arg1, int pc )
    55 {
    56     REXW(); MOV_r32_r32(arg1, R_EDI);
    57     load_exc_backpatch(R_ESI);
    58     call_func0(ptr);
    59 }
    61 static inline void call_func1_r32disp8( int preg, uint32_t disp8, int arg1 )
    62 {
    63     REXW(); MOV_r32_r32(arg1, R_EDI);
    64     CALL_r32disp8(preg, disp8);    
    65 }
    67 #define CALL_FUNC2_SIZE 16
    68 static inline void call_func2( void *ptr, int arg1, int arg2 )
    69 {
    70     REXW(); MOV_r32_r32(arg1, R_EDI);
    71     REXW(); MOV_r32_r32(arg2, R_ESI);
    72     call_func0(ptr);
    73 }
    75 static inline void call_func2_r32disp8( int preg, uint32_t disp8, int arg1, int arg2 )
    76 {
    77     REXW(); MOV_r32_r32(arg1, R_EDI);
    78     REXW(); MOV_r32_r32(arg2, R_ESI);
    79     CALL_r32disp8(preg, disp8);    
    80 }
    83 #define MEM_WRITE_DOUBLE_SIZE 35
    84 /**
    85  * Write a double (64-bit) value into memory, with the first word in arg2a, and
    86  * the second in arg2b
    87  */
    88 static inline void MEM_WRITE_DOUBLE( int addr, int arg2a, int arg2b )
    89 {
    90     PUSH_r32(arg2b);
    91     PUSH_r32(addr);
    92     call_func2(sh4_write_long, addr, arg2a);
    93     POP_r32(R_EDI);
    94     POP_r32(R_ESI);
    95     ADD_imm8s_r32(4, R_EDI);
    96     call_func0(sh4_write_long);
    97 }
    99 #define MEM_READ_DOUBLE_SIZE 43
   100 /**
   101  * Read a double (64-bit) value from memory, writing the first word into arg2a
   102  * and the second into arg2b. The addr must not be in EAX
   103  */
   104 static inline void MEM_READ_DOUBLE( int addr, int arg2a, int arg2b )
   105 {
   106     REXW(); SUB_imm8s_r32( 8, R_ESP );
   107     PUSH_r32(addr);
   108     call_func1(sh4_read_long, addr);
   109     POP_r32(R_EDI);
   110     PUSH_r32(R_EAX);
   111     ADD_imm8s_r32(4, R_EDI);
   112     call_func0(sh4_read_long);
   113     MOV_r32_r32(R_EAX, arg2b);
   114     POP_r32(arg2a);
   115     REXW(); ADD_imm8s_r32( 8, R_ESP );
   116 }
   119 /**
   120  * Emit the 'start of block' assembly. Sets up the stack frame and save
   121  * SI/DI as required
   122  */
   123 void enter_block( ) 
   124 {
   125     PUSH_r32(R_EBP);
   126     load_ptr( R_EBP, ((uint8_t *)&sh4r) + 128 );
   127     // Minimum aligned allocation is 16 bytes
   128     REXW(); SUB_imm8s_r32( 16, R_ESP );
   129 }
   131 static inline void exit_block( )
   132 {
   133     REXW(); ADD_imm8s_r32( 16, R_ESP );
   134     POP_r32(R_EBP);
   135     RET();
   136 }
   138 /**
   139  * Exit the block with sh4r.pc already written
   140  */
   141 void exit_block_pcset( sh4addr_t pc )
   142 {
   143     load_imm32( R_ECX, ((pc - sh4_x86.block_start_pc)>>1)*sh4_cpu_period ); // 5
   144     ADD_r32_sh4r( R_ECX, REG_OFFSET(slice_cycle) );    // 6
   145     load_spreg( R_EAX, R_PC );
   146     if( sh4_x86.tlb_on ) {
   147         call_func1(xlat_get_code_by_vma,R_EAX);
   148     } else {
   149         call_func1(xlat_get_code,R_EAX);
   150     }
   151     exit_block();
   152 }
   154 /**
   155  * Exit the block with sh4r.new_pc written with the target address
   156  */
   157 void exit_block_newpcset( sh4addr_t pc )
   158 {
   159     load_imm32( R_ECX, ((pc - sh4_x86.block_start_pc)>>1)*sh4_cpu_period ); // 5
   160     ADD_r32_sh4r( R_ECX, REG_OFFSET(slice_cycle) );    // 6
   161     load_spreg( R_EAX, R_NEW_PC );
   162     store_spreg( R_EAX, R_PC );
   163     if( sh4_x86.tlb_on ) {
   164         call_func1(xlat_get_code_by_vma,R_EAX);
   165     } else {
   166         call_func1(xlat_get_code,R_EAX);
   167     }
   168     exit_block();
   169 }
   171 #define EXIT_BLOCK_SIZE(pc) (25 + (IS_IN_ICACHE(pc)?10:CALL_FUNC1_SIZE))
   172 /**
   173  * Exit the block to an absolute PC
   174  */
   175 void exit_block_abs( sh4addr_t pc, sh4addr_t endpc )
   176 {
   177     load_imm32( R_ECX, pc );                            // 5
   178     store_spreg( R_ECX, REG_OFFSET(pc) );               // 3
   179     if( IS_IN_ICACHE(pc) ) {
   180         REXW(); MOV_moff32_EAX( xlat_get_lut_entry(pc) );
   181         REXW(); AND_imm8s_r32( 0xFC, R_EAX ); // 4
   182     } else if( sh4_x86.tlb_on ) {
   183         call_func1(xlat_get_code_by_vma, R_ECX);
   184     } else {
   185         call_func1(xlat_get_code,R_ECX);
   186     }
   187     load_imm32( R_ECX, ((endpc - sh4_x86.block_start_pc)>>1)*sh4_cpu_period ); // 5
   188     ADD_r32_sh4r( R_ECX, REG_OFFSET(slice_cycle) );     // 6
   189     exit_block();
   190 }
   193 #define EXIT_BLOCK_REL_SIZE(pc)  (28 + (IS_IN_ICACHE(pc)?10:CALL_FUNC1_SIZE))
   195 /**
   196  * Exit the block to a relative PC
   197  */
   198 void exit_block_rel( sh4addr_t pc, sh4addr_t endpc )
   199 {
   200     load_imm32( R_ECX, pc - sh4_x86.block_start_pc );   // 5
   201     ADD_sh4r_r32( R_PC, R_ECX );
   202     store_spreg( R_ECX, REG_OFFSET(pc) );               // 3
   203     if( IS_IN_ICACHE(pc) ) {
   204         REXW(); MOV_moff32_EAX( xlat_get_lut_entry(GET_ICACHE_PHYS(pc)) ); // 5
   205         REXW(); AND_imm8s_r32( 0xFC, R_EAX ); // 4
   206     } else if( sh4_x86.tlb_on ) {
   207         call_func1(xlat_get_code_by_vma,R_ECX);
   208     } else {
   209         call_func1(xlat_get_code,R_ECX);
   210     }
   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     exit_block();
   214 }
   216 /**
   217  * Write the block trailer (exception handling block)
   218  */
   219 void sh4_translate_end_block( sh4addr_t pc ) {
   220     if( sh4_x86.branch_taken == FALSE ) {
   221         // Didn't exit unconditionally already, so write the termination here
   222         exit_block_rel( pc, pc );
   223     }
   224     if( sh4_x86.backpatch_posn != 0 ) {
   225         unsigned int i;
   226         // Raise exception
   227         uint8_t *end_ptr = xlat_output;
   228         MOV_r32_r32( R_EDX, R_ECX );
   229         ADD_r32_r32( R_EDX, R_ECX );
   230         ADD_r32_sh4r( R_ECX, R_PC );
   231         MOV_moff32_EAX( &sh4_cpu_period );
   232         MUL_r32( R_EDX );
   233         ADD_r32_sh4r( R_EAX, REG_OFFSET(slice_cycle) );
   235         call_func0( sh4_raise_exception );
   236         load_spreg( R_EAX, R_PC );
   237         if( sh4_x86.tlb_on ) {
   238             call_func1(xlat_get_code_by_vma,R_EAX);
   239         } else {
   240             call_func1(xlat_get_code,R_EAX);
   241         }
   242         exit_block();
   244         // Exception already raised - just cleanup
   245         uint8_t *preexc_ptr = xlat_output;
   246         MOV_r32_r32( R_EDX, R_ECX );
   247         ADD_r32_r32( R_EDX, R_ECX );
   248         ADD_r32_sh4r( R_ECX, R_SPC );
   249         MOV_moff32_EAX( &sh4_cpu_period );
   250         MUL_r32( R_EDX );
   251         ADD_r32_sh4r( R_EAX, REG_OFFSET(slice_cycle) );
   252         load_spreg( R_EDI, R_PC );
   253         if( sh4_x86.tlb_on ) {
   254             call_func0(xlat_get_code_by_vma);
   255         } else {
   256             call_func0(xlat_get_code);
   257         }
   258         exit_block();
   260         for( i=0; i< sh4_x86.backpatch_posn; i++ ) {
   261             uint32_t *fixup_addr = (uint32_t *)&xlat_current_block->code[sh4_x86.backpatch_list[i].fixup_offset];
   262             if( sh4_x86.backpatch_list[i].exc_code < 0 ) {
   263                 if( sh4_x86.backpatch_list[i].exc_code == -2 ) {
   264                     *((uintptr_t *)fixup_addr) = (uintptr_t)xlat_output; 
   265                 } else {
   266                     *fixup_addr = xlat_output - (uint8_t *)&xlat_current_block->code[sh4_x86.backpatch_list[i].fixup_offset] - 4;
   267                 }
   268                 load_imm32( R_EDX, sh4_x86.backpatch_list[i].fixup_icount );
   269                 int rel = preexc_ptr - xlat_output;
   270                 JMP_rel(rel);
   271             } else {
   272                 *fixup_addr = xlat_output - (uint8_t *)&xlat_current_block->code[sh4_x86.backpatch_list[i].fixup_offset] - 4;
   273                 load_imm32( R_EDI, sh4_x86.backpatch_list[i].exc_code );
   274                 load_imm32( R_EDX, sh4_x86.backpatch_list[i].fixup_icount );
   275                 int rel = end_ptr - xlat_output;
   276                 JMP_rel(rel);
   277             }
   278         }
   279     }
   280 }
   282 struct UnwindInfo {
   283     uintptr_t block_start;
   284     uintptr_t block_end;
   285     void *pc;
   286 };
   288 _Unwind_Reason_Code xlat_check_frame( struct _Unwind_Context *context, void *arg )
   289 {
   290     struct UnwindInfo *info = arg;
   291     void *pc = (void *)_Unwind_GetIP(context);
   292     if( ((uintptr_t)pc) >= info->block_start && ((uintptr_t)pc) < info->block_end ) {
   293         info->pc = pc;
   294         return _URC_NORMAL_STOP;
   295     }
   297     return _URC_NO_REASON;
   298 }
   300 void *xlat_get_native_pc( void *code, uint32_t code_size )
   301 {
   302     struct _Unwind_Exception exc;
   303     struct UnwindInfo info;
   305     info.pc = NULL;
   306     info.block_start = (uintptr_t)code;
   307     info.block_end = info.block_start + code_size;
   308     void *result = NULL;
   309     _Unwind_Backtrace( xlat_check_frame, &info );
   310     return info.pc;
   311 }
   313 #endif /* !lxdream_ia64abi_H */
.