Search
lxdream.org :: lxdream/src/sh4/xltcache.c
lxdream 0.9.1
released Jun 29
Download Now
filename src/sh4/xltcache.c
changeset 901:32c5cf5e206f
prev809:8bdbf4d95da4
next905:4c17ebd9ef5e
author nkeynes
date Wed Oct 29 23:36:31 2008 +0000 (15 years ago)
permissions -rw-r--r--
last change Enable the FIPR SSE3 code for now, and add a comment on the sh4r.fr alignment
view annotate diff log raw
     1 /**
     2  * $Id$
     3  * 
     4  * Translation cache management. This part is architecture independent.
     5  *
     6  * Copyright (c) 2005 Nathan Keynes.
     7  *
     8  * This program is free software; you can redistribute it and/or modify
     9  * it under the terms of the GNU General Public License as published by
    10  * the Free Software Foundation; either version 2 of the License, or
    11  * (at your option) any later version.
    12  *
    13  * This program is distributed in the hope that it will be useful,
    14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16  * GNU General Public License for more details.
    17  */
    19 #include <sys/types.h>
    20 #include <sys/mman.h>
    21 #include <assert.h>
    23 #include "dreamcast.h"
    24 #include "sh4/sh4core.h"
    25 #include "sh4/xltcache.h"
    26 #include "x86dasm/x86dasm.h"
    28 #define XLAT_LUT_PAGE_BITS 12
    29 #define XLAT_LUT_TOTAL_BITS 28
    30 #define XLAT_LUT_PAGE(addr) (((addr)>>13) & 0xFFFF)
    31 #define XLAT_LUT_ENTRY(addr) (((addr)&0x1FFE) >> 1)
    33 #define XLAT_LUT_PAGES (1<<(XLAT_LUT_TOTAL_BITS-XLAT_LUT_PAGE_BITS))
    34 #define XLAT_LUT_PAGE_ENTRIES (1<<XLAT_LUT_PAGE_BITS)
    35 #define XLAT_LUT_PAGE_SIZE (XLAT_LUT_PAGE_ENTRIES * sizeof(void *))
    37 #define XLAT_LUT_ENTRY_EMPTY (void *)0
    38 #define XLAT_LUT_ENTRY_USED  (void *)1
    40 #define NEXT(block) ( (xlat_cache_block_t)&((block)->code[(block)->size]))
    41 #define IS_ENTRY_POINT(ent) (ent > XLAT_LUT_ENTRY_USED)
    42 #define IS_ENTRY_USED(ent) (ent != XLAT_LUT_ENTRY_EMPTY)
    44 #define MIN_BLOCK_SIZE 32
    45 #define MIN_TOTAL_SIZE (sizeof(struct xlat_cache_block)+MIN_BLOCK_SIZE)
    47 #define BLOCK_INACTIVE 0
    48 #define BLOCK_ACTIVE 1
    49 #define BLOCK_USED 2
    51 xlat_cache_block_t xlat_new_cache;
    52 xlat_cache_block_t xlat_new_cache_ptr;
    53 xlat_cache_block_t xlat_new_create_ptr;
    54 xlat_cache_block_t xlat_temp_cache;
    55 xlat_cache_block_t xlat_temp_cache_ptr;
    56 xlat_cache_block_t xlat_old_cache;
    57 xlat_cache_block_t xlat_old_cache_ptr;
    58 static void ***xlat_lut;
    59 static gboolean xlat_initialized = FALSE;
    61 void xlat_cache_init(void) 
    62 {
    63     if( !xlat_initialized ) {
    64         xlat_initialized = TRUE;
    65         xlat_new_cache = mmap( NULL, XLAT_NEW_CACHE_SIZE, PROT_EXEC|PROT_READ|PROT_WRITE,
    66                 MAP_PRIVATE|MAP_ANON, -1, 0 );
    67         xlat_temp_cache = mmap( NULL, XLAT_TEMP_CACHE_SIZE, PROT_EXEC|PROT_READ|PROT_WRITE,
    68                 MAP_PRIVATE|MAP_ANON, -1, 0 );
    69         xlat_old_cache = mmap( NULL, XLAT_OLD_CACHE_SIZE, PROT_EXEC|PROT_READ|PROT_WRITE,
    70                 MAP_PRIVATE|MAP_ANON, -1, 0 );
    71         xlat_new_cache_ptr = xlat_new_cache;
    72         xlat_temp_cache_ptr = xlat_temp_cache;
    73         xlat_old_cache_ptr = xlat_old_cache;
    74         xlat_new_create_ptr = xlat_new_cache;
    76         xlat_lut = mmap( NULL, XLAT_LUT_PAGES*sizeof(void *), PROT_READ|PROT_WRITE,
    77                 MAP_PRIVATE|MAP_ANON, -1, 0);
    78         memset( xlat_lut, 0, XLAT_LUT_PAGES*sizeof(void *) );
    79     }
    80     xlat_flush_cache();
    81 }
    83 void xlat_print_free( FILE *out )
    84 {
    85     fprintf( out, "New space: %d\nTemp space: %d\nOld space: %d\n", 
    86             xlat_new_cache_ptr->size, xlat_temp_cache_ptr->size, xlat_old_cache_ptr->size );
    87 }
    89 /**
    90  * Reset the cache structure to its default state
    91  */
    92 void xlat_flush_cache() 
    93 {
    94     xlat_cache_block_t tmp;
    95     int i;
    96     xlat_new_cache_ptr = xlat_new_cache;
    97     xlat_new_cache_ptr->active = 0;
    98     xlat_new_cache_ptr->size = XLAT_NEW_CACHE_SIZE - 2*sizeof(struct xlat_cache_block);
    99     tmp = NEXT(xlat_new_cache_ptr);
   100     tmp->active = 1;
   101     tmp->size = 0;
   102     xlat_temp_cache_ptr = xlat_temp_cache;
   103     xlat_temp_cache_ptr->active = 0;
   104     xlat_temp_cache_ptr->size = XLAT_TEMP_CACHE_SIZE - 2*sizeof(struct xlat_cache_block);
   105     tmp = NEXT(xlat_temp_cache_ptr);
   106     tmp->active = 1;
   107     tmp->size = 0;
   108     xlat_old_cache_ptr = xlat_old_cache;
   109     xlat_old_cache_ptr->active = 0;
   110     xlat_old_cache_ptr->size = XLAT_OLD_CACHE_SIZE - 2*sizeof(struct xlat_cache_block);
   111     tmp = NEXT(xlat_old_cache_ptr);
   112     tmp->active = 1;
   113     tmp->size = 0;
   114     for( i=0; i<XLAT_LUT_PAGES; i++ ) {
   115         if( xlat_lut[i] != NULL ) {
   116             memset( xlat_lut[i], 0, XLAT_LUT_PAGE_SIZE );
   117         }
   118     }
   119 }
   121 static void xlat_flush_page_by_lut( void **page )
   122 {
   123     int i;
   124     for( i=0; i<XLAT_LUT_PAGE_ENTRIES; i++ ) {
   125         if( IS_ENTRY_POINT(page[i]) ) {
   126             XLAT_BLOCK_FOR_CODE(page[i])->active = 0;
   127         }
   128         page[i] = NULL;
   129     }
   130 }
   132 void xlat_invalidate_word( sh4addr_t addr )
   133 {
   134     if( xlat_lut ) {
   135         void **page = xlat_lut[XLAT_LUT_PAGE(addr)];
   136         if( page != NULL ) {
   137             int entry = XLAT_LUT_ENTRY(addr);
   138             if( page[entry] != NULL ) {
   139                 xlat_flush_page_by_lut(page);
   140             }
   141         }
   142     }
   143 }
   145 void xlat_invalidate_long( sh4addr_t addr )
   146 {
   147     if( xlat_lut ) {
   148         void **page = xlat_lut[XLAT_LUT_PAGE(addr)];
   149         if( page != NULL ) {
   150             int entry = XLAT_LUT_ENTRY(addr);
   151             if( page[entry] != NULL || page[entry+1] != NULL ) {
   152                 xlat_flush_page_by_lut(page);
   153             }
   154         }
   155     }
   156 }
   158 void xlat_invalidate_block( sh4addr_t address, size_t size )
   159 {
   160     int i;
   161     int entry_count = size >> 1; // words;
   162     uint32_t page_no = XLAT_LUT_PAGE(address);
   163     int entry = XLAT_LUT_ENTRY(address);
   164     if( xlat_lut ) {
   165         do {
   166             void **page = xlat_lut[page_no];
   167             int page_entries = XLAT_LUT_PAGE_ENTRIES - entry;
   168             if( entry_count < page_entries ) {
   169                 page_entries = entry_count;
   170             }
   171             if( page != NULL ) {
   172                 if( page_entries == XLAT_LUT_PAGE_ENTRIES ) {
   173                     /* Overwriting the entire page anyway */
   174                     xlat_flush_page_by_lut(page);
   175                 } else {
   176                     for( i=entry; i<entry+page_entries; i++ ) {
   177                         if( page[i] != NULL ) {
   178                             xlat_flush_page_by_lut(page);
   179                             break;
   180                         }
   181                     }
   182                 }
   183                 entry_count -= page_entries;
   184             }
   185             page_no ++;
   186             entry_count -= page_entries;
   187             entry = 0;
   188         } while( entry_count > 0 );
   189     }
   190 }
   192 void xlat_flush_page( sh4addr_t address )
   193 {
   194     void **page = xlat_lut[XLAT_LUT_PAGE(address)];
   195     if( page != NULL ) {
   196         xlat_flush_page_by_lut(page);
   197     }
   198 }
   200 void *xlat_get_code( sh4addr_t address )
   201 {
   202     void *result = NULL;
   203     void **page = xlat_lut[XLAT_LUT_PAGE(address)];
   204     if( page != NULL ) {
   205         result = (void *)(((uintptr_t)(page[XLAT_LUT_ENTRY(address)])) & (~((uintptr_t)0x03)));
   206     }
   207     return result;
   208 }
   210 xlat_recovery_record_t xlat_get_post_recovery( void *code, void *native_pc, gboolean with_terminal )
   211 {
   212     if( code != NULL ) {
   213         uintptr_t pc_offset = ((uint8_t *)native_pc) - ((uint8_t *)code);
   214         xlat_cache_block_t block = XLAT_BLOCK_FOR_CODE(code);
   215         uint32_t count = block->recover_table_size;
   216         xlat_recovery_record_t records = (xlat_recovery_record_t)(&block->code[block->recover_table_offset]);
   217         uint32_t posn;
   218         if( count > 0 && !with_terminal )
   219         	count--;
   220         if( records[count-1].xlat_offset < pc_offset ) {
   221         	return NULL;
   222         }
   223         for( posn=count-1; posn > 0; posn-- ) {
   224         	if( records[posn-1].xlat_offset < pc_offset ) {
   225         		return &records[posn];
   226         	}
   227         }
   228         return &records[0]; // shouldn't happen
   229     }
   230     return NULL;
   231 }
   233 xlat_recovery_record_t xlat_get_pre_recovery( void *code, void *native_pc )
   234 {
   235     if( code != NULL ) {
   236         uintptr_t pc_offset = ((uint8_t *)native_pc) - ((uint8_t *)code);
   237         xlat_cache_block_t block = XLAT_BLOCK_FOR_CODE(code);
   238         uint32_t count = block->recover_table_size;
   239         xlat_recovery_record_t records = (xlat_recovery_record_t)(&block->code[block->recover_table_offset]);
   240         uint32_t posn;
   241         for( posn = 1; posn < count; posn++ ) {
   242         	if( records[posn].xlat_offset >= pc_offset ) {
   243         		return &records[posn-1];
   244         	}
   245         }
   246         return &records[count-1];
   247     }
   248     return NULL;	
   249 }
   251 void **xlat_get_lut_entry( sh4addr_t address )
   252 {
   253     void **page = xlat_lut[XLAT_LUT_PAGE(address)];
   255     /* Add the LUT entry for the block */
   256     if( page == NULL ) {
   257         xlat_lut[XLAT_LUT_PAGE(address)] = page =
   258             mmap( NULL, XLAT_LUT_PAGE_SIZE, PROT_READ|PROT_WRITE,
   259                     MAP_PRIVATE|MAP_ANON, -1, 0 );
   260         memset( page, 0, XLAT_LUT_PAGE_SIZE );
   261     }
   263     return &page[XLAT_LUT_ENTRY(address)];
   264 }
   268 uint32_t xlat_get_block_size( void *block )
   269 {
   270     xlat_cache_block_t xlt = (xlat_cache_block_t)(((char *)block)-sizeof(struct xlat_cache_block));
   271     return xlt->size;
   272 }
   274 uint32_t xlat_get_code_size( void *block )
   275 {
   276     xlat_cache_block_t xlt = (xlat_cache_block_t)(((char *)block)-sizeof(struct xlat_cache_block));
   277     if( xlt->recover_table_offset == 0 ) {
   278         return xlt->size;
   279     } else {
   280         return xlt->recover_table_offset;
   281     }
   282 }
   284 /**
   285  * Cut the specified block so that it has the given size, with the remaining data
   286  * forming a new free block. If the free block would be less than the minimum size,
   287  * the cut is not performed.
   288  * @return the next block after the (possibly cut) block.
   289  */
   290 static inline xlat_cache_block_t xlat_cut_block( xlat_cache_block_t block, int cutsize )
   291 {
   292     cutsize = (cutsize + 3) & 0xFFFFFFFC; // force word alignment
   293     assert( cutsize <= block->size );
   294     if( block->size > cutsize + MIN_TOTAL_SIZE ) {
   295         int oldsize = block->size;
   296         block->size = cutsize;
   297         xlat_cache_block_t next = NEXT(block);
   298         next->active = 0;
   299         next->size = oldsize - cutsize - sizeof(struct xlat_cache_block);
   300         return next;
   301     } else {
   302         return NEXT(block);
   303     }
   304 }
   306 /**
   307  * Promote a block in temp space (or elsewhere for that matter) to old space.
   308  *
   309  * @param block to promote.
   310  */
   311 static void xlat_promote_to_old_space( xlat_cache_block_t block )
   312 {
   313     int allocation = (int)-sizeof(struct xlat_cache_block);
   314     int size = block->size;
   315     xlat_cache_block_t curr = xlat_old_cache_ptr;
   316     xlat_cache_block_t start_block = curr;
   317     do {
   318         allocation += curr->size + sizeof(struct xlat_cache_block);
   319         curr = NEXT(curr);
   320         if( allocation > size ) {
   321             break; /* done */
   322         }
   323         if( curr->size == 0 ) { /* End-of-cache Sentinel */
   324             /* Leave what we just released as free space and start again from the
   325              * top of the cache
   326              */
   327             start_block->active = 0;
   328             start_block->size = allocation;
   329             allocation = (int)-sizeof(struct xlat_cache_block);
   330             start_block = curr = xlat_old_cache;
   331         }
   332     } while(1);
   333     start_block->active = 1;
   334     start_block->size = allocation;
   335     start_block->lut_entry = block->lut_entry;
   336     start_block->fpscr_mask = block->fpscr_mask;
   337     start_block->fpscr = block->fpscr;
   338     start_block->recover_table_offset = block->recover_table_offset;
   339     start_block->recover_table_size = block->recover_table_size;
   340     *block->lut_entry = &start_block->code;
   341     memcpy( start_block->code, block->code, block->size );
   342     xlat_old_cache_ptr = xlat_cut_block(start_block, size );
   343     if( xlat_old_cache_ptr->size == 0 ) {
   344         xlat_old_cache_ptr = xlat_old_cache;
   345     }
   346 }
   348 /**
   349  * Similarly to the above method, promotes a block to temp space.
   350  * TODO: Try to combine these - they're nearly identical
   351  */
   352 void xlat_promote_to_temp_space( xlat_cache_block_t block )
   353 {
   354     int size = block->size;
   355     int allocation = (int)-sizeof(struct xlat_cache_block);
   356     xlat_cache_block_t curr = xlat_temp_cache_ptr;
   357     xlat_cache_block_t start_block = curr;
   358     do {
   359         if( curr->active == BLOCK_USED ) {
   360             xlat_promote_to_old_space( curr );
   361         } else if( curr->active == BLOCK_ACTIVE ) {
   362             // Active but not used, release block
   363             *((uintptr_t *)curr->lut_entry) &= ((uintptr_t)0x03);
   364         }
   365         allocation += curr->size + sizeof(struct xlat_cache_block);
   366         curr = NEXT(curr);
   367         if( allocation > size ) {
   368             break; /* done */
   369         }
   370         if( curr->size == 0 ) { /* End-of-cache Sentinel */
   371             /* Leave what we just released as free space and start again from the
   372              * top of the cache
   373              */
   374             start_block->active = 0;
   375             start_block->size = allocation;
   376             allocation = (int)-sizeof(struct xlat_cache_block);
   377             start_block = curr = xlat_temp_cache;
   378         }
   379     } while(1);
   380     start_block->active = 1;
   381     start_block->size = allocation;
   382     start_block->lut_entry = block->lut_entry;
   383     start_block->fpscr_mask = block->fpscr_mask;
   384     start_block->fpscr = block->fpscr;
   385     start_block->recover_table_offset = block->recover_table_offset;
   386     start_block->recover_table_size = block->recover_table_size;
   387     *block->lut_entry = &start_block->code;
   388     memcpy( start_block->code, block->code, block->size );
   389     xlat_temp_cache_ptr = xlat_cut_block(start_block, size );
   390     if( xlat_temp_cache_ptr->size == 0 ) {
   391         xlat_temp_cache_ptr = xlat_temp_cache;
   392     }
   394 }
   396 /**
   397  * Returns the next block in the new cache list that can be written to by the
   398  * translator. If the next block is active, it is evicted first.
   399  */
   400 xlat_cache_block_t xlat_start_block( sh4addr_t address )
   401 {
   402     if( xlat_new_cache_ptr->size == 0 ) {
   403         xlat_new_cache_ptr = xlat_new_cache;
   404     }
   406     if( xlat_new_cache_ptr->active ) {
   407         xlat_promote_to_temp_space( xlat_new_cache_ptr );
   408     }
   409     xlat_new_create_ptr = xlat_new_cache_ptr;
   410     xlat_new_create_ptr->active = 1;
   411     xlat_new_cache_ptr = NEXT(xlat_new_cache_ptr);
   413     /* Add the LUT entry for the block */
   414     if( xlat_lut[XLAT_LUT_PAGE(address)] == NULL ) {
   415         xlat_lut[XLAT_LUT_PAGE(address)] =
   416             mmap( NULL, XLAT_LUT_PAGE_SIZE, PROT_READ|PROT_WRITE,
   417                     MAP_PRIVATE|MAP_ANON, -1, 0 );
   418         memset( xlat_lut[XLAT_LUT_PAGE(address)], 0, XLAT_LUT_PAGE_SIZE );
   419     }
   421     if( IS_ENTRY_POINT(xlat_lut[XLAT_LUT_PAGE(address)][XLAT_LUT_ENTRY(address)]) ) {
   422         xlat_cache_block_t oldblock = XLAT_BLOCK_FOR_CODE(xlat_lut[XLAT_LUT_PAGE(address)][XLAT_LUT_ENTRY(address)]);
   423         oldblock->active = 0;
   424     }
   426     xlat_lut[XLAT_LUT_PAGE(address)][XLAT_LUT_ENTRY(address)] = 
   427         &xlat_new_create_ptr->code;
   428     xlat_new_create_ptr->lut_entry = xlat_lut[XLAT_LUT_PAGE(address)] + XLAT_LUT_ENTRY(address);
   430     return xlat_new_create_ptr;
   431 }
   433 xlat_cache_block_t xlat_extend_block( uint32_t newSize )
   434 {
   435     while( xlat_new_create_ptr->size < newSize ) {
   436         if( xlat_new_cache_ptr->size == 0 ) {
   437             /* Migrate to the front of the cache to keep it contiguous */
   438             xlat_new_create_ptr->active = 0;
   439             sh4ptr_t olddata = xlat_new_create_ptr->code;
   440             int oldsize = xlat_new_create_ptr->size;
   441             int size = oldsize + MIN_BLOCK_SIZE; /* minimum expansion */
   442             void **lut_entry = xlat_new_create_ptr->lut_entry;
   443             int allocation = (int)-sizeof(struct xlat_cache_block);
   444             xlat_new_cache_ptr = xlat_new_cache;
   445             do {
   446                 if( xlat_new_cache_ptr->active ) {
   447                     xlat_promote_to_temp_space( xlat_new_cache_ptr );
   448                 }
   449                 allocation += xlat_new_cache_ptr->size + sizeof(struct xlat_cache_block);
   450                 xlat_new_cache_ptr = NEXT(xlat_new_cache_ptr);
   451             } while( allocation < size );
   452             xlat_new_create_ptr = xlat_new_cache;
   453             xlat_new_create_ptr->active = 1;
   454             xlat_new_create_ptr->size = allocation;
   455             xlat_new_create_ptr->lut_entry = lut_entry;
   456             *lut_entry = &xlat_new_create_ptr->code;
   457             memmove( xlat_new_create_ptr->code, olddata, oldsize );
   458         } else {
   459             if( xlat_new_cache_ptr->active ) {
   460                 xlat_promote_to_temp_space( xlat_new_cache_ptr );
   461             }
   462             xlat_new_create_ptr->size += xlat_new_cache_ptr->size + sizeof(struct xlat_cache_block);
   463             xlat_new_cache_ptr = NEXT(xlat_new_cache_ptr);
   464         }
   465     }
   466     return xlat_new_create_ptr;
   468 }
   470 void xlat_commit_block( uint32_t destsize, uint32_t srcsize )
   471 {
   472     void **ptr = xlat_new_create_ptr->lut_entry;
   473     void **endptr = ptr + (srcsize>>2);
   474     while( ptr < endptr ) {
   475         if( *ptr == NULL ) {
   476             *ptr = XLAT_LUT_ENTRY_USED;
   477         }
   478         ptr++;
   479     }
   481     xlat_new_cache_ptr = xlat_cut_block( xlat_new_create_ptr, destsize );
   482 }
   484 void xlat_delete_block( xlat_cache_block_t block ) 
   485 {
   486     block->active = 0;
   487     *block->lut_entry = NULL;
   488 }
   490 void xlat_check_cache_integrity( xlat_cache_block_t cache, xlat_cache_block_t ptr, int size )
   491 {
   492     int foundptr = 0;
   493     xlat_cache_block_t tail = 
   494         (xlat_cache_block_t)(((char *)cache) + size - sizeof(struct xlat_cache_block));
   496     assert( tail->active == 1 );
   497     assert( tail->size == 0 ); 
   498     while( cache < tail ) {
   499         assert( cache->active >= 0 && cache->active <= 2 );
   500         assert( cache->size >= 0 && cache->size < size );
   501         if( cache == ptr ) {
   502             foundptr = 1;
   503         }
   504         cache = NEXT(cache);
   505     }
   506     assert( cache == tail );
   507     assert( foundptr == 1 || tail == ptr );
   508 }
   510 void xlat_check_integrity( )
   511 {
   512     xlat_check_cache_integrity( xlat_new_cache, xlat_new_cache_ptr, XLAT_NEW_CACHE_SIZE );
   513     xlat_check_cache_integrity( xlat_temp_cache, xlat_temp_cache_ptr, XLAT_TEMP_CACHE_SIZE );
   514     xlat_check_cache_integrity( xlat_old_cache, xlat_old_cache_ptr, XLAT_OLD_CACHE_SIZE );
   515 }
.