/*--------------------------------------------------------------------*/ /*--- Cache simulation. ---*/ /*--- ct_sim.c ---*/ /*--------------------------------------------------------------------*/ /* This file is part of Cachegrind, a Valgrind skin for cache profiling programs. Copyright (C) 2002 Nicholas Nethercote njn25@cam.ac.uk Modified for Calltree-Skin, Josef Weidendorfer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The GNU General Public License is contained in the file COPYING. */ #include "ct_include.h" /* Notes: - simulates a write-allocate cache - (block --> set) hash function uses simple bit selection - handling of references straddling two cache blocks: - counts as only one cache access (not two) - both blocks hit --> one hit - one block hits, the other misses --> one miss - both blocks miss --> one miss (not two) */ typedef struct { int size; /* bytes */ int assoc; int line_size; /* bytes */ int sets; int sets_min_1; int assoc_bits; int line_size_bits; int tag_shift; char desc_line[128]; int* tags; } cache_t2; /* By this point, the size/assoc/line_size has been checked. */ static void cachesim_initcache(cache_t config, cache_t2* c) { int i; c->size = config.size; c->assoc = config.assoc; c->line_size = config.line_size; c->sets = (c->size / c->line_size) / c->assoc; c->sets_min_1 = c->sets - 1; c->assoc_bits = VG_(log2)(c->assoc); c->line_size_bits = VG_(log2)(c->line_size); c->tag_shift = c->line_size_bits + VG_(log2)(c->sets); if (c->assoc == 1) { VG_(sprintf)(c->desc_line, "%d B, %d B, direct-mapped", c->size, c->line_size); } else { VG_(sprintf)(c->desc_line, "%d B, %d B, %d-way associative", c->size, c->line_size, c->assoc); } c->tags = VG_(malloc)(sizeof(UInt) * c->sets * c->assoc); for (i = 0; i < c->sets * c->assoc; i++) c->tags[i] = 0; } #if 0 static void print_cache(cache_t2* c) { UInt set, way, i; /* Note initialisation and update of 'i'. */ for (i = 0, set = 0; set < c->sets; set++) { for (way = 0; way < c->assoc; way++, i++) { VG_(printf)("%8x ", c->tags[i]); } VG_(printf)("\n"); } } #endif /* This is done as a macro rather than by passing in the cache_t2 as an * arg because it slows things down by a small amount (3-5%) due to all * that extra indirection. */ #define CACHESIM(L, MISS_TREATMENT, HIT_TREATMENT) \ /* The cache and associated bits and pieces. */ \ static cache_t2 L; \ \ static void cachesim_##L##_initcache(cache_t config) \ { \ cachesim_initcache(config, &L); \ } \ \ static Int cachesim_##L##_doref(Addr a, UChar size) \ { \ register UInt set1 = ( a >> L.line_size_bits) & (L.sets_min_1); \ register UInt set2 = ((a+size-1) >> L.line_size_bits) & (L.sets_min_1); \ register UInt tag = a >> L.tag_shift; \ int i, j; \ Bool is_miss = False; \ int* set; \ \ /* First case: word entirely within line. */ \ if (set1 == set2) { \ \ /* Shifting is a bit faster than multiplying */ \ set = &(L.tags[set1 << L.assoc_bits]); \ \ /* This loop is unrolled for just the first case, which is the most */\ /* common. We can't unroll any further because it would screw up */\ /* if we have a direct-mapped (1-way) cache. */\ if (tag == set[0]) { \ HIT_TREATMENT; \ } \ /* If the tag is one other than the MRU, move it into the MRU spot */\ /* and shuffle the rest down. */\ for (i = 1; i < L.assoc; i++) { \ if (tag == set[i]) { \ for (j = i; j > 0; j--) { \ set[j] = set[j - 1]; \ } \ set[0] = tag; \ HIT_TREATMENT; \ } \ } \ \ /* A miss; install this tag as MRU, shuffle rest down. */ \ for (j = L.assoc - 1; j > 0; j--) { \ set[j] = set[j - 1]; \ } \ set[0] = tag; \ MISS_TREATMENT; \ \ /* Second case: word straddles two lines. */ \ /* Nb: this is a fast way of doing ((set1+1) % L.sets) */ \ } else if (((set1 + 1) & (L.sets-1)) == set2) { \ set = &(L.tags[set1 << L.assoc_bits]); \ if (tag == set[0]) { \ goto block2; \ } \ for (i = 1; i < L.assoc; i++) { \ if (tag == set[i]) { \ for (j = i; j > 0; j--) { \ set[j] = set[j - 1]; \ } \ set[0] = tag; \ goto block2; \ } \ } \ for (j = L.assoc - 1; j > 0; j--) { \ set[j] = set[j - 1]; \ } \ set[0] = tag; \ is_miss = True; \ block2: \ set = &(L.tags[set2 << L.assoc_bits]); \ if (tag == set[0]) { \ goto miss_treatment; \ } \ for (i = 1; i < L.assoc; i++) { \ if (tag == set[i]) { \ for (j = i; j > 0; j--) { \ set[j] = set[j - 1]; \ } \ set[0] = tag; \ goto miss_treatment; \ } \ } \ for (j = L.assoc - 1; j > 0; j--) { \ set[j] = set[j - 1]; \ } \ set[0] = tag; \ is_miss = True; \ miss_treatment: \ if (is_miss) { MISS_TREATMENT; } \ \ } else { \ VG_(printf)("addr: %x size: %u sets: %d %d", a, size, set1, set2); \ VG_(skin_panic)("item straddles more than two cache sets"); \ } \ HIT_TREATMENT; \ } #if !CT_HWPREFETCH CACHESIM(L2, { return 2; }, { return 1; } ); CACHESIM(I1, { return cachesim_L2_doref(a, size); }, { return 0; } ); CACHESIM(D1, { return cachesim_L2_doref(a, size); }, { return 0; } ); #else static Int prefetch_L2_doref(Addr a, UChar size); CACHESIM(L2, { return 2; }, { return 1; } ); CACHESIM(I1, { return prefetch_L2_doref(a, size); }, { return 0; } ); CACHESIM(D1, { return prefetch_L2_doref(a, size); }, { return 0; } ); static ULong prefetch_up = 0; static ULong prefetch_down = 0; #define PF_STREAMS 8 #define PF_PAGEBITS 12 static UInt pf_lastblock[PF_STREAMS]; static Int pf_seqblocks[PF_STREAMS]; static void prefetch_init() { int i; for(i=0;i> PF_PAGEBITS) % PF_STREAMS; block = ( a >> L2.line_size_bits); if (block != pf_lastblock[stream]) { if (pf_seqblocks[stream] == 0) { if (pf_lastblock[stream] +1 == block) pf_seqblocks[stream]++; else if (pf_lastblock[stream] -1 == block) pf_seqblocks[stream]--; } else if (pf_seqblocks[stream] >0) { if (pf_lastblock[stream] +1 == block) { pf_seqblocks[stream]++; if (pf_seqblocks[stream] >= 2) { prefetch_up++; cachesim_L2_doref(a + 5 * L2.line_size,1); } } else pf_seqblocks[stream] = 0; } else if (pf_seqblocks[stream] <0) { if (pf_lastblock[stream] -1 == block) { pf_seqblocks[stream]--; if (pf_seqblocks[stream] <= -2) { prefetch_down++; cachesim_L2_doref(a - 5 * L2.line_size,1); } } else pf_seqblocks[stream] = 0; } pf_lastblock[stream] = block; } return cachesim_L2_doref(a, size); } #endif /* CT_HWPREFETCH */ /*------------------------------------------------------------*/ /*--- Automagic cache initialisation stuff ---*/ /*------------------------------------------------------------*/ #define UNDEFINED_CACHE ((cache_t) { -1, -1, -1 }) static cache_t clo_I1_cache = UNDEFINED_CACHE; static cache_t clo_D1_cache = UNDEFINED_CACHE; static cache_t clo_L2_cache = UNDEFINED_CACHE; /* All CPUID info taken from sandpile.org/a32/cpuid.htm */ /* Probably only works for Intel and AMD chips, and probably only for some of * them. */ static __inline__ void cpuid(Int n, UInt *a, UInt *b, UInt *c, UInt *d) { __asm__ __volatile__ ( "cpuid" : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d) /* output */ : "0" (n) /* input */ ); } static void micro_ops_warn(Int actual_size, Int used_size, Int line_size) { VG_(message)(Vg_DebugMsg, "warning: Pentium with %d K micro-op instruction trace cache", actual_size); VG_(message)(Vg_DebugMsg, " Simulating a %d KB cache with %d B lines", used_size, line_size); } /* Intel method is truly wretched. We have to do an insane indexing into an * array of pre-defined configurations for various parts of the memory * hierarchy. */ static Int Intel_cache_info(Int level, cache_t* I1c, cache_t* D1c, cache_t* L2c) { UChar info[16]; Int i, trials; Bool L2_found = False; if (level < 2) { VG_(message)(Vg_DebugMsg, "warning: CPUID level < 2 for Intel processor (%d)", level); return -1; } cpuid(2, (Int*)&info[0], (Int*)&info[4], (Int*)&info[8], (Int*)&info[12]); trials = info[0] - 1; /* AL register - bits 0..7 of %eax */ info[0] = 0x0; /* reset AL */ if (0 != trials) { VG_(message)(Vg_DebugMsg, "warning: non-zero CPUID trials for Intel processor (%d)", trials); return -1; } for (i = 0; i < 16; i++) { switch (info[i]) { case 0x0: /* ignore zeros */ break; /* TLB info, ignore */ case 0x01: case 0x02: case 0x03: case 0x04: case 0x50: case 0x51: case 0x52: case 0x5b: case 0x5c: case 0x5d: case 0xb0: case 0xb3: break; case 0x06: *I1c = (cache_t) { 8, 4, 32 }; break; case 0x08: *I1c = (cache_t) { 16, 4, 32 }; break; case 0x30: *I1c = (cache_t) { 32, 8, 64 }; break; case 0x0a: *D1c = (cache_t) { 8, 2, 32 }; break; case 0x0c: *D1c = (cache_t) { 16, 4, 32 }; break; case 0x2c: *D1c = (cache_t) { 32, 8, 64 }; break; /* IA-64 info -- panic! */ case 0x10: case 0x15: case 0x1a: case 0x88: case 0x89: case 0x8a: case 0x8d: case 0x90: case 0x96: case 0x9b: VG_(message)(Vg_DebugMsg, "error: IA-64 cache stats! Cachegrind doesn't run on IA-64..."); VG_(skin_panic)("IA-64 detected"); case 0x22: case 0x23: case 0x25: case 0x29: VG_(message)(Vg_DebugMsg, "warning: L3 cache detected but ignored\n"); break; /* These are sectored, whatever that means */ case 0x39: *L2c = (cache_t) { 128, 4, 64 }; L2_found = True; break; case 0x3c: *L2c = (cache_t) { 256, 4, 64 }; L2_found = True; break; /* If a P6 core, this means "no L2 cache". If a P4 core, this means "no L3 cache". We don't know what core it is, so don't issue a warning. To detect a missing L2 cache, we use 'L2_found'. */ case 0x40: break; case 0x41: *L2c = (cache_t) { 128, 4, 32 }; L2_found = True; break; case 0x42: *L2c = (cache_t) { 256, 4, 32 }; L2_found = True; break; case 0x43: *L2c = (cache_t) { 512, 4, 32 }; L2_found = True; break; case 0x44: *L2c = (cache_t) { 1024, 4, 32 }; L2_found = True; break; case 0x45: *L2c = (cache_t) { 2048, 4, 32 }; L2_found = True; break; /* These are sectored, whatever that means */ case 0x66: *D1c = (cache_t) { 8, 4, 64 }; break; /* sectored */ case 0x67: *D1c = (cache_t) { 16, 4, 64 }; break; /* sectored */ case 0x68: *D1c = (cache_t) { 32, 4, 64 }; break; /* sectored */ /* HACK ALERT: Instruction trace cache -- capacity is micro-ops based. * conversion to byte size is a total guess; treat the 12K and 16K * cases the same since the cache byte size must be a power of two for * everything to work!. Also guessing 32 bytes for the line size... */ case 0x70: /* 12K micro-ops, 8-way */ *I1c = (cache_t) { 16, 8, 32 }; micro_ops_warn(12, 16, 32); break; case 0x71: /* 16K micro-ops, 8-way */ *I1c = (cache_t) { 16, 8, 32 }; micro_ops_warn(16, 16, 32); break; case 0x72: /* 32K micro-ops, 8-way */ *I1c = (cache_t) { 32, 8, 32 }; micro_ops_warn(32, 32, 32); break; /* These are sectored, whatever that means */ case 0x79: *L2c = (cache_t) { 128, 8, 64 }; L2_found = True; break; case 0x7a: *L2c = (cache_t) { 256, 8, 64 }; L2_found = True; break; case 0x7b: *L2c = (cache_t) { 512, 8, 64 }; L2_found = True; break; case 0x7c: *L2c = (cache_t) { 1024, 8, 64 }; L2_found = True; break; case 0x7e: *L2c = (cache_t) { 256, 8, 128 }; L2_found = True; break; case 0x81: *L2c = (cache_t) { 128, 8, 32 }; L2_found = True; break; case 0x82: *L2c = (cache_t) { 256, 8, 32 }; L2_found = True; break; case 0x83: *L2c = (cache_t) { 512, 8, 32 }; L2_found = True; break; case 0x84: *L2c = (cache_t) { 1024, 8, 32 }; L2_found = True; break; case 0x85: *L2c = (cache_t) { 2048, 8, 32 }; L2_found = True; break; case 0x86: *L2c = (cache_t) { 512, 4, 64 }; L2_found = True; break; case 0x87: *L2c = (cache_t) { 1024, 8, 64 }; L2_found = True; break; default: VG_(message)(Vg_DebugMsg, "warning: Unknown Intel cache config value " "(0x%x), ignoring", info[i]); break; } } if (!L2_found) VG_(message)(Vg_DebugMsg, "warning: L2 cache not installed, ignore L2 results."); return 0; } /* AMD method is straightforward, just extract appropriate bits from the * result registers. * * Bits, for D1 and I1: * 31..24 data L1 cache size in KBs * 23..16 data L1 cache associativity (FFh=full) * 15.. 8 data L1 cache lines per tag * 7.. 0 data L1 cache line size in bytes * * Bits, for L2: * 31..16 unified L2 cache size in KBs * 15..12 unified L2 cache associativity (0=off, FFh=full) * 11.. 8 unified L2 cache lines per tag * 7.. 0 unified L2 cache line size in bytes * * #3 The AMD K7 processor's L2 cache must be configured prior to relying * upon this information. (Whatever that means -- njn) * * Also, according to Cyrille Chepelov, Duron stepping A0 processors (model * 0x630) have a bug and misreport their L2 size as 1KB (it's really 64KB), * so we detect that. * * Returns 0 on success, non-zero on failure. */ static Int AMD_cache_info(cache_t* I1c, cache_t* D1c, cache_t* L2c) { UInt ext_level; Int dummy, model; Int I1i, D1i, L2i; cpuid(0x80000000, &ext_level, &dummy, &dummy, &dummy); if (0 == (ext_level & 0x80000000) || ext_level < 0x80000006) { VG_(message)(Vg_UserMsg, "warning: ext_level < 0x80000006 for AMD processor (0x%x)", ext_level); return -1; } cpuid(0x80000005, &dummy, &dummy, &D1i, &I1i); cpuid(0x80000006, &dummy, &dummy, &L2i, &dummy); cpuid(0x1, &model, &dummy, &dummy, &dummy); /*VG_(message)(Vg_UserMsg,"CPU model %04x",model);*/ /* Check for Duron bug */ if (model == 0x630) { VG_(message)(Vg_UserMsg, "Buggy Duron stepping A0. Assuming L2 size=65536 bytes"); L2i = (64 << 16) | (L2i & 0xffff); } D1c->size = (D1i >> 24) & 0xff; D1c->assoc = (D1i >> 16) & 0xff; D1c->line_size = (D1i >> 0) & 0xff; I1c->size = (I1i >> 24) & 0xff; I1c->assoc = (I1i >> 16) & 0xff; I1c->line_size = (I1i >> 0) & 0xff; L2c->size = (L2i >> 16) & 0xffff; /* Nb: different bits used for L2 */ L2c->assoc = (L2i >> 12) & 0xf; L2c->line_size = (L2i >> 0) & 0xff; return 0; } static jmp_buf cpuid_jmpbuf; static void cpuid_SIGILL_handler(int signum) { __builtin_longjmp(cpuid_jmpbuf, 1); } static Int get_caches_from_CPUID(cache_t* I1c, cache_t* D1c, cache_t* L2c) { Int level, res, ret; Char vendor_id[13]; vki_ksigaction sigill_new, sigill_saved; /* Install own SIGILL handler */ sigill_new.ksa_handler = cpuid_SIGILL_handler; sigill_new.ksa_flags = 0; //sigill_new.ksa_restorer = NULL; res = VG_(ksigemptyset)( &sigill_new.ksa_mask ); CT_ASSERT(res == 0); res = VG_(ksigaction)( VKI_SIGILL, &sigill_new, &sigill_saved ); CT_ASSERT(res == 0); /* Trap for illegal instruction, in case it's a really old processor that * doesn't support CPUID. */ if (__builtin_setjmp(cpuid_jmpbuf) == 0) { cpuid(0, &level, (int*)&vendor_id[0], (int*)&vendor_id[8], (int*)&vendor_id[4]); vendor_id[12] = '\0'; /* Restore old SIGILL handler */ res = VG_(ksigaction)( VKI_SIGILL, &sigill_saved, NULL ); CT_ASSERT(res == 0); } else { VG_(message)(Vg_DebugMsg, "CPUID instruction not supported"); /* Restore old SIGILL handler */ res = VG_(ksigaction)( VKI_SIGILL, &sigill_saved, NULL ); CT_ASSERT(res == 0); return -1; } if (0 == level) { VG_(message)(Vg_DebugMsg, "CPUID level is 0, early Pentium?\n"); return -1; } /* Only handling Intel and AMD chips... no Cyrix, Transmeta, etc */ if (0 == VG_(strcmp)(vendor_id, "GenuineIntel")) { ret = Intel_cache_info(level, I1c, D1c, L2c); } else if (0 == VG_(strcmp)(vendor_id, "AuthenticAMD")) { ret = AMD_cache_info(I1c, D1c, L2c); } else if (0 == VG_(strcmp)(vendor_id, "CentaurHauls")) { /* Total kludge. Pretend to be a VIA Nehemiah. */ D1c->size = 64; D1c->assoc = 16; D1c->line_size = 16; I1c->size = 64; I1c->assoc = 4; I1c->line_size = 16; L2c->size = 64; L2c->assoc = 16; L2c->line_size = 16; ret = 0; } else { VG_(message)(Vg_DebugMsg, "CPU vendor ID not recognised (%s)", vendor_id); return -1; } /* Successful! Convert sizes from KB to bytes */ I1c->size *= 1024; D1c->size *= 1024; L2c->size *= 1024; return ret; } /* Checks cache config is ok; makes it so if not. */ static void check_cache(cache_t* cache, cache_t* dflt, Char *name) { /* First check they're all powers of two */ if (-1 == VG_(log2)(cache->size)) { VG_(message)(Vg_UserMsg, "warning: %s size of %dB not a power of two; " "defaulting to %dB", name, cache->size, dflt->size); cache->size = dflt->size; } if (-1 == VG_(log2)(cache->assoc)) { VG_(message)(Vg_UserMsg, "warning: %s associativity of %d not a power of two; " "defaulting to %d-way", name, cache->assoc, dflt->assoc); cache->assoc = dflt->assoc; } if (-1 == VG_(log2)(cache->line_size)) { VG_(message)(Vg_UserMsg, "warning: %s line size of %dB not a power of two; " "defaulting to %dB", name, cache->line_size, dflt->line_size); cache->line_size = dflt->line_size; } /* Then check line size >= 16 -- any smaller and a single instruction could * straddle three cache lines, which breaks a simulation assertion and is * stupid anyway. */ if (cache->line_size < MIN_LINE_SIZE) { VG_(message)(Vg_UserMsg, "warning: %s line size of %dB too small; " "increasing to %dB", name, cache->line_size, MIN_LINE_SIZE); cache->line_size = MIN_LINE_SIZE; } /* Then check cache size > line size (causes seg faults if not). */ if (cache->size <= cache->line_size) { VG_(message)(Vg_UserMsg, "warning: %s cache size of %dB <= line size of %dB; " "increasing to %dB", name, cache->size, cache->line_size, cache->line_size * 2); cache->size = cache->line_size * 2; } /* Then check assoc <= (size / line size) (seg faults otherwise). */ if (cache->assoc > (cache->size / cache->line_size)) { VG_(message)(Vg_UserMsg, "warning: %s associativity > (size / line size); " "increasing size to %dB", name, cache->assoc * cache->line_size); cache->size = cache->assoc * cache->line_size; } } /* On entry, args are undefined. Fill them with any info from the * command-line, then fill in any remaining with CPUID instruction if possible, * otherwise use defaults. Then check them and fix if not ok. */ static void get_caches(cache_t* I1c, cache_t* D1c, cache_t* L2c) { /* Defaults are for a model 3 or 4 Athlon */ cache_t I1_dflt = (cache_t) { 65536, 2, 64 }; cache_t D1_dflt = (cache_t) { 65536, 2, 64 }; cache_t L2_dflt = (cache_t) { 262144, 8, 64 }; #define CMD_LINE_DEFINED(L) \ (-1 != clo_##L##_cache.size || \ -1 != clo_##L##_cache.assoc || \ -1 != clo_##L##_cache.line_size) *I1c = clo_I1_cache; *D1c = clo_D1_cache; *L2c = clo_L2_cache; /* If any undefined on command-line, try CPUID */ if (! CMD_LINE_DEFINED(I1) || ! CMD_LINE_DEFINED(D1) || ! CMD_LINE_DEFINED(L2)) { /* Overwrite CPUID result for any cache defined on command-line */ if (0 == get_caches_from_CPUID(I1c, D1c, L2c)) { if (CMD_LINE_DEFINED(I1)) *I1c = clo_I1_cache; if (CMD_LINE_DEFINED(D1)) *D1c = clo_D1_cache; if (CMD_LINE_DEFINED(L2)) *L2c = clo_L2_cache; /* CPUID failed, use defaults for each undefined by command-line */ } else { VG_(message)(Vg_DebugMsg, "Couldn't detect cache configuration, using one " "or more defaults "); *I1c = (CMD_LINE_DEFINED(I1) ? clo_I1_cache : I1_dflt); *D1c = (CMD_LINE_DEFINED(D1) ? clo_D1_cache : D1_dflt); *L2c = (CMD_LINE_DEFINED(L2) ? clo_L2_cache : L2_dflt); } } #undef CMD_LINE_DEFINED check_cache(I1c, &I1_dflt, "I1"); check_cache(D1c, &D1_dflt, "D1"); check_cache(L2c, &L2_dflt, "L2"); if (VG_(clo_verbosity) > 1) { VG_(message)(Vg_UserMsg, "Cache configuration used:"); VG_(message)(Vg_UserMsg, " I1: %dB, %d-way, %dB lines", I1c->size, I1c->assoc, I1c->line_size); VG_(message)(Vg_UserMsg, " D1: %dB, %d-way, %dB lines", D1c->size, D1c->assoc, D1c->line_size); VG_(message)(Vg_UserMsg, " L2: %dB, %d-way, %dB lines", L2c->size, L2c->assoc, L2c->line_size); } } void SK_(cachesim_init)(void) { cache_t I1c, D1c, L2c; get_caches(&I1c, &D1c, &L2c); cachesim_I1_initcache(I1c); cachesim_D1_initcache(D1c); cachesim_L2_initcache(L2c); #if CT_HWPREFETCH prefetch_init(); #endif } void SK_(cachesim_getdesc)(Char* buf) { Int p; p = VG_(sprintf)(buf, "\ndesc: I1 cache: %s\n", I1.desc_line); p += VG_(sprintf)(buf+p, "desc: D1 cache: %s\n", D1.desc_line); VG_(sprintf)(buf+p, "desc: L2 cache: %s\n", L2.desc_line); } /*------------------------------------------------------------*/ /*--- Setup for Event set. ---*/ /*------------------------------------------------------------*/ struct event_sets SK_(sets); /* Offset to events in event set, used in log_* functions */ static Int off_D0_Ir; static Int off_D1r_Ir; static Int off_D1r_Dr; static Int off_D1w_Ir; static Int off_D1w_Dw; static Int off_D2_Ir; static Int off_D2_Dr; static Int off_D2_Dw; void SK_(init_eventsets)(Bool do_cache, Int max_user) { EventType * e1, *e2, *e3; EventSet *Ir, *Dr, *Dw; EventSet *D0, *D1r, *D1w, *D2; EventSet *sim, *full; if (do_cache) { Ir = SK_(get_eventset)("Ir", 3); e1 = SK_(register_eventtype)("Ir"); e2 = SK_(register_eventtype)("I1mr"); e3 = SK_(register_eventtype)("I2mr"); SK_(add_dep_event3)(Ir, e1,e2,e3); Dr = SK_(get_eventset)("Dr", 3); e1 = SK_(register_eventtype)("Dr"); e2 = SK_(register_eventtype)("D1mr"); e3 = SK_(register_eventtype)("D2mr"); SK_(add_dep_event3)(Dr, e1,e2,e3); Dw = SK_(get_eventset)("Dw", 3); e1 = SK_(register_eventtype)("Dw"); e2 = SK_(register_eventtype)("D1mw"); e3 = SK_(register_eventtype)("D2mw"); SK_(add_dep_event3)(Dw, e1,e2,e3); } else { Ir = SK_(get_eventset)("Ir", 1); e1 = SK_(register_eventtype)("Ir"); SK_(add_eventtype)(Ir, e1); Dr = 0; Dw = 0; } D0 = Ir; off_D0_Ir = 0; D1r = SK_(get_eventset)("D1r", 6); off_D1r_Ir = SK_(add_eventset)(D1r, Ir); off_D1r_Dr = SK_(add_eventset)(D1r, Dr); D1w = SK_(get_eventset)("D1w", 6); off_D1w_Ir = SK_(add_eventset)(D1w, Ir); off_D1w_Dw = SK_(add_eventset)(D1w, Dw); D2 = SK_(get_eventset)("D2", 9); off_D2_Ir = SK_(add_eventset)(D2, Ir); off_D2_Dr = SK_(add_eventset)(D2, Dr); off_D2_Dw = SK_(add_eventset)(D2, Dw); sim = SK_(get_eventset)("sim", 9); SK_(sets).off_sim_Ir = SK_(add_eventset)(sim, Ir); SK_(sets).off_sim_Dr = SK_(add_eventset)(sim, Dr); SK_(sets).off_sim_Dw = SK_(add_eventset)(sim, Dw); if (SK_(clo).collect_alloc) max_user += 2; if (SK_(clo).collect_systime) max_user += 2; full = SK_(get_eventset)("full", sim->size + max_user); SK_(add_eventset)(full, sim); SK_(sets).off_full_Ir = SK_(sets).off_sim_Ir; SK_(sets).off_full_Dr = SK_(sets).off_sim_Dr; SK_(sets).off_full_Dw = SK_(sets).off_sim_Dw; SK_(sets).Ir = Ir; SK_(sets).Dr = Dr; SK_(sets).Dw = Dw; SK_(sets).D0 = D0; SK_(sets).D1r = D1r; SK_(sets).D1w = D1w; SK_(sets).D2 = D2; SK_(sets).sim = sim; SK_(sets).full = full; /* Not-existing events are silently ignored */ SK_(dumpmap) = SK_(get_eventmapping)(full); SK_(append_event)(SK_(dumpmap), "Ir"); SK_(append_event)(SK_(dumpmap), "Dr"); SK_(append_event)(SK_(dumpmap), "Dw"); SK_(append_event)(SK_(dumpmap), "I1mr"); SK_(append_event)(SK_(dumpmap), "D1mr"); SK_(append_event)(SK_(dumpmap), "D1mw"); SK_(append_event)(SK_(dumpmap), "I2mr"); SK_(append_event)(SK_(dumpmap), "D2mr"); SK_(append_event)(SK_(dumpmap), "D2mw"); if (SK_(clo).collect_alloc) { e1 = SK_(register_eventtype)("allocCount"); e2 = SK_(register_eventtype)("allocSize"); SK_(sets).off_full_user = SK_(add_dep_event2)(full, e1,e2); SK_(append_event)(SK_(dumpmap), "allocCount"); SK_(append_event)(SK_(dumpmap), "allocSize"); } if (SK_(clo).collect_systime) { e1 = SK_(register_eventtype)("sysCount"); e2 = SK_(register_eventtype)("sysTime"); SK_(sets).off_full_systime = SK_(add_dep_event2)(full, e1,e2); SK_(append_event)(SK_(dumpmap), "sysCount"); SK_(append_event)(SK_(dumpmap), "sysTime"); } } void SK_(add_and_zero_Dx)(EventSet* es, SimCost dst, ULong* cost) { /* FIXME: This is hardcoded... */ if (es == SK_(sets).D0) { SK_(add_and_zero_cost)( SK_(sets).Ir, dst + SK_(sets).off_sim_Ir, cost + off_D0_Ir); } else if (es == SK_(sets).D1r) { SK_(add_and_zero_cost)( SK_(sets).Ir, dst + SK_(sets).off_sim_Ir, cost + off_D1r_Ir); SK_(add_and_zero_cost)( SK_(sets).Ir, dst + SK_(sets).off_sim_Dr, cost + off_D1r_Dr); } else if (es == SK_(sets).D1w) { SK_(add_and_zero_cost)( SK_(sets).Ir, dst + SK_(sets).off_sim_Ir, cost + off_D1w_Ir); SK_(add_and_zero_cost)( SK_(sets).Ir, dst + SK_(sets).off_sim_Dw, cost + off_D1w_Dw); } else { CT_ASSERT(es == SK_(sets).D2); SK_(add_and_zero_cost)( SK_(sets).Ir, dst + SK_(sets).off_sim_Ir, cost + off_D2_Ir); SK_(add_and_zero_cost)( SK_(sets).Ir, dst + SK_(sets).off_sim_Dr, cost + off_D2_Dr); SK_(add_and_zero_cost)( SK_(sets).Ir, dst + SK_(sets).off_sim_Dw, cost + off_D2_Dw); } } /*------------------------------------------------------------*/ /*--- Helper functions called by instrumented code ---*/ /*------------------------------------------------------------*/ static __inline__ void inc_costs(int miss, ULong* c1, ULong* c2) { c1[0]++; c2[0]++; if (miss == 0) return; c1[1]++; c2[1]++; if (miss==1) return; c1[2]++; c2[2]++; CT_ASSERT(miss==2); } /* Following global vars are setup before by setup_bbcc(): * - Addr bb_base (instruction start address of original BB) * - ULong* cost_base (start of cost array for BB) * - BBCC* nonskipped (only != 0 when in a function not skipped) */ __attribute__ ((regparm (1))) void SK_(log_0D)(InstrInfo* ii) { Int missIr; VGP_PUSHCC(VgpCacheSimulate); CT_DEBUG(6,"log_0D: iaddr=%p, isize=%u\n", SK_(bb_base) + ii->instr_offset, ii->instr_size); missIr = cachesim_I1_doref(SK_(bb_base) + ii->instr_offset, ii->instr_size); if (SK_(current_state).collect) { ULong* cost_Ir; if (SK_(current_state).nonskipped) cost_Ir = SK_(current_state).nonskipped->skipped + SK_(sets).off_full_Ir; else cost_Ir = SK_(cost_base) + ii->cost_offset + off_D0_Ir; inc_costs(missIr, cost_Ir, SK_(current_state).cost + SK_(sets).off_full_Ir ); } VGP_POPCC(VgpCacheSimulate); } __attribute__ ((regparm (2))) void SK_(log_1Dr)(InstrInfo* ii, Addr data) { Int missIr, missDr; VGP_PUSHCC(VgpCacheSimulate); CT_DEBUG(6,"log_1Dr: iaddr=%p, isize=%u, daddr=%p, dsize=%u\n", SK_(bb_base) + ii->instr_offset, ii->instr_size, data, ii->data_size); missIr = cachesim_I1_doref(SK_(bb_base) + ii->instr_offset, ii->instr_size); missDr = cachesim_D1_doref(data, ii->data_size); if (SK_(current_state).collect) { ULong *cost_Ir, *cost_Dr; if (SK_(current_state).nonskipped) { cost_Ir = SK_(current_state).nonskipped->skipped + SK_(sets).off_full_Ir; cost_Dr = SK_(current_state).nonskipped->skipped + SK_(sets).off_full_Dr; } else { cost_Ir = SK_(cost_base) + ii->cost_offset + off_D1r_Ir; cost_Dr = SK_(cost_base) + ii->cost_offset + off_D1r_Dr; } inc_costs(missIr, cost_Ir, SK_(current_state).cost + SK_(sets).off_full_Ir ); inc_costs(missDr, cost_Dr, SK_(current_state).cost + SK_(sets).off_full_Dr ); } VGP_POPCC(VgpCacheSimulate); } __attribute__ ((regparm (2))) void SK_(log_1Dw)(InstrInfo* ii, Addr data) { Int missIr, missDw; VGP_PUSHCC(VgpCacheSimulate); CT_DEBUG(6,"log_1Dw: iaddr=%p, isize=%u, daddr=%p, dsize=%u\n", SK_(bb_base) + ii->instr_offset, ii->instr_size, data, ii->data_size); missIr = cachesim_I1_doref(SK_(bb_base) + ii->instr_offset, ii->instr_size); missDw = cachesim_D1_doref(data, ii->data_size); if (SK_(current_state).collect) { ULong *cost_Ir, *cost_Dw; if (SK_(current_state).nonskipped) { cost_Ir = SK_(current_state).nonskipped->skipped + SK_(sets).off_sim_Ir; cost_Dw = SK_(current_state).nonskipped->skipped + SK_(sets).off_sim_Dw; } else { cost_Ir = SK_(cost_base) + ii->cost_offset + off_D1w_Ir; cost_Dw = SK_(cost_base) + ii->cost_offset + off_D1w_Dw; } inc_costs(missIr, cost_Ir, SK_(current_state).cost + SK_(sets).off_full_Ir ); inc_costs(missDw, cost_Dw, SK_(current_state).cost + SK_(sets).off_full_Dw ); } VGP_POPCC(VgpCacheSimulate); } __attribute__ ((regparm (3))) void SK_(log_2D)(InstrInfo* ii, Addr data1, Addr data2) { Int missIr, missDr, missDw; VGP_PUSHCC(VgpCacheSimulate); CT_DEBUG(6,"log_D2: iaddr=%p, isize=%u, daddr1=%p, daddr2=%p, dsize=%u\n", SK_(bb_base) + ii->instr_offset, ii->instr_size, data1, data2, ii->data_size); missIr = cachesim_I1_doref(SK_(bb_base) + ii->instr_offset, ii->instr_size); missDr = cachesim_D1_doref(data1, ii->data_size); missDw = cachesim_D1_doref(data2, ii->data_size); if (SK_(current_state).collect) { ULong *cost_Ir, *cost_Dr, *cost_Dw; if (SK_(current_state).nonskipped) { cost_Ir = SK_(current_state).nonskipped->skipped + SK_(sets).off_sim_Ir; cost_Dr = SK_(current_state).nonskipped->skipped + SK_(sets).off_sim_Dr; cost_Dw = SK_(current_state).nonskipped->skipped + SK_(sets).off_sim_Dw; } else { cost_Ir = SK_(cost_base) + ii->cost_offset + off_D2_Ir; cost_Dr = SK_(cost_base) + ii->cost_offset + off_D2_Dr; cost_Dw = SK_(cost_base) + ii->cost_offset + off_D2_Dw; } inc_costs(missIr, cost_Ir, SK_(current_state).cost + SK_(sets).off_full_Ir ); inc_costs(missDr, cost_Dr, SK_(current_state).cost + SK_(sets).off_full_Dr ); inc_costs(missDw, cost_Dw, SK_(current_state).cost + SK_(sets).off_full_Dw ); } VGP_POPCC(VgpCacheSimulate); } /*--------------------------------------------------------------------*/ /*--- end ct_sim.c ---*/ /*--------------------------------------------------------------------*/