blob: eaca9729b06e7042ccdf4328615072a8fc031ece [file] [log] [blame]
sewardjde4a1d02002-03-22 01:27:54 +00001
2/*--------------------------------------------------------------------*/
3/*--- Management of the translation table and cache. ---*/
4/*--- vg_transtab.c ---*/
5/*--------------------------------------------------------------------*/
6
7/*
njnc9539842002-10-02 13:26:35 +00008 This file is part of Valgrind, an extensible x86 protected-mode
9 emulator for monitoring program execution on x86-Unixes.
sewardjde4a1d02002-03-22 01:27:54 +000010
nethercotebb1c9912004-01-04 16:43:23 +000011 Copyright (C) 2000-2004 Julian Seward
sewardjde4a1d02002-03-22 01:27:54 +000012 jseward@acm.org
sewardjde4a1d02002-03-22 01:27:54 +000013
14 This program is free software; you can redistribute it and/or
15 modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation; either version 2 of the
17 License, or (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful, but
20 WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
27 02111-1307, USA.
28
njn25e49d8e72002-09-23 09:36:25 +000029 The GNU General Public License is contained in the file COPYING.
sewardjde4a1d02002-03-22 01:27:54 +000030*/
31
nethercotef1e5e152004-09-01 23:58:16 +000032#include "core.h"
sewardjde4a1d02002-03-22 01:27:54 +000033
sewardj18d75132002-05-16 11:06:21 +000034/* #define DEBUG_TRANSTAB */
35
sewardjde4a1d02002-03-22 01:27:54 +000036
sewardj6c3769f2002-11-29 01:02:45 +000037/*-------------------------------------------------------------*/
38/*--- Management of the FIFO-based translation table+cache. ---*/
39/*-------------------------------------------------------------*/
sewardjde4a1d02002-03-22 01:27:54 +000040
sewardj6c3769f2002-11-29 01:02:45 +000041/*------------------ CONSTANTS ------------------*/
sewardjde4a1d02002-03-22 01:27:54 +000042
sewardj6c3769f2002-11-29 01:02:45 +000043/* Number of sectors the TC is divided into. */
44#define VG_TC_N_SECTORS 8
sewardjde4a1d02002-03-22 01:27:54 +000045
sewardjc0d8f682002-11-30 00:49:43 +000046/* Calculated once at startup and never changed. */
njn9b007f62003-04-07 14:40:25 +000047static /* const */ Int vg_tc_sector_szB = 0;
sewardjde4a1d02002-03-22 01:27:54 +000048
49/* Number of entries in the translation table. This must be a prime
50 number in order to make the hashing work properly. */
jseward3f409e82003-12-14 14:25:19 +000051#define VG_TT_SIZE /*5281*/ /*100129*/ /*200191*/ /*250829*/ 300007
sewardjde4a1d02002-03-22 01:27:54 +000052
53/* Do an LRU pass when the translation table becomes this full. */
54#define VG_TT_LIMIT_PERCENT /*67*/ 80
55
sewardj6c3769f2002-11-29 01:02:45 +000056#define VG_TT_LIMIT ((VG_TT_SIZE * VG_TT_LIMIT_PERCENT) / 100)
sewardjde4a1d02002-03-22 01:27:54 +000057
sewardjde4a1d02002-03-22 01:27:54 +000058
sewardj6c3769f2002-11-29 01:02:45 +000059/*------------------ TYPES ------------------*/
60
nethercotee0ce1ac2004-10-26 13:37:48 +000061#define CODE_ALIGNMENT sizeof(void*) // alignment of TCEntries
sewardj22854b92002-11-30 14:00:47 +000062#define CODE_ALIGN(a) (((a)+CODE_ALIGNMENT-1) & ~(CODE_ALIGNMENT-1))
63#define IS_ALIGNED(a) (((a) & (CODE_ALIGNMENT-1)) == 0)
64
nethercotee0ce1ac2004-10-26 13:37:48 +000065/* An entry in TC. Payload always is always padded out to a word-aligned
sewardj6c3769f2002-11-29 01:02:45 +000066 quantity so that these structs are always word-aligned. */
67typedef
68 struct {
nethercotee0ce1ac2004-10-26 13:37:48 +000069 /* 32-bit or 64-bit offsets */
70 /* +0 or 0 */ Addr orig_addr;
71 /* +4 or 8 */ UShort orig_size;
72 /* +6 or 10 */ UShort trans_size;
73 /* +8 or 12 */ UShort jump_sites[VG_MAX_JUMPS];
sewardj22854b92002-11-30 14:00:47 +000074 /* +VG_CODE_OFFSET */ UChar payload[0];
sewardj6c3769f2002-11-29 01:02:45 +000075 }
76 TCEntry;
77
78/* An entry in TT. */
79typedef
80 struct {
81 Addr orig_addr;
82 TCEntry* tcentry;
83 }
84 TTEntry;
85
86/* Denotes an empty TT slot, when TTEntry.orig_addr holds this
87 value. */
88#define VG_TTE_EMPTY ((Addr)1)
89
90/* Denotes an empty TT slot, when TTEntry.orig_addr holds this
91 value. */
sewardjde4a1d02002-03-22 01:27:54 +000092#define VG_TTE_DELETED ((Addr)3)
93
sewardj6c3769f2002-11-29 01:02:45 +000094/* A bogus TCEntry which hopefully does not match code from any valid
95 address. This is what all VG_(tt_fast) entries are made to point
96 at when we want to invalidate it. */
njn29a0abf2003-05-14 12:56:36 +000097static const TCEntry vg_tc_bogus_TCEntry = { ((Addr)5), 0, 0 };
sewardjde4a1d02002-03-22 01:27:54 +000098
sewardjde4a1d02002-03-22 01:27:54 +000099
sewardj6c3769f2002-11-29 01:02:45 +0000100/*------------------ DECLS ------------------*/
101
102/* The translation cache sectors. These are NULL until allocated
103 dynamically. */
104static UChar* vg_tc[VG_TC_N_SECTORS];
105
106/* Count of bytes used in each sector of the TC. */
107static Int vg_tc_used[VG_TC_N_SECTORS];
108
109/* The age of each sector, so we can find the oldest. We just use the
110 global count of translations made when the sector was brought into
111 use. Doesn't matter if this mechanism gets confused (wraps around
112 4G) once in a while. */
113static Int vg_tc_age[VG_TC_N_SECTORS];
114
115/* The number of the sector currently being allocated in. */
116static Int vg_tc_current;
117
sewardjc0d8f682002-11-30 00:49:43 +0000118/* Count of number of translations, orig and new bytes in each sector.
119 For stats purposes only. */
120static Int vg_tc_stats_count[VG_TC_N_SECTORS];
121static Int vg_tc_stats_osize[VG_TC_N_SECTORS];
122static Int vg_tc_stats_tsize[VG_TC_N_SECTORS];
123
nethercote92e7b7f2004-08-07 17:52:25 +0000124static UInt n_tt_fast_misses = 0; // number of lookups missing fast TT helper
125static UInt n_tc_discards = 0; // number of TT/TC discards
126
127// Number and total original/translated size of translations overall.
128static UInt overall_in_count = 0;
129static UInt overall_in_osize = 0;
130static UInt overall_in_tsize = 0;
131// Number and total original/t size of discards overall.
132static UInt overall_out_count = 0;
133static UInt overall_out_osize = 0;
134static UInt overall_out_tsize = 0;
135
136
sewardj6c3769f2002-11-29 01:02:45 +0000137
138/*------------------ TRANSLATION TABLE ------------------*/
139
140/* The translation table. An array of VG_TT_SIZE TTEntrys. */
sewardjde4a1d02002-03-22 01:27:54 +0000141static TTEntry* vg_tt = NULL;
142
sewardj18d75132002-05-16 11:06:21 +0000143/* Count of non-empty TT entries. This includes deleted ones. */
sewardjde4a1d02002-03-22 01:27:54 +0000144static Int vg_tt_used = 0;
145
146/* Fast helper for the TT. A direct-mapped cache which holds a
147 pointer to a TT entry which may or may not be the correct one, but
148 which we hope usually is. This array is referred to directly from
149 vg_dispatch.S. */
sewardj6c3769f2002-11-29 01:02:45 +0000150Addr /* TCEntry*, really */ VG_(tt_fast)[VG_TT_FAST_SIZE];
sewardj8aef1192002-07-24 09:36:36 +0000151
sewardj22854b92002-11-30 14:00:47 +0000152static void for_each_tc(Int sector, void (*fn)(TCEntry *));
153
154
155/*------------------ T-CHAINING HELPERS ------------------*/
156
157static
158void for_each_jumpsite(TCEntry *tce, void (*fn)(Addr))
159{
160 Int i;
161 for(i = 0; i < VG_MAX_JUMPS; i++) {
162 Addr a;
163 UShort idx = tce->jump_sites[i];
164
165 if (idx == (UShort)-1)
166 continue;
167
168 a = (Addr)&tce->payload[idx];
169
170 (*fn)(a);
171 }
172}
173
174static inline
175void unchain_tce(TCEntry *tce)
176{
177 for_each_jumpsite(tce, VG_(unchain_jumpsite));
178}
179
180/* Unchain any jumps pointing to a sector we're about to free */
thughes4ad52d02004-06-27 17:37:21 +0000181static Addr sector_base;
182static Addr sector_len;
183
184static
185void unchain_site_for_sector(Addr a) {
186 Addr jmp = VG_(get_jmp_dest)(a);
187 if (jmp >= sector_base && jmp < (sector_base+sector_len))
188 VG_(unchain_jumpsite)(a);
189}
190
191static
192void unchain_tce_for_sector(TCEntry *tce) {
193 for_each_jumpsite(tce, unchain_site_for_sector);
194}
195
sewardj22854b92002-11-30 14:00:47 +0000196static
197void unchain_sector(Int s, Addr base, UInt len)
198{
thughes4ad52d02004-06-27 17:37:21 +0000199 sector_base = base;
200 sector_len = len;
sewardj22854b92002-11-30 14:00:47 +0000201
thughes4ad52d02004-06-27 17:37:21 +0000202 for_each_tc(s, unchain_tce_for_sector);
sewardj22854b92002-11-30 14:00:47 +0000203}
204
sewardjde4a1d02002-03-22 01:27:54 +0000205
sewardj6c3769f2002-11-29 01:02:45 +0000206/*------------------ TT HELPERS ------------------*/
207
sewardjc0d8f682002-11-30 00:49:43 +0000208static
209void pp_tt_tc_status ( Char* submsg )
210{
211 Int tc_used, s;
212 if (VG_(clo_verbosity) <= 2)
213 return;
214 tc_used = 0;
215 for (s = 0; s < VG_TC_N_SECTORS; s++)
216 tc_used += vg_tc_used[s];
217
218 VG_(message)(Vg_DebugMsg,
njne0205ff2003-04-08 00:56:14 +0000219 "%lluk bbs: tt %d, tc %d, %s",
sewardjc0d8f682002-11-30 00:49:43 +0000220 VG_(bbs_done) / 1000,
221 vg_tt_used, tc_used, submsg );
222}
223
sewardj6c3769f2002-11-29 01:02:45 +0000224/* Invalidate the tt_fast cache, for whatever reason, by pointing all
225 entries at vg_tc_bogus_TCEntry. */
226static
227void vg_invalidate_tt_fast( void )
228{
229 Int j;
230 for (j = 0; j < VG_TT_FAST_SIZE; j++)
231 VG_(tt_fast)[j] = (Addr)&vg_tc_bogus_TCEntry;
232}
233
234
235static
236void add_tt_entry ( TCEntry* tce )
237{
238 UInt i;
239 /* VG_(printf)("add_TT_entry orig_addr %p\n", tce->orig_addr); */
240 /* Hash to get initial probe point. */
nethercote50397c22004-11-04 18:03:06 +0000241 i = tce->orig_addr % VG_TT_SIZE;
sewardj6c3769f2002-11-29 01:02:45 +0000242 while (True) {
243 if (vg_tt[i].orig_addr == tce->orig_addr)
244 VG_(core_panic)("add_TT_entry: duplicate");
245 if (vg_tt[i].orig_addr == VG_TTE_EMPTY)
246 break;
247 i++;
248 if (i == VG_TT_SIZE)
249 i = 0;
250 }
sewardj22854b92002-11-30 14:00:47 +0000251
sewardj6c3769f2002-11-29 01:02:45 +0000252 vg_tt[i].orig_addr = tce->orig_addr;
253 vg_tt[i].tcentry = tce;
254 vg_tt_used++;
255 /* sanity ... */
256 vg_assert(vg_tt_used < VG_TT_SIZE-1000);
257}
258
259
260/* Search TT to find the translated address of the supplied original,
261 or NULL if not found. This routine is used when we miss in
262 VG_(tt_fast).
263*/
264static __inline__
265TTEntry* search_tt ( Addr orig_addr )
266{
267 Int i;
268 /* Hash to get initial probe point. */
nethercote50397c22004-11-04 18:03:06 +0000269 i = orig_addr % VG_TT_SIZE;
sewardj6c3769f2002-11-29 01:02:45 +0000270 while (True) {
271 if (vg_tt[i].orig_addr == orig_addr)
272 return &vg_tt[i];
273 if (vg_tt[i].orig_addr == VG_TTE_EMPTY)
274 return NULL;
275 i++;
276 if (i == VG_TT_SIZE) i = 0;
277 }
278}
279
280
281static
282void initialise_tt ( void )
283{
284 Int i;
285 vg_tt_used = 0;
286 for (i = 0; i < VG_TT_SIZE; i++) {
287 vg_tt[i].orig_addr = VG_TTE_EMPTY;
288 }
289 vg_invalidate_tt_fast();
290}
291
292
293static
294void rebuild_TT ( void )
295{
296 Int s;
sewardj6c3769f2002-11-29 01:02:45 +0000297
298 /* Throw away TT. */
299 initialise_tt();
300
301 /* Rebuild TT from the remaining quarters. */
302 for (s = 0; s < VG_TC_N_SECTORS; s++) {
sewardj22854b92002-11-30 14:00:47 +0000303 for_each_tc(s, add_tt_entry);
sewardj6c3769f2002-11-29 01:02:45 +0000304 }
sewardjc0d8f682002-11-30 00:49:43 +0000305 pp_tt_tc_status ( "after rebuild of TC" );
sewardj6c3769f2002-11-29 01:02:45 +0000306}
307
308
309/*------------------ TC HELPERS ------------------*/
310
sewardj22854b92002-11-30 14:00:47 +0000311static
312void for_each_tc(Int s, void (*fn)(TCEntry *))
313{
314 UChar *pc;
315 UChar *pc_lim;
316 TCEntry *tce;
317
318 pc = &(vg_tc[s][0]);
319 pc_lim = &(vg_tc[s][vg_tc_used[s]]);
320 while (True) {
321 if (pc >= pc_lim) break;
322 tce = (TCEntry*)pc;
323 pc += sizeof(TCEntry) + tce->trans_size;
324 if (tce->orig_addr != VG_TTE_DELETED)
325 (*fn)(tce);
326 }
327}
328
sewardj6c3769f2002-11-29 01:02:45 +0000329/* Find the oldest non-NULL, non-empty sector, or -1 if none such. */
330static
331Int find_oldest_sector ( void )
332{
333 Int oldest_age, oldest, i;
334 oldest_age = 1000 * 1000 * 1000;
335 oldest = -1;
336 for (i = 0; i < VG_TC_N_SECTORS; i++) {
337 if (vg_tc[i] == NULL)
338 continue;
339 if (vg_tc_used[i] == 0)
340 continue;
341 if (vg_tc_age[i] < oldest_age) {
342 oldest = i;
343 oldest_age = vg_tc_age[i];
344 }
345 }
346 return oldest;
347}
348
349
350/* Discard the oldest sector, if any such exists. */
351static
352void discard_oldest_sector ( void )
353{
sewardjc0d8f682002-11-30 00:49:43 +0000354 Char msg[100];
sewardj6c3769f2002-11-29 01:02:45 +0000355 Int s = find_oldest_sector();
356 if (s != -1) {
sewardj22854b92002-11-30 14:00:47 +0000357 Int i;
358
sewardj6c3769f2002-11-29 01:02:45 +0000359 vg_assert(s >= 0 && s < VG_TC_N_SECTORS);
sewardjc0d8f682002-11-30 00:49:43 +0000360 VG_(sprintf)(msg, "before discard of sector %d (%d bytes)",
361 s, vg_tc_used[s]);
sewardj22854b92002-11-30 14:00:47 +0000362
363 for(i = 0; i < VG_TC_N_SECTORS; i++) {
364 if (i != s && vg_tc[i] != NULL)
365 unchain_sector(i, (Addr)vg_tc[s], vg_tc_used[s]);
366 }
367
sewardjc0d8f682002-11-30 00:49:43 +0000368 pp_tt_tc_status ( msg );
nethercote92e7b7f2004-08-07 17:52:25 +0000369 overall_out_count += vg_tc_stats_count[s];
370 overall_out_osize += vg_tc_stats_osize[s];
371 overall_out_tsize += vg_tc_stats_tsize[s];
sewardj6c3769f2002-11-29 01:02:45 +0000372 vg_tc_used[s] = 0;
sewardjc0d8f682002-11-30 00:49:43 +0000373 vg_tc_stats_count[s] = 0;
374 vg_tc_stats_osize[s] = 0;
375 vg_tc_stats_tsize[s] = 0;
nethercote92e7b7f2004-08-07 17:52:25 +0000376 n_tc_discards++;
sewardj6c3769f2002-11-29 01:02:45 +0000377 }
378}
379
380
381/* Find an empty sector and bring it into use. If there isn't one,
382 try and allocate one. If that fails, return -1. */
383static
384Int maybe_commission_sector ( void )
385{
sewardjc0d8f682002-11-30 00:49:43 +0000386 Char msg[100];
387 Int s;
sewardj6c3769f2002-11-29 01:02:45 +0000388 for (s = 0; s < VG_TC_N_SECTORS; s++) {
389 if (vg_tc[s] != NULL && vg_tc_used[s] == 0) {
nethercote92e7b7f2004-08-07 17:52:25 +0000390 vg_tc_age[s] = overall_in_count;
sewardjc0d8f682002-11-30 00:49:43 +0000391 VG_(sprintf)(msg, "after commission of sector %d "
392 "at time %d",
393 s, vg_tc_age[s]);
394 pp_tt_tc_status ( msg );
sewardj6c3769f2002-11-29 01:02:45 +0000395# ifdef DEBUG_TRANSTAB
nethercote92e7b7f2004-08-07 17:52:25 +0000396 VG_(sanity_check_tt_tc)();
sewardj6c3769f2002-11-29 01:02:45 +0000397# endif
398 return s;
399 }
400 }
401 for (s = 0; s < VG_TC_N_SECTORS; s++) {
402 if (vg_tc[s] == NULL) {
fitzhardinge98abfc72003-12-16 02:05:15 +0000403#if 1
sewardj6c3769f2002-11-29 01:02:45 +0000404 vg_tc[s] = VG_(get_memory_from_mmap)
sewardjc0d8f682002-11-30 00:49:43 +0000405 ( vg_tc_sector_szB, "trans-cache(sector)" );
fitzhardinge98abfc72003-12-16 02:05:15 +0000406#else
nethercote794a5912004-07-12 09:01:24 +0000407 // Alternative: put translations in an mmap'd file. The main
408 // reason is to help OProfile -- OProfile can assign time spent in
409 // translations to a particular file. The file format doesn't
410 // really matter, which is good because it's not really readable,
411 // being generated code but not a proper ELF file.
fitzhardinge98abfc72003-12-16 02:05:15 +0000412 Char buf[20];
413 static Int count = 0;
414 Int fd;
415
416 VG_(sprintf)(buf, ".transtab.%d", count++);
417
418 fd = VG_(open)(buf, VKI_O_RDWR|VKI_O_CREAT|VKI_O_TRUNC, 0700);
419 //VG_(unlink)(buf);
420 VG_(do_syscall)(__NR_ftruncate, fd, PGROUNDUP(vg_tc_sector_szB));
nethercote43f583e2004-07-11 18:16:36 +0000421 vg_tc[s] = VG_(mmap)(0, PGROUNDUP(vg_tc_sector_szB), VKI_PROT_READ|VKI_PROT_WRITE|VKI_PROT_EXEC, VKI_MAP_SHARED, 0, fd, 0);
fitzhardinge98abfc72003-12-16 02:05:15 +0000422 VG_(close)(fd);
423#endif
sewardj6c3769f2002-11-29 01:02:45 +0000424 vg_tc_used[s] = 0;
nethercote794a5912004-07-12 09:01:24 +0000425 VG_(sprintf)(msg, "after allocation of sector %d (size %d)",
sewardjc0d8f682002-11-30 00:49:43 +0000426 s, vg_tc_sector_szB );
427 pp_tt_tc_status ( msg );
sewardj6c3769f2002-11-29 01:02:45 +0000428 return maybe_commission_sector();
429 }
430 }
431 return -1;
432}
433
sewardj6c3769f2002-11-29 01:02:45 +0000434
435static
436UChar* allocate ( Int nBytes )
437{
438 Int i;
439
sewardj22854b92002-11-30 14:00:47 +0000440 vg_assert(IS_ALIGNED(nBytes));
sewardj6c3769f2002-11-29 01:02:45 +0000441
442 /* Ensure the TT is still OK. */
443 while (vg_tt_used >= VG_TT_LIMIT) {
444 (void)discard_oldest_sector();
445 rebuild_TT();
446 vg_assert(vg_tt_used < VG_TT_LIMIT);
447 }
448
449 /* Can we get it into the current sector? */
450 if (vg_tc_current >= 0
451 && vg_tc_current < VG_TC_N_SECTORS
452 && vg_tc[vg_tc_current] != NULL
sewardjc0d8f682002-11-30 00:49:43 +0000453 && vg_tc_used[vg_tc_current] + nBytes <= vg_tc_sector_szB) {
sewardj6c3769f2002-11-29 01:02:45 +0000454 /* Yes. */
455 UChar* p = &(vg_tc[vg_tc_current][ vg_tc_used[vg_tc_current] ]);
456 vg_tc_used[vg_tc_current] += nBytes;
457 return p;
458 }
459
460 /* Perhaps we can bring a new sector into use, for the first
461 time. */
462 vg_tc_current = maybe_commission_sector();
463 if (vg_tc_current >= 0 && vg_tc_current < VG_TC_N_SECTORS)
464 return allocate(nBytes);
465
466 /* That didn't work. We'll have to dump the oldest. We take the
467 opportunity to dump the N oldest at once. */
468 for (i = 0; i < 1; i++)
469 (void)discard_oldest_sector();
470
471 rebuild_TT();
472 vg_tc_current = maybe_commission_sector();
473 vg_assert(vg_tc_current >= 0 && vg_tc_current < VG_TC_N_SECTORS);
474# ifdef DEBUG_TRANSTAB
nethercote92e7b7f2004-08-07 17:52:25 +0000475 VG_(sanity_check_tt_tc)();
sewardj6c3769f2002-11-29 01:02:45 +0000476# endif
477
478 return allocate(nBytes);
479}
sewardjde4a1d02002-03-22 01:27:54 +0000480
481
482/* Just so these counts can be queried without making them globally
483 visible. */
484void VG_(get_tt_tc_used) ( UInt* tt_used, UInt* tc_used )
485{
sewardj6c3769f2002-11-29 01:02:45 +0000486 Int s;
sewardjde4a1d02002-03-22 01:27:54 +0000487 *tt_used = vg_tt_used;
sewardj6c3769f2002-11-29 01:02:45 +0000488 *tc_used = 0;
489 for (s = 0; s < VG_TC_N_SECTORS; s++)
490 *tc_used += vg_tc_used[s];
sewardjde4a1d02002-03-22 01:27:54 +0000491}
492
493
494/* Do a sanity check on TT/TC.
495*/
nethercote92e7b7f2004-08-07 17:52:25 +0000496void VG_(sanity_check_tt_tc) ( void )
sewardjde4a1d02002-03-22 01:27:54 +0000497{
sewardjc0d8f682002-11-30 00:49:43 +0000498 Int i, s;
499 TTEntry* tte;
500 TCEntry* tce;
501 /* Checks:
502 - Each TT entry points to a valid and corresponding TC entry.
sewardj6c3769f2002-11-29 01:02:45 +0000503 */
504 for (i = 0; i < VG_TT_SIZE; i++) {
505 tte = &vg_tt[i];
506 /* empty slots are harmless. */
507 if (tte->orig_addr == VG_TTE_EMPTY) continue;
508 /* all others should agree with the TC entry. */
509 tce = tte->tcentry;
510 vg_assert(IS_ALIGNED4_ADDR(tce));
511 /* does this point into a valid TC sector? */
512 for (s = 0; s < VG_TC_N_SECTORS; s++)
513 if (vg_tc[s] != NULL
514 && ((Addr)tce) >= (Addr)&vg_tc[s][0]
515 && ((Addr)tce) < (Addr)&vg_tc[s][ vg_tc_used[s] ])
516 break;
517 vg_assert(s < VG_TC_N_SECTORS);
518 /* It should agree with the TC entry on the orig_addr. This may
519 be VG_TTE_DELETED, or a real orig addr. */
520 vg_assert(tte->orig_addr == tce->orig_addr);
521 }
sewardjde4a1d02002-03-22 01:27:54 +0000522}
523
nethercote92e7b7f2004-08-07 17:52:25 +0000524static __inline__ Int safe_idiv(Int a, Int b)
525{
526 return (b == 0 ? 0 : a / b);
527}
528
529void VG_(print_tt_tc_stats)(void)
530{
531 VG_(message)(Vg_DebugMsg,
532 " TT/TC: %d tc sectors discarded.",
533 n_tc_discards );
534 VG_(message)(Vg_DebugMsg,
535 " %d tt_fast misses.",
536 n_tt_fast_misses);
537 VG_(message)(Vg_DebugMsg,
538 "translate: new %d (%d -> %d; ratio %d:10)",
539 overall_in_count, overall_in_osize, overall_in_tsize,
540 safe_idiv(10*overall_in_tsize, overall_in_osize));
541 VG_(message)(Vg_DebugMsg,
542 " discard %d (%d -> %d; ratio %d:10).",
543 overall_out_count, overall_out_osize, overall_out_tsize,
544 safe_idiv(10*overall_out_tsize, overall_out_osize));
545}
546
547Int VG_(get_bbs_translated) ( void )
548{
549 return overall_in_count;
550}
sewardjde4a1d02002-03-22 01:27:54 +0000551
552/* Add this already-filled-in entry to the TT. Assumes that the
553 relevant code chunk has been placed in TC, along with a dummy back
sewardjc0d8f682002-11-30 00:49:43 +0000554 pointer, which is inserted here.
sewardjde4a1d02002-03-22 01:27:54 +0000555*/
sewardjc0d8f682002-11-30 00:49:43 +0000556void VG_(add_to_trans_tab) ( Addr orig_addr, Int orig_size,
sewardj22854b92002-11-30 14:00:47 +0000557 Addr trans_addr, Int trans_size,
558 UShort jumps[VG_MAX_JUMPS])
sewardjde4a1d02002-03-22 01:27:54 +0000559{
sewardj6c3769f2002-11-29 01:02:45 +0000560 Int i, nBytes, trans_size_aligned;
561 TCEntry* tce;
sewardjde4a1d02002-03-22 01:27:54 +0000562 /*
563 VG_(printf)("add_to_trans_tab(%d) %x %d %x %d\n",
564 vg_tt_used, tte->orig_addr, tte->orig_size,
565 tte->trans_addr, tte->trans_size);
566 */
sewardj6c3769f2002-11-29 01:02:45 +0000567
sewardj22854b92002-11-30 14:00:47 +0000568 vg_assert(offsetof(TCEntry, payload) == VG_CODE_OFFSET);
569
sewardj6c3769f2002-11-29 01:02:45 +0000570 /* figure out how many bytes we require. */
sewardj22854b92002-11-30 14:00:47 +0000571 nBytes = CODE_ALIGN(trans_size + sizeof(TCEntry));
572 trans_size_aligned = nBytes-sizeof(TCEntry);
573 vg_assert(IS_ALIGNED(nBytes));
sewardj6c3769f2002-11-29 01:02:45 +0000574
575 tce = (TCEntry*)allocate(nBytes);
sewardj78210aa2002-12-01 02:55:46 +0000576 /*
577 VG_(printf)("allocate returned %p (code start %p)\n",
578 tce, &tce->payload[0]);
579 */
sewardjc0d8f682002-11-30 00:49:43 +0000580 vg_assert(vg_tc_current >= 0 && vg_tc_current < VG_TC_N_SECTORS);
581
sewardj6c3769f2002-11-29 01:02:45 +0000582 tce->orig_addr = orig_addr;
583 tce->orig_size = (UShort)orig_size; /* what's the point of storing this? */
584 tce->trans_size = (UShort)trans_size_aligned;
sewardj22854b92002-11-30 14:00:47 +0000585 for (i = 0; i < VG_MAX_JUMPS; i++) {
586 tce->jump_sites[i] = jumps[i];
587 }
sewardj6c3769f2002-11-29 01:02:45 +0000588 for (i = 0; i < trans_size; i++) {
589 tce->payload[i] = ((UChar*)trans_addr)[i];
sewardjde4a1d02002-03-22 01:27:54 +0000590 }
sewardj22854b92002-11-30 14:00:47 +0000591
592 unchain_tce(tce);
sewardj6c3769f2002-11-29 01:02:45 +0000593 add_tt_entry(tce);
sewardjc0d8f682002-11-30 00:49:43 +0000594
595 /* Update stats. */
nethercote92e7b7f2004-08-07 17:52:25 +0000596 overall_in_count ++;
597 overall_in_osize += orig_size;
598 overall_in_tsize += trans_size;
sewardjc0d8f682002-11-30 00:49:43 +0000599
600 vg_tc_stats_count[vg_tc_current] ++;
601 vg_tc_stats_osize[vg_tc_current] += orig_size;
602 vg_tc_stats_tsize[vg_tc_current] += trans_size;
sewardjde4a1d02002-03-22 01:27:54 +0000603}
604
605
606/* Find the translation address for a given (original) code address.
607 If found, update VG_(tt_fast) so subsequent lookups are fast. If
608 no translation can be found, return zero. This routine is (the
609 only one) called from vg_run_innerloop. */
610Addr VG_(search_transtab) ( Addr original_addr )
611{
612 TTEntry* tte;
613 VGP_PUSHCC(VgpSlowFindT);
sewardj6c3769f2002-11-29 01:02:45 +0000614 tte = search_tt ( original_addr );
sewardjde4a1d02002-03-22 01:27:54 +0000615 if (tte == NULL) {
616 /* We didn't find it. vg_run_innerloop will have to request a
617 translation. */
njn25e49d8e72002-09-23 09:36:25 +0000618 VGP_POPCC(VgpSlowFindT);
sewardjde4a1d02002-03-22 01:27:54 +0000619 return (Addr)0;
620 } else {
sewardj1f086892002-12-15 01:47:05 +0000621 /* Found it. Put the search result into the fast cache now. */
sewardjde4a1d02002-03-22 01:27:54 +0000622 UInt cno = (UInt)original_addr & VG_TT_FAST_MASK;
sewardj6c3769f2002-11-29 01:02:45 +0000623 VG_(tt_fast)[cno] = (Addr)(tte->tcentry);
nethercote92e7b7f2004-08-07 17:52:25 +0000624 n_tt_fast_misses++;
njn25e49d8e72002-09-23 09:36:25 +0000625 VGP_POPCC(VgpSlowFindT);
sewardj6c3769f2002-11-29 01:02:45 +0000626 return (Addr)&(tte->tcentry->payload[0]);
sewardjde4a1d02002-03-22 01:27:54 +0000627 }
628}
629
630
sewardj18d75132002-05-16 11:06:21 +0000631/* Invalidate translations of original code [start .. start + range - 1].
sewardj97ad5522003-05-04 12:32:56 +0000632 This is slow, so you *really* don't want to call it very often.
633 Set 'unchain_blocks' if the translation being invalidated may be chained
634 to by other local blocks (which are NOT being discarded).
sewardjde4a1d02002-03-22 01:27:54 +0000635*/
sewardj97ad5522003-05-04 12:32:56 +0000636void VG_(invalidate_translations) ( Addr start, UInt range, Bool unchain_blocks )
sewardjde4a1d02002-03-22 01:27:54 +0000637{
sewardj6c3769f2002-11-29 01:02:45 +0000638 Addr i_start, i_end, o_start, o_end;
639 UInt out_count, out_osize, out_tsize;
sewardj97ad5522003-05-04 12:32:56 +0000640 Int i, j;
sewardj6c3769f2002-11-29 01:02:45 +0000641 TCEntry* tce;
sewardj18d75132002-05-16 11:06:21 +0000642# ifdef DEBUG_TRANSTAB
nethercote92e7b7f2004-08-07 17:52:25 +0000643 VG_(sanity_check_tt_tc)();
sewardjde4a1d02002-03-22 01:27:54 +0000644# endif
sewardj18d75132002-05-16 11:06:21 +0000645 i_start = start;
646 i_end = start + range - 1;
647 out_count = out_osize = out_tsize = 0;
sewardjde4a1d02002-03-22 01:27:54 +0000648
sewardj18d75132002-05-16 11:06:21 +0000649 for (i = 0; i < VG_TT_SIZE; i++) {
650 if (vg_tt[i].orig_addr == VG_TTE_EMPTY
651 || vg_tt[i].orig_addr == VG_TTE_DELETED) continue;
sewardj6c3769f2002-11-29 01:02:45 +0000652 tce = vg_tt[i].tcentry;
653 o_start = tce->orig_addr;
654 o_end = o_start + tce->trans_size - 1;
sewardj18d75132002-05-16 11:06:21 +0000655 if (o_end < i_start || o_start > i_end)
656 continue;
njn25e49d8e72002-09-23 09:36:25 +0000657
658 if (VG_(needs).basic_block_discards)
sewardj6c3769f2002-11-29 01:02:45 +0000659 SK_(discard_basic_block_info)( tce->orig_addr,
660 tce->orig_size );
njn25e49d8e72002-09-23 09:36:25 +0000661
sewardj18d75132002-05-16 11:06:21 +0000662 vg_tt[i].orig_addr = VG_TTE_DELETED;
sewardj6c3769f2002-11-29 01:02:45 +0000663 tce->orig_addr = VG_TTE_DELETED;
sewardj97ad5522003-05-04 12:32:56 +0000664
665 if (unchain_blocks) {
666 /* make sure no other blocks chain to the one we just discarded */
667 for(j = 0; j < VG_TC_N_SECTORS; j++) {
668 if (vg_tc[j] != NULL)
sewardj05bcdcb2003-05-18 10:05:38 +0000669 unchain_sector(j, (Addr)tce->payload, tce->trans_size);
sewardj97ad5522003-05-04 12:32:56 +0000670 }
671 }
672
nethercote92e7b7f2004-08-07 17:52:25 +0000673 overall_out_count ++;
674 overall_out_osize += tce->orig_size;
675 overall_out_tsize += tce->trans_size;
sewardj18d75132002-05-16 11:06:21 +0000676 out_count ++;
sewardj6c3769f2002-11-29 01:02:45 +0000677 out_osize += tce->orig_size;
678 out_tsize += tce->trans_size;
sewardjde4a1d02002-03-22 01:27:54 +0000679 }
sewardjde4a1d02002-03-22 01:27:54 +0000680
sewardj18d75132002-05-16 11:06:21 +0000681 if (out_count > 0) {
sewardj6c3769f2002-11-29 01:02:45 +0000682 vg_invalidate_tt_fast();
nethercote92e7b7f2004-08-07 17:52:25 +0000683 VG_(sanity_check_tt_tc)();
sewardj18d75132002-05-16 11:06:21 +0000684# ifdef DEBUG_TRANSTAB
685 { Addr aa;
686 for (aa = i_start; aa <= i_end; aa++)
sewardj6c3769f2002-11-29 01:02:45 +0000687 vg_assert(search_tt ( aa ) == NULL);
sewardjde4a1d02002-03-22 01:27:54 +0000688 }
sewardj18d75132002-05-16 11:06:21 +0000689# endif
sewardjde4a1d02002-03-22 01:27:54 +0000690 }
691
sewardjc0d8f682002-11-30 00:49:43 +0000692 if (VG_(clo_verbosity) > 2)
sewardj18d75132002-05-16 11:06:21 +0000693 VG_(message)(Vg_UserMsg,
694 "discard %d (%d -> %d) translations in range %p .. %p",
695 out_count, out_osize, out_tsize, i_start, i_end );
sewardjde4a1d02002-03-22 01:27:54 +0000696}
697
698
699/*------------------------------------------------------------*/
700/*--- Initialisation. ---*/
701/*------------------------------------------------------------*/
702
sewardjc0d8f682002-11-30 00:49:43 +0000703void VG_(init_tt_tc) ( void )
704{
705 Int s;
706
sewardj22854b92002-11-30 14:00:47 +0000707 /* Otherwise we wind up with non-32-bit-aligned code in
708 TCEntries. */
709 vg_assert((VG_MAX_JUMPS % 2) == 0);
710
nethercote463c63c2004-10-26 11:18:32 +0000711 // Otherwise lots of things go wrong...
712 vg_assert(VG_CODE_OFFSET == sizeof(TCEntry));
713
sewardjc0d8f682002-11-30 00:49:43 +0000714 /* Figure out how big each sector should be. */
715 vg_tc_sector_szB
716 = (VG_TT_LIMIT /* max TT entries we expect */
sewardj78210aa2002-12-01 02:55:46 +0000717 * (VG_(details).avg_translation_sizeB
718 + sizeof(TCEntry)
719 + (CODE_ALIGNMENT/2) /* avg alignment loss */)
720 )
sewardjc0d8f682002-11-30 00:49:43 +0000721 / VG_TC_N_SECTORS;
722 /* Ensure the calculated value is not way crazy. */
723 vg_assert(vg_tc_sector_szB >= 200000);
jseward3f409e82003-12-14 14:25:19 +0000724 vg_assert(vg_tc_sector_szB <= 8000000);
sewardjc0d8f682002-11-30 00:49:43 +0000725
726 for (s = 0; s < VG_TC_N_SECTORS; s++) {
727 vg_tc[s] = NULL;
728 vg_tc_used[s] = 0;
729 vg_tc_age[s] = 0;
730 vg_tc_stats_count[s] = 0;
731 vg_tc_stats_osize[s] = 0;
732 vg_tc_stats_tsize[s] = 0;
733 }
734 vg_tc_current = 0;
735
736 vg_tt = VG_(get_memory_from_mmap) ( VG_TT_SIZE * sizeof(TTEntry),
737 "trans-table" );
738 /* The main translation table is empty. */
739 initialise_tt();
740
741 if (VG_(clo_verbosity) > 2) {
742 VG_(message)(Vg_DebugMsg,
743 "Translation Cache: using %d sectors of %d bytes each",
744 VG_TC_N_SECTORS, vg_tc_sector_szB );
745 VG_(message)(Vg_DebugMsg,
746 "Translation Table: %d total entries, max occupancy %d (%d%%)",
747 VG_TT_SIZE, VG_TT_LIMIT, VG_TT_LIMIT_PERCENT );
748 }
749
750# ifdef DEBUG_TRANSTAB
nethercote92e7b7f2004-08-07 17:52:25 +0000751 VG_(sanity_check_tt_tc)();
sewardjc0d8f682002-11-30 00:49:43 +0000752# endif
753}
754
sewardjde4a1d02002-03-22 01:27:54 +0000755/*--------------------------------------------------------------------*/
756/*--- end vg_transtab.c ---*/
757/*--------------------------------------------------------------------*/