filename | src/sh4/xltcache.c |
changeset | 935:45246788ca00 |
prev | 922:8a8361264b1e |
next | 938:e377bd827c54 |
author | nkeynes |
date | Sat Dec 27 02:59:35 2008 +0000 (15 years ago) |
branch | lxdream-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 * 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;
55 #ifdef XLAT_GENERATIONAL_CACHE
56 xlat_cache_block_t xlat_temp_cache;
57 xlat_cache_block_t xlat_temp_cache_ptr;
58 xlat_cache_block_t xlat_old_cache;
59 xlat_cache_block_t xlat_old_cache_ptr;
60 #endif
62 static void **xlat_lut[XLAT_LUT_PAGES];
63 static gboolean xlat_initialized = FALSE;
65 void xlat_cache_init(void)
66 {
67 if( !xlat_initialized ) {
68 xlat_initialized = TRUE;
69 xlat_new_cache = mmap( NULL, XLAT_NEW_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_new_create_ptr = xlat_new_cache;
73 #ifdef XLAT_GENERATIONAL_CACHE
74 xlat_temp_cache = mmap( NULL, XLAT_TEMP_CACHE_SIZE, PROT_EXEC|PROT_READ|PROT_WRITE,
75 MAP_PRIVATE|MAP_ANON, -1, 0 );
76 xlat_old_cache = mmap( NULL, XLAT_OLD_CACHE_SIZE, PROT_EXEC|PROT_READ|PROT_WRITE,
77 MAP_PRIVATE|MAP_ANON, -1, 0 );
78 xlat_temp_cache_ptr = xlat_temp_cache;
79 xlat_old_cache_ptr = xlat_old_cache;
80 #endif
81 // xlat_lut = mmap( NULL, XLAT_LUT_PAGES*sizeof(void *), PROT_READ|PROT_WRITE,
82 // MAP_PRIVATE|MAP_ANON, -1, 0);
83 memset( xlat_lut, 0, XLAT_LUT_PAGES*sizeof(void *) );
84 }
85 xlat_flush_cache();
86 }
88 /**
89 * Reset the cache structure to its default state
90 */
91 void xlat_flush_cache()
92 {
93 xlat_cache_block_t tmp;
94 int i;
95 xlat_new_cache_ptr = xlat_new_cache;
96 xlat_new_cache_ptr->active = 0;
97 xlat_new_cache_ptr->size = XLAT_NEW_CACHE_SIZE - 2*sizeof(struct xlat_cache_block);
98 tmp = NEXT(xlat_new_cache_ptr);
99 tmp->active = 1;
100 tmp->size = 0;
101 #ifdef XLAT_GENERATIONAL_CACHE
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 #endif
115 for( i=0; i<XLAT_LUT_PAGES; i++ ) {
116 if( xlat_lut[i] != NULL ) {
117 memset( xlat_lut[i], 0, XLAT_LUT_PAGE_SIZE );
118 }
119 }
120 }
122 static void xlat_flush_page_by_lut( void **page )
123 {
124 int i;
125 for( i=0; i<XLAT_LUT_PAGE_ENTRIES; i++ ) {
126 if( IS_ENTRY_POINT(page[i]) ) {
127 XLAT_BLOCK_FOR_CODE(page[i])->active = 0;
128 }
129 page[i] = NULL;
130 }
131 }
133 void FASTCALL xlat_invalidate_word( sh4addr_t addr )
134 {
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 }
144 void FASTCALL xlat_invalidate_long( sh4addr_t addr )
145 {
146 void **page = xlat_lut[XLAT_LUT_PAGE(addr)];
147 if( page != NULL ) {
148 int entry = XLAT_LUT_ENTRY(addr);
149 if( page[entry] != NULL || page[entry+1] != NULL ) {
150 xlat_flush_page_by_lut(page);
151 }
152 }
153 }
155 void FASTCALL xlat_invalidate_block( sh4addr_t address, size_t size )
156 {
157 int i;
158 int entry_count = size >> 1; // words;
159 uint32_t page_no = XLAT_LUT_PAGE(address);
160 int entry = XLAT_LUT_ENTRY(address);
161 do {
162 void **page = xlat_lut[page_no];
163 int page_entries = XLAT_LUT_PAGE_ENTRIES - entry;
164 if( entry_count < page_entries ) {
165 page_entries = entry_count;
166 }
167 if( page != NULL ) {
168 if( page_entries == XLAT_LUT_PAGE_ENTRIES ) {
169 /* Overwriting the entire page anyway */
170 xlat_flush_page_by_lut(page);
171 } else {
172 for( i=entry; i<entry+page_entries; i++ ) {
173 if( page[i] != NULL ) {
174 xlat_flush_page_by_lut(page);
175 break;
176 }
177 }
178 }
179 entry_count -= page_entries;
180 }
181 page_no ++;
182 entry_count -= page_entries;
183 entry = 0;
184 } while( entry_count > 0 );
185 }
187 void FASTCALL xlat_flush_page( sh4addr_t address )
188 {
189 void **page = xlat_lut[XLAT_LUT_PAGE(address)];
190 if( page != NULL ) {
191 xlat_flush_page_by_lut(page);
192 }
193 }
195 void * FASTCALL xlat_get_code( sh4addr_t address )
196 {
197 void *result = NULL;
198 void **page = xlat_lut[XLAT_LUT_PAGE(address)];
199 if( page != NULL ) {
200 result = (void *)(((uintptr_t)(page[XLAT_LUT_ENTRY(address)])) & (~((uintptr_t)0x03)));
201 }
202 return result;
203 }
205 xlat_recovery_record_t xlat_get_post_recovery( void *code, void *native_pc, gboolean with_terminal )
206 {
207 if( code != NULL ) {
208 uintptr_t pc_offset = ((uint8_t *)native_pc) - ((uint8_t *)code);
209 xlat_cache_block_t block = XLAT_BLOCK_FOR_CODE(code);
210 uint32_t count = block->recover_table_size;
211 xlat_recovery_record_t records = (xlat_recovery_record_t)(&block->code[block->recover_table_offset]);
212 uint32_t posn;
213 if( count > 0 && !with_terminal )
214 count--;
215 if( records[count-1].xlat_offset < pc_offset ) {
216 return NULL;
217 }
218 for( posn=count-1; posn > 0; posn-- ) {
219 if( records[posn-1].xlat_offset < pc_offset ) {
220 return &records[posn];
221 }
222 }
223 return &records[0]; // shouldn't happen
224 }
225 return NULL;
226 }
228 xlat_recovery_record_t xlat_get_pre_recovery( void *code, void *native_pc )
229 {
230 if( code != NULL ) {
231 uintptr_t pc_offset = ((uint8_t *)native_pc) - ((uint8_t *)code);
232 xlat_cache_block_t block = XLAT_BLOCK_FOR_CODE(code);
233 uint32_t count = block->recover_table_size;
234 xlat_recovery_record_t records = (xlat_recovery_record_t)(&block->code[block->recover_table_offset]);
235 uint32_t posn;
236 for( posn = 1; posn < count; posn++ ) {
237 if( records[posn].xlat_offset >= pc_offset ) {
238 return &records[posn-1];
239 }
240 }
241 return &records[count-1];
242 }
243 return NULL;
244 }
246 void ** FASTCALL xlat_get_lut_entry( sh4addr_t address )
247 {
248 void **page = xlat_lut[XLAT_LUT_PAGE(address)];
250 /* Add the LUT entry for the block */
251 if( page == NULL ) {
252 xlat_lut[XLAT_LUT_PAGE(address)] = page =
253 mmap( NULL, XLAT_LUT_PAGE_SIZE, PROT_READ|PROT_WRITE,
254 MAP_PRIVATE|MAP_ANON, -1, 0 );
255 memset( page, 0, XLAT_LUT_PAGE_SIZE );
256 }
258 return &page[XLAT_LUT_ENTRY(address)];
259 }
263 uint32_t FASTCALL xlat_get_block_size( void *block )
264 {
265 xlat_cache_block_t xlt = (xlat_cache_block_t)(((char *)block)-sizeof(struct xlat_cache_block));
266 return xlt->size;
267 }
269 uint32_t FASTCALL xlat_get_code_size( void *block )
270 {
271 xlat_cache_block_t xlt = (xlat_cache_block_t)(((char *)block)-sizeof(struct xlat_cache_block));
272 if( xlt->recover_table_offset == 0 ) {
273 return xlt->size;
274 } else {
275 return xlt->recover_table_offset;
276 }
277 }
279 /**
280 * Cut the specified block so that it has the given size, with the remaining data
281 * forming a new free block. If the free block would be less than the minimum size,
282 * the cut is not performed.
283 * @return the next block after the (possibly cut) block.
284 */
285 static inline xlat_cache_block_t xlat_cut_block( xlat_cache_block_t block, int cutsize )
286 {
287 cutsize = (cutsize + 3) & 0xFFFFFFFC; // force word alignment
288 assert( cutsize <= block->size );
289 if( block->size > cutsize + MIN_TOTAL_SIZE ) {
290 int oldsize = block->size;
291 block->size = cutsize;
292 xlat_cache_block_t next = NEXT(block);
293 next->active = 0;
294 next->size = oldsize - cutsize - sizeof(struct xlat_cache_block);
295 return next;
296 } else {
297 return NEXT(block);
298 }
299 }
301 #ifdef XLAT_GENERATIONAL_CACHE
302 /**
303 * Promote a block in temp space (or elsewhere for that matter) to old space.
304 *
305 * @param block to promote.
306 */
307 static void xlat_promote_to_old_space( xlat_cache_block_t block )
308 {
309 int allocation = (int)-sizeof(struct xlat_cache_block);
310 int size = block->size;
311 xlat_cache_block_t curr = xlat_old_cache_ptr;
312 xlat_cache_block_t start_block = curr;
313 do {
314 allocation += curr->size + sizeof(struct xlat_cache_block);
315 curr = NEXT(curr);
316 if( allocation > size ) {
317 break; /* done */
318 }
319 if( curr->size == 0 ) { /* End-of-cache Sentinel */
320 /* Leave what we just released as free space and start again from the
321 * top of the cache
322 */
323 start_block->active = 0;
324 start_block->size = allocation;
325 allocation = (int)-sizeof(struct xlat_cache_block);
326 start_block = curr = xlat_old_cache;
327 }
328 } while(1);
329 start_block->active = 1;
330 start_block->size = allocation;
331 start_block->lut_entry = block->lut_entry;
332 start_block->fpscr_mask = block->fpscr_mask;
333 start_block->fpscr = block->fpscr;
334 start_block->recover_table_offset = block->recover_table_offset;
335 start_block->recover_table_size = block->recover_table_size;
336 *block->lut_entry = &start_block->code;
337 memcpy( start_block->code, block->code, block->size );
338 xlat_old_cache_ptr = xlat_cut_block(start_block, size );
339 if( xlat_old_cache_ptr->size == 0 ) {
340 xlat_old_cache_ptr = xlat_old_cache;
341 }
342 }
344 /**
345 * Similarly to the above method, promotes a block to temp space.
346 * TODO: Try to combine these - they're nearly identical
347 */
348 void xlat_promote_to_temp_space( xlat_cache_block_t block )
349 {
350 int size = block->size;
351 int allocation = (int)-sizeof(struct xlat_cache_block);
352 xlat_cache_block_t curr = xlat_temp_cache_ptr;
353 xlat_cache_block_t start_block = curr;
354 do {
355 if( curr->active == BLOCK_USED ) {
356 xlat_promote_to_old_space( curr );
357 } else if( curr->active == BLOCK_ACTIVE ) {
358 // Active but not used, release block
359 *((uintptr_t *)curr->lut_entry) &= ((uintptr_t)0x03);
360 }
361 allocation += curr->size + sizeof(struct xlat_cache_block);
362 curr = NEXT(curr);
363 if( allocation > size ) {
364 break; /* done */
365 }
366 if( curr->size == 0 ) { /* End-of-cache Sentinel */
367 /* Leave what we just released as free space and start again from the
368 * top of the cache
369 */
370 start_block->active = 0;
371 start_block->size = allocation;
372 allocation = (int)-sizeof(struct xlat_cache_block);
373 start_block = curr = xlat_temp_cache;
374 }
375 } while(1);
376 start_block->active = 1;
377 start_block->size = allocation;
378 start_block->lut_entry = block->lut_entry;
379 start_block->fpscr_mask = block->fpscr_mask;
380 start_block->fpscr = block->fpscr;
381 start_block->recover_table_offset = block->recover_table_offset;
382 start_block->recover_table_size = block->recover_table_size;
383 *block->lut_entry = &start_block->code;
384 memcpy( start_block->code, block->code, block->size );
385 xlat_temp_cache_ptr = xlat_cut_block(start_block, size );
386 if( xlat_temp_cache_ptr->size == 0 ) {
387 xlat_temp_cache_ptr = xlat_temp_cache;
388 }
390 }
391 #else
392 void xlat_promote_to_temp_space( xlat_cache_block_t block )
393 {
394 *block->lut_entry = 0;
395 }
396 #endif
398 /**
399 * Returns the next block in the new cache list that can be written to by the
400 * translator. If the next block is active, it is evicted first.
401 */
402 xlat_cache_block_t xlat_start_block( sh4addr_t address )
403 {
404 if( xlat_new_cache_ptr->size == 0 ) {
405 xlat_new_cache_ptr = xlat_new_cache;
406 }
408 if( xlat_new_cache_ptr->active ) {
409 xlat_promote_to_temp_space( xlat_new_cache_ptr );
410 }
411 xlat_new_create_ptr = xlat_new_cache_ptr;
412 xlat_new_create_ptr->active = 1;
413 xlat_new_cache_ptr = NEXT(xlat_new_cache_ptr);
415 /* Add the LUT entry for the block */
416 if( xlat_lut[XLAT_LUT_PAGE(address)] == NULL ) {
417 xlat_lut[XLAT_LUT_PAGE(address)] =
418 mmap( NULL, XLAT_LUT_PAGE_SIZE, PROT_READ|PROT_WRITE,
419 MAP_PRIVATE|MAP_ANON, -1, 0 );
420 memset( xlat_lut[XLAT_LUT_PAGE(address)], 0, XLAT_LUT_PAGE_SIZE );
421 }
423 if( IS_ENTRY_POINT(xlat_lut[XLAT_LUT_PAGE(address)][XLAT_LUT_ENTRY(address)]) ) {
424 xlat_cache_block_t oldblock = XLAT_BLOCK_FOR_CODE(xlat_lut[XLAT_LUT_PAGE(address)][XLAT_LUT_ENTRY(address)]);
425 oldblock->active = 0;
426 }
428 xlat_lut[XLAT_LUT_PAGE(address)][XLAT_LUT_ENTRY(address)] =
429 &xlat_new_create_ptr->code;
430 xlat_new_create_ptr->lut_entry = xlat_lut[XLAT_LUT_PAGE(address)] + XLAT_LUT_ENTRY(address);
432 return xlat_new_create_ptr;
433 }
435 xlat_cache_block_t xlat_extend_block( uint32_t newSize )
436 {
437 while( xlat_new_create_ptr->size < newSize ) {
438 if( xlat_new_cache_ptr->size == 0 ) {
439 /* Migrate to the front of the cache to keep it contiguous */
440 xlat_new_create_ptr->active = 0;
441 sh4ptr_t olddata = xlat_new_create_ptr->code;
442 int oldsize = xlat_new_create_ptr->size;
443 int size = oldsize + MIN_BLOCK_SIZE; /* minimum expansion */
444 void **lut_entry = xlat_new_create_ptr->lut_entry;
445 int allocation = (int)-sizeof(struct xlat_cache_block);
446 xlat_new_cache_ptr = xlat_new_cache;
447 do {
448 if( xlat_new_cache_ptr->active ) {
449 xlat_promote_to_temp_space( xlat_new_cache_ptr );
450 }
451 allocation += xlat_new_cache_ptr->size + sizeof(struct xlat_cache_block);
452 xlat_new_cache_ptr = NEXT(xlat_new_cache_ptr);
453 } while( allocation < size );
454 xlat_new_create_ptr = xlat_new_cache;
455 xlat_new_create_ptr->active = 1;
456 xlat_new_create_ptr->size = allocation;
457 xlat_new_create_ptr->lut_entry = lut_entry;
458 *lut_entry = &xlat_new_create_ptr->code;
459 memmove( xlat_new_create_ptr->code, olddata, oldsize );
460 } else {
461 if( xlat_new_cache_ptr->active ) {
462 xlat_promote_to_temp_space( xlat_new_cache_ptr );
463 }
464 xlat_new_create_ptr->size += xlat_new_cache_ptr->size + sizeof(struct xlat_cache_block);
465 xlat_new_cache_ptr = NEXT(xlat_new_cache_ptr);
466 }
467 }
468 return xlat_new_create_ptr;
470 }
472 void xlat_commit_block( uint32_t destsize, uint32_t srcsize )
473 {
474 void **ptr = xlat_new_create_ptr->lut_entry;
475 void **endptr = ptr + (srcsize>>2);
476 while( ptr < endptr ) {
477 if( *ptr == NULL ) {
478 *ptr = XLAT_LUT_ENTRY_USED;
479 }
480 ptr++;
481 }
483 xlat_new_cache_ptr = xlat_cut_block( xlat_new_create_ptr, destsize );
484 }
486 void xlat_delete_block( xlat_cache_block_t block )
487 {
488 block->active = 0;
489 *block->lut_entry = NULL;
490 }
492 void xlat_check_cache_integrity( xlat_cache_block_t cache, xlat_cache_block_t ptr, int size )
493 {
494 int foundptr = 0;
495 xlat_cache_block_t tail =
496 (xlat_cache_block_t)(((char *)cache) + size - sizeof(struct xlat_cache_block));
498 assert( tail->active == 1 );
499 assert( tail->size == 0 );
500 while( cache < tail ) {
501 assert( cache->active >= 0 && cache->active <= 2 );
502 assert( cache->size >= 0 && cache->size < size );
503 if( cache == ptr ) {
504 foundptr = 1;
505 }
506 cache = NEXT(cache);
507 }
508 assert( cache == tail );
509 assert( foundptr == 1 || tail == ptr );
510 }
512 void xlat_check_integrity( )
513 {
514 xlat_check_cache_integrity( xlat_new_cache, xlat_new_cache_ptr, XLAT_NEW_CACHE_SIZE );
515 #ifdef XLAT_GENERATIONAL_CACHE
516 xlat_check_cache_integrity( xlat_temp_cache, xlat_temp_cache_ptr, XLAT_TEMP_CACHE_SIZE );
517 xlat_check_cache_integrity( xlat_old_cache, xlat_old_cache_ptr, XLAT_OLD_CACHE_SIZE );
518 #endif
519 }
.