sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 1 | |
| 2 | /*--------------------------------------------------------------------*/ |
| 3 | /*--- Management of the translation table and cache. ---*/ |
| 4 | /*--- vg_transtab.c ---*/ |
| 5 | /*--------------------------------------------------------------------*/ |
| 6 | |
| 7 | /* |
| 8 | This file is part of Valgrind, an x86 protected-mode emulator |
| 9 | designed for debugging and profiling binaries on x86-Unixes. |
| 10 | |
| 11 | Copyright (C) 2000-2002 Julian Seward |
| 12 | jseward@acm.org |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 13 | |
| 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 | |
| 29 | The GNU General Public License is contained in the file LICENSE. |
| 30 | */ |
| 31 | |
| 32 | #include "vg_include.h" |
| 33 | #include "vg_constants.h" |
| 34 | |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 35 | /* #define DEBUG_TRANSTAB */ |
| 36 | |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 37 | |
| 38 | /*------------------------------------------------------------*/ |
| 39 | /*--- Management of the LRU-based translation table+cache. ---*/ |
| 40 | /*------------------------------------------------------------*/ |
| 41 | |
| 42 | /* These sizes were set up so as to be able to debug large KDE 3 |
| 43 | applications (are there any small ones?) without excessive amounts |
| 44 | of code retranslation. */ |
| 45 | |
| 46 | /* Size of the translation cache, in bytes. */ |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 47 | #define VG_TC_SIZE /*1000000*/ /*16000000*/ 32000000 /*40000000*/ |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 48 | |
| 49 | /* Do a LRU pass when the translation cache becomes this full. */ |
sewardj | ec05564 | 2002-05-03 18:59:21 +0000 | [diff] [blame] | 50 | #define VG_TC_LIMIT_PERCENT 98 |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 51 | |
| 52 | /* When doing an LRU pass, reduce TC fullness to this level. */ |
| 53 | #define VG_TC_TARGET_PERCENT 85 |
| 54 | |
| 55 | /* Number of entries in the translation table. This must be a prime |
| 56 | number in order to make the hashing work properly. */ |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 57 | #define VG_TT_SIZE /*5281*/ /*100129*/ 200191 /*250829*/ |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 58 | |
| 59 | /* Do an LRU pass when the translation table becomes this full. */ |
| 60 | #define VG_TT_LIMIT_PERCENT /*67*/ 80 |
| 61 | |
| 62 | /* When doing an LRU pass, reduce TT fullness to this level. */ |
| 63 | #define VG_TT_TARGET_PERCENT /*60*/ 70 |
| 64 | |
| 65 | /* The number of age steps we track. 0 means the current epoch, |
| 66 | N_EPOCHS-1 means used the epoch N_EPOCHS-1 or more ago. */ |
sewardj | ac68028 | 2002-03-25 02:02:52 +0000 | [diff] [blame] | 67 | #define VG_N_EPOCHS /*2000*/ /*4000*/ 20000 |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 68 | |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 69 | /* This TT entry is empty. There is no associated TC storage. */ |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 70 | #define VG_TTE_EMPTY ((Addr)1) |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 71 | /* This TT entry has been deleted, in the sense that it does not |
| 72 | contribute to the orig->trans mapping. However, the ex-translation |
| 73 | it points at still occupies space in TC. This slot cannot be |
| 74 | re-used without doing an LRU pass. */ |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 75 | #define VG_TTE_DELETED ((Addr)3) |
| 76 | |
| 77 | /* The TC. This used to be statically allocated, but that forces many |
| 78 | SecMap arrays to be pointlessly allocated at startup, bloating the |
| 79 | process size by about 22M and making startup slow. So now we |
| 80 | dynamically allocate it at startup time. |
| 81 | was: static UChar vg_tc[VG_TC_SIZE]; |
| 82 | */ |
| 83 | static UChar* vg_tc = NULL; |
| 84 | |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 85 | /* Count of bytes used in the TC. This includes those pointed to from |
| 86 | VG_TTE_DELETED entries. */ |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 87 | static Int vg_tc_used = 0; |
| 88 | |
| 89 | /* The TT. Like TC, for the same reason, is dynamically allocated at |
| 90 | startup. |
| 91 | was: static TTEntry vg_tt[VG_TT_SIZE]; |
| 92 | */ |
| 93 | static TTEntry* vg_tt = NULL; |
| 94 | |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 95 | /* Count of non-empty TT entries. This includes deleted ones. */ |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 96 | static Int vg_tt_used = 0; |
| 97 | |
| 98 | /* Fast helper for the TT. A direct-mapped cache which holds a |
| 99 | pointer to a TT entry which may or may not be the correct one, but |
| 100 | which we hope usually is. This array is referred to directly from |
| 101 | vg_dispatch.S. */ |
| 102 | Addr VG_(tt_fast)[VG_TT_FAST_SIZE]; |
| 103 | |
| 104 | /* For reading/writing the misaligned TT-index word at immediately |
| 105 | preceding every translation in TC. */ |
sewardj | 8aef119 | 2002-07-24 09:36:36 +0000 | [diff] [blame] | 106 | #if 0 |
| 107 | /* Big sigh. However reasonable this seems, there are those who |
| 108 | set AC in %EFLAGS (Alignment Check) to 1, causing bus errors. A |
| 109 | proper solution is for valgrind to properly virtualise AC, like |
| 110 | the other flags (DOSZACP). The current cheap hack simply avoids |
| 111 | all misaligned accesses, so valgrind doesn't fault even if AC is |
| 112 | set. */ |
| 113 | # define VG_READ_MISALIGNED_WORD(aaa) (*((UInt*)(aaa))) |
| 114 | # define VG_WRITE_MISALIGNED_WORD(aaa,vvv) *((UInt*)(aaa)) = ((UInt)(vvv)) |
| 115 | #else |
| 116 | static __inline__ |
| 117 | UInt VG_READ_MISALIGNED_WORD ( Addr aaa ) |
| 118 | { |
| 119 | UInt w = 0; |
| 120 | UChar* p = (UChar*)aaa; |
| 121 | w = 0xFF & ((UInt)(p[3])); |
| 122 | w = (w << 8) | (0xFF & ((UInt)(p[2]))); |
| 123 | w = (w << 8) | (0xFF & ((UInt)(p[1]))); |
| 124 | w = (w << 8) | (0xFF & ((UInt)(p[0]))); |
| 125 | return w; |
| 126 | } |
| 127 | |
| 128 | static __inline__ |
| 129 | void VG_WRITE_MISALIGNED_WORD ( Addr aaa, UInt vvv ) |
| 130 | { |
| 131 | UChar* p = (UChar*)aaa; |
| 132 | p[0] = vvv & 0xFF; |
| 133 | p[1] = (vvv >> 8) & 0xFF; |
| 134 | p[2] = (vvv >> 16) & 0xFF; |
| 135 | p[3] = (vvv >> 24) & 0xFF; |
| 136 | } |
| 137 | #endif |
| 138 | |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 139 | |
| 140 | /* Used for figuring out an age threshold for translations. */ |
| 141 | static Int vg_bytes_in_epoch[VG_N_EPOCHS]; |
| 142 | static Int vg_entries_in_epoch[VG_N_EPOCHS]; |
| 143 | |
| 144 | |
| 145 | /* Just so these counts can be queried without making them globally |
| 146 | visible. */ |
| 147 | void VG_(get_tt_tc_used) ( UInt* tt_used, UInt* tc_used ) |
| 148 | { |
| 149 | *tt_used = vg_tt_used; |
| 150 | *tc_used = vg_tc_used; |
| 151 | } |
| 152 | |
| 153 | |
| 154 | /* Do the LRU thing on TT/TC, clearing them back to the target limits |
| 155 | if they are over the threshold limits. |
| 156 | */ |
| 157 | void VG_(maybe_do_lru_pass) ( void ) |
| 158 | { |
| 159 | Int i, j, r, w, thresh, ttno; |
| 160 | TTEntry* tte; |
| 161 | |
sewardj | 215e7f0 | 2002-05-02 03:47:01 +0000 | [diff] [blame] | 162 | const Int tc_limit = (Int)(((double)VG_TC_SIZE * (double)VG_TC_LIMIT_PERCENT) |
| 163 | / (double)100.0); |
| 164 | const Int tt_limit = (Int)(((double)VG_TT_SIZE * (double)VG_TT_LIMIT_PERCENT) |
| 165 | / (double)100.0); |
| 166 | const Int tc_target = (Int)(((double)VG_TC_SIZE * (double)VG_TC_TARGET_PERCENT) |
| 167 | / (double)100.0); |
| 168 | const Int tt_target = (Int)(((double)VG_TT_SIZE * (double)VG_TT_TARGET_PERCENT) |
| 169 | / (double)100.0); |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 170 | |
| 171 | /* Decide quickly if we need to do an LRU pass ? */ |
| 172 | if (vg_tc_used <= tc_limit && vg_tt_used <= tt_limit) |
| 173 | return; |
| 174 | |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 175 | # ifdef DEBUG_TRANSTAB |
| 176 | VG_(sanity_check_tc_tt)(); |
| 177 | # endif |
| 178 | |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 179 | VGP_PUSHCC(VgpDoLRU); |
| 180 | /* |
| 181 | VG_(printf)( |
| 182 | "limits: tc_limit %d, tt_limit %d, tc_target %d, tt_target %d\n", |
| 183 | tc_limit, tt_limit, tc_target, tt_target); |
| 184 | */ |
| 185 | |
| 186 | if (VG_(clo_verbosity) > 2) |
| 187 | VG_(printf)(" pre-LRU: tc %d (target %d), tt %d (target %d)\n", |
| 188 | vg_tc_used, tc_target, vg_tt_used, tt_target); |
| 189 | |
| 190 | /* Yes we do. Figure out what threshold age is required in order to |
| 191 | shrink both the TC and TT occupancy below TC_TARGET_PERCENT and |
| 192 | TT_TARGET_PERCENT respectively. */ |
| 193 | |
| 194 | VG_(number_of_lrus)++; |
| 195 | |
| 196 | /* Count the number of TC bytes and TT entries in each epoch. */ |
| 197 | for (i = 0; i < VG_N_EPOCHS; i++) |
| 198 | vg_bytes_in_epoch[i] = vg_entries_in_epoch[i] = 0; |
| 199 | |
| 200 | for (i = 0; i < VG_TT_SIZE; i++) { |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 201 | if (vg_tt[i].orig_addr == VG_TTE_EMPTY |
| 202 | || vg_tt[i].orig_addr == VG_TTE_DELETED) |
| 203 | continue; |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 204 | j = vg_tt[i].mru_epoch; |
| 205 | vg_assert(j <= VG_(current_epoch)); |
| 206 | j = VG_(current_epoch) - j; |
| 207 | if (j >= VG_N_EPOCHS) j = VG_N_EPOCHS-1; |
| 208 | vg_assert(0 <= j && j < VG_N_EPOCHS); |
| 209 | /* Greater j now means older. */ |
| 210 | vg_entries_in_epoch[j]++; |
| 211 | vg_bytes_in_epoch[j] += 4+vg_tt[i].trans_size; |
| 212 | } |
| 213 | |
| 214 | /* |
| 215 | for (i = 0; i < VG_N_EPOCHS; i++) |
| 216 | VG_(printf)("epoch %d: ents %d, bytes %d\n", |
| 217 | i, vg_entries_in_epoch[i], vg_bytes_in_epoch[i]); |
| 218 | */ |
| 219 | |
| 220 | /* Cumulatise. Make vg_{bytes,entries}_in_epoch[n] contain the |
| 221 | counts for itself and all younger epochs. */ |
| 222 | for (i = 1; i < VG_N_EPOCHS; i++) { |
| 223 | vg_entries_in_epoch[i] += vg_entries_in_epoch[i-1]; |
| 224 | vg_bytes_in_epoch[i] += vg_bytes_in_epoch[i-1]; |
| 225 | } |
| 226 | |
| 227 | for (thresh = 0; thresh < VG_N_EPOCHS; thresh++) { |
| 228 | if (vg_entries_in_epoch[thresh] > tt_target |
| 229 | || vg_bytes_in_epoch[thresh] >= tc_target) |
| 230 | break; |
| 231 | } |
| 232 | |
| 233 | if (VG_(clo_verbosity) > 2) |
| 234 | VG_(printf)( |
| 235 | " LRU: discard translations %d or more epochs since last use\n", |
| 236 | thresh |
| 237 | ); |
| 238 | |
| 239 | thresh = VG_(current_epoch) - thresh; |
| 240 | |
| 241 | /* Ok, so we will hit our targets if we retain all entries most |
| 242 | recently used at most thresh epochs ago. Traverse the TT and |
| 243 | mark such entries as deleted. */ |
| 244 | for (i = 0; i < VG_TT_SIZE; i++) { |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 245 | if (vg_tt[i].orig_addr == VG_TTE_EMPTY |
| 246 | || vg_tt[i].orig_addr == VG_TTE_DELETED) |
| 247 | continue; |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 248 | if (vg_tt[i].mru_epoch <= thresh) { |
| 249 | vg_tt[i].orig_addr = VG_TTE_DELETED; |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 250 | VG_(this_epoch_out_count) ++; |
| 251 | VG_(this_epoch_out_osize) += vg_tt[i].orig_size; |
| 252 | VG_(this_epoch_out_tsize) += vg_tt[i].trans_size; |
| 253 | VG_(overall_out_count) ++; |
| 254 | VG_(overall_out_osize) += vg_tt[i].orig_size; |
| 255 | VG_(overall_out_tsize) += vg_tt[i].trans_size; |
| 256 | } |
| 257 | } |
| 258 | |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 259 | /* Now compact the TC, sliding live entries downwards to fill spaces |
| 260 | left by deleted entries. In this loop, r is the offset in TC of |
| 261 | the current translation under consideration, and w is the next |
| 262 | allocation point. */ |
| 263 | r = w = 0; |
| 264 | while (True) { |
| 265 | if (r >= vg_tc_used) break; |
| 266 | /* The first four bytes of every translation contain the index |
| 267 | of its TT entry. The TT entry's .trans_addr field points at |
| 268 | the start of the code proper, not at this 4-byte index, so |
| 269 | that we don't constantly have to keep adding 4 in the main |
| 270 | lookup/dispatch loop. */ |
sewardj | 8aef119 | 2002-07-24 09:36:36 +0000 | [diff] [blame] | 271 | |
| 272 | ttno = VG_READ_MISALIGNED_WORD((Addr)(&vg_tc[r])); |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 273 | vg_assert(ttno >= 0 && ttno < VG_TT_SIZE); |
| 274 | tte = & vg_tt[ ttno ]; |
| 275 | vg_assert(tte->orig_addr != VG_TTE_EMPTY); |
| 276 | if (tte->orig_addr != VG_TTE_DELETED) { |
| 277 | /* We want to keep this one alive. */ |
| 278 | /* Sanity check the pointer back to TC. */ |
| 279 | vg_assert(tte->trans_addr == (Addr)&vg_tc[r+4]); |
| 280 | for (i = 0; i < 4+tte->trans_size; i++) |
| 281 | vg_tc[w+i] = vg_tc[r+i]; |
| 282 | tte->trans_addr = (Addr)&vg_tc[w+4]; |
| 283 | w += 4+tte->trans_size; |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 284 | } else { |
| 285 | tte->orig_addr = VG_TTE_EMPTY; |
| 286 | vg_tt_used--; |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 287 | } |
| 288 | r += 4+tte->trans_size; |
| 289 | } |
| 290 | /* should have traversed an exact number of translations, with no |
| 291 | slop at the end. */ |
| 292 | vg_assert(w <= r); |
| 293 | vg_assert(r == vg_tc_used); |
| 294 | vg_assert(w <= r); |
| 295 | vg_assert(w <= tc_target); |
| 296 | vg_tc_used = w; |
| 297 | |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 298 | vg_assert(vg_tt_used >= 0); |
| 299 | vg_assert(vg_tt_used <= tt_target); |
| 300 | |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 301 | /* Invalidate the fast cache, since it is now out of date. It will get |
| 302 | reconstructed incrementally when the client resumes. */ |
| 303 | VG_(invalidate_tt_fast)(); |
| 304 | |
| 305 | if (VG_(clo_verbosity) > 2) |
| 306 | VG_(printf)("post-LRU: tc %d (target %d), tt %d (target %d)\n", |
| 307 | vg_tc_used, tc_target, vg_tt_used, tt_target); |
| 308 | |
| 309 | if (VG_(clo_verbosity) > 1) |
| 310 | VG_(message)(Vg_UserMsg, |
| 311 | "epoch %d (bb %luk): thresh %d, " |
| 312 | "out %d (%dk -> %dk), new TT %d, TC %dk", |
| 313 | VG_(current_epoch), |
| 314 | VG_(bbs_done) / 1000, |
| 315 | VG_(current_epoch) - thresh, |
| 316 | VG_(this_epoch_out_count), |
| 317 | VG_(this_epoch_out_osize) / 1000, |
| 318 | VG_(this_epoch_out_tsize) / 1000, |
| 319 | vg_tt_used, vg_tc_used / 1000 |
| 320 | ); |
| 321 | |
| 322 | /* Reconstruct the SMC detection structures. */ |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 323 | # ifdef DEBUG_TRANSTAB |
| 324 | for (i = 0; i < VG_TT_SIZE; i++) |
| 325 | vg_assert(vg_tt[i].orig_addr != VG_TTE_DELETED); |
| 326 | # endif |
| 327 | VG_(sanity_check_tc_tt)(); |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 328 | |
| 329 | VGP_POPCC; |
| 330 | } |
| 331 | |
| 332 | |
| 333 | /* Do a sanity check on TT/TC. |
| 334 | */ |
| 335 | void VG_(sanity_check_tc_tt) ( void ) |
| 336 | { |
| 337 | Int i, counted_entries, counted_bytes; |
| 338 | TTEntry* tte; |
| 339 | counted_entries = 0; |
| 340 | counted_bytes = 0; |
| 341 | for (i = 0; i < VG_TT_SIZE; i++) { |
| 342 | tte = &vg_tt[i]; |
| 343 | if (tte->orig_addr == VG_TTE_EMPTY) continue; |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 344 | vg_assert(tte->mru_epoch >= 0); |
| 345 | vg_assert(tte->mru_epoch <= VG_(current_epoch)); |
| 346 | counted_entries++; |
| 347 | counted_bytes += 4+tte->trans_size; |
| 348 | vg_assert(tte->trans_addr >= (Addr)&vg_tc[4]); |
| 349 | vg_assert(tte->trans_addr < (Addr)&vg_tc[vg_tc_used]); |
| 350 | vg_assert(VG_READ_MISALIGNED_WORD(tte->trans_addr-4) == i); |
| 351 | } |
| 352 | vg_assert(counted_entries == vg_tt_used); |
| 353 | vg_assert(counted_bytes == vg_tc_used); |
| 354 | } |
| 355 | |
| 356 | |
| 357 | /* Add this already-filled-in entry to the TT. Assumes that the |
| 358 | relevant code chunk has been placed in TC, along with a dummy back |
| 359 | pointer, which is inserted here. |
| 360 | */ |
| 361 | extern void VG_(add_to_trans_tab) ( TTEntry* tte ) |
| 362 | { |
| 363 | Int i; |
| 364 | /* |
| 365 | VG_(printf)("add_to_trans_tab(%d) %x %d %x %d\n", |
| 366 | vg_tt_used, tte->orig_addr, tte->orig_size, |
| 367 | tte->trans_addr, tte->trans_size); |
| 368 | */ |
| 369 | vg_assert(tte->orig_addr != VG_TTE_DELETED |
| 370 | && tte->orig_addr != VG_TTE_EMPTY); |
| 371 | /* Hash to get initial probe point. */ |
| 372 | i = ((UInt)(tte->orig_addr)) % VG_TT_SIZE; |
| 373 | while (True) { |
| 374 | if (vg_tt[i].orig_addr == tte->orig_addr) |
| 375 | VG_(panic)("add_to_trans_tab: duplicate"); |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 376 | if (vg_tt[i].orig_addr == VG_TTE_EMPTY) { |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 377 | /* Put it here, and set the back pointer. */ |
| 378 | vg_tt[i] = *tte; |
| 379 | VG_WRITE_MISALIGNED_WORD(tte->trans_addr-4, i); |
| 380 | vg_tt_used++; |
| 381 | return; |
| 382 | } |
| 383 | i++; |
| 384 | if (i == VG_TT_SIZE) i = 0; |
| 385 | } |
| 386 | } |
| 387 | |
| 388 | |
| 389 | /* Copy a new translation's code into TC, leaving a 4-byte hole for |
| 390 | the back pointer, and returning a pointer to the code proper (not |
| 391 | the hole) in TC. |
| 392 | */ |
| 393 | Addr VG_(copy_to_transcache) ( Addr trans_addr, Int trans_size ) |
| 394 | { |
| 395 | Int i; |
| 396 | Addr ret_addr; |
| 397 | if (4+trans_size > VG_TC_SIZE-vg_tc_used) |
| 398 | VG_(panic)("copy_to_transcache: not enough free space?!"); |
| 399 | /* Leave a hole for the back pointer to the TT entry. */ |
| 400 | vg_tc_used += 4; |
| 401 | ret_addr = (Addr)&vg_tc[vg_tc_used]; |
| 402 | for (i = 0; i < trans_size; i++) |
| 403 | vg_tc[vg_tc_used+i] = ((UChar*)trans_addr)[i]; |
| 404 | vg_tc_used += trans_size; |
| 405 | return ret_addr; |
| 406 | } |
| 407 | |
| 408 | |
| 409 | /* Invalidate the tt_fast cache, for whatever reason. Tricky. We |
| 410 | have to find a TTE_EMPTY slot to point all entries at. */ |
| 411 | void VG_(invalidate_tt_fast)( void ) |
| 412 | { |
| 413 | Int i, j; |
| 414 | for (i = 0; i < VG_TT_SIZE && vg_tt[i].orig_addr != VG_TTE_EMPTY; i++) |
| 415 | ; |
| 416 | vg_assert(i < VG_TT_SIZE |
| 417 | && vg_tt[i].orig_addr == VG_TTE_EMPTY); |
| 418 | for (j = 0; j < VG_TT_FAST_SIZE; j++) |
| 419 | VG_(tt_fast)[j] = (Addr)&vg_tt[i]; |
| 420 | } |
| 421 | |
| 422 | |
| 423 | /* Search TT to find the translated address of the supplied original, |
| 424 | or NULL if not found. This routine is used when we miss in |
| 425 | VG_(tt_fast). |
| 426 | */ |
| 427 | static __inline__ TTEntry* search_trans_table ( Addr orig_addr ) |
| 428 | { |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 429 | //static Int queries = 0; |
| 430 | //static Int probes = 0; |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 431 | Int i; |
| 432 | /* Hash to get initial probe point. */ |
| 433 | // if (queries == 10000) { |
| 434 | // VG_(printf)("%d queries, %d probes\n", queries, probes); |
| 435 | // queries = probes = 0; |
| 436 | //} |
| 437 | //queries++; |
| 438 | i = ((UInt)orig_addr) % VG_TT_SIZE; |
| 439 | while (True) { |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 440 | //probes++; |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 441 | if (vg_tt[i].orig_addr == orig_addr) |
| 442 | return &vg_tt[i]; |
| 443 | if (vg_tt[i].orig_addr == VG_TTE_EMPTY) |
| 444 | return NULL; |
| 445 | i++; |
| 446 | if (i == VG_TT_SIZE) i = 0; |
| 447 | } |
| 448 | } |
| 449 | |
| 450 | |
| 451 | /* Find the translation address for a given (original) code address. |
| 452 | If found, update VG_(tt_fast) so subsequent lookups are fast. If |
| 453 | no translation can be found, return zero. This routine is (the |
| 454 | only one) called from vg_run_innerloop. */ |
| 455 | Addr VG_(search_transtab) ( Addr original_addr ) |
| 456 | { |
| 457 | TTEntry* tte; |
| 458 | VGP_PUSHCC(VgpSlowFindT); |
| 459 | tte = search_trans_table ( original_addr ); |
| 460 | if (tte == NULL) { |
| 461 | /* We didn't find it. vg_run_innerloop will have to request a |
| 462 | translation. */ |
| 463 | VGP_POPCC; |
| 464 | return (Addr)0; |
| 465 | } else { |
| 466 | /* Found it. Put the search result into the fast cache now. |
| 467 | Also set the mru_epoch to mark this translation as used. */ |
| 468 | UInt cno = (UInt)original_addr & VG_TT_FAST_MASK; |
| 469 | VG_(tt_fast)[cno] = (Addr)tte; |
| 470 | VG_(tt_fast_misses)++; |
| 471 | tte->mru_epoch = VG_(current_epoch); |
| 472 | VGP_POPCC; |
| 473 | return tte->trans_addr; |
| 474 | } |
| 475 | } |
| 476 | |
| 477 | |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 478 | /* Invalidate translations of original code [start .. start + range - 1]. |
| 479 | This is slow, so you *really* don't want to call it very often. |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 480 | */ |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 481 | void VG_(invalidate_translations) ( Addr start, UInt range ) |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 482 | { |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 483 | Addr i_start, i_end, o_start, o_end; |
| 484 | UInt out_count, out_osize, out_tsize; |
| 485 | Int i; |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 486 | |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 487 | # ifdef DEBUG_TRANSTAB |
| 488 | VG_(sanity_check_tc_tt)(); |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 489 | # endif |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 490 | i_start = start; |
| 491 | i_end = start + range - 1; |
| 492 | out_count = out_osize = out_tsize = 0; |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 493 | |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 494 | for (i = 0; i < VG_TT_SIZE; i++) { |
| 495 | if (vg_tt[i].orig_addr == VG_TTE_EMPTY |
| 496 | || vg_tt[i].orig_addr == VG_TTE_DELETED) continue; |
| 497 | o_start = vg_tt[i].orig_addr; |
| 498 | o_end = o_start + vg_tt[i].orig_size - 1; |
| 499 | if (o_end < i_start || o_start > i_end) |
| 500 | continue; |
| 501 | if (VG_(clo_cachesim)) |
| 502 | VG_(cachesim_notify_discard)( & vg_tt[i] ); |
| 503 | vg_tt[i].orig_addr = VG_TTE_DELETED; |
| 504 | VG_(this_epoch_out_count) ++; |
| 505 | VG_(this_epoch_out_osize) += vg_tt[i].orig_size; |
| 506 | VG_(this_epoch_out_tsize) += vg_tt[i].trans_size; |
| 507 | VG_(overall_out_count) ++; |
| 508 | VG_(overall_out_osize) += vg_tt[i].orig_size; |
| 509 | VG_(overall_out_tsize) += vg_tt[i].trans_size; |
| 510 | out_count ++; |
| 511 | out_osize += vg_tt[i].orig_size; |
| 512 | out_tsize += vg_tt[i].trans_size; |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 513 | } |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 514 | |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 515 | if (out_count > 0) { |
| 516 | VG_(invalidate_tt_fast)(); |
| 517 | VG_(sanity_check_tc_tt)(); |
| 518 | # ifdef DEBUG_TRANSTAB |
| 519 | { Addr aa; |
| 520 | for (aa = i_start; aa <= i_end; aa++) |
| 521 | vg_assert(search_trans_table ( aa ) == NULL); |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 522 | } |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 523 | # endif |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 524 | } |
| 525 | |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 526 | if (1|| VG_(clo_verbosity) > 1) |
| 527 | VG_(message)(Vg_UserMsg, |
| 528 | "discard %d (%d -> %d) translations in range %p .. %p", |
| 529 | out_count, out_osize, out_tsize, i_start, i_end ); |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 530 | } |
| 531 | |
| 532 | |
| 533 | /*------------------------------------------------------------*/ |
| 534 | /*--- Initialisation. ---*/ |
| 535 | /*------------------------------------------------------------*/ |
| 536 | |
sewardj | 18d7513 | 2002-05-16 11:06:21 +0000 | [diff] [blame] | 537 | void VG_(init_tt_tc) ( void ) |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 538 | { |
| 539 | Int i; |
| 540 | |
| 541 | /* Allocate the translation table and translation cache. */ |
| 542 | vg_assert(vg_tc == NULL); |
sewardj | e904795 | 2002-06-05 20:28:33 +0000 | [diff] [blame] | 543 | vg_tc = VG_(get_memory_from_mmap) ( VG_TC_SIZE * sizeof(UChar), |
| 544 | "trans-cache" ); |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 545 | vg_assert(vg_tc != NULL); |
| 546 | |
| 547 | vg_assert(vg_tt == NULL); |
sewardj | e904795 | 2002-06-05 20:28:33 +0000 | [diff] [blame] | 548 | vg_tt = VG_(get_memory_from_mmap) ( VG_TT_SIZE * sizeof(TTEntry), |
| 549 | "trans-table" ); |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 550 | vg_assert(vg_tt != NULL); |
| 551 | |
| 552 | /* The main translation table is empty. */ |
| 553 | vg_tt_used = 0; |
| 554 | for (i = 0; i < VG_TT_SIZE; i++) { |
| 555 | vg_tt[i].orig_addr = VG_TTE_EMPTY; |
| 556 | } |
| 557 | |
| 558 | /* The translation table's fast cache is empty. Point all entries |
| 559 | at the first TT entry, which is, of course, empty. */ |
| 560 | for (i = 0; i < VG_TT_FAST_SIZE; i++) |
| 561 | VG_(tt_fast)[i] = (Addr)(&vg_tt[0]); |
sewardj | de4a1d0 | 2002-03-22 01:27:54 +0000 | [diff] [blame] | 562 | } |
| 563 | |
| 564 | /*--------------------------------------------------------------------*/ |
| 565 | /*--- end vg_transtab.c ---*/ |
| 566 | /*--------------------------------------------------------------------*/ |