/*--------------------------------------------------------------------*/
/*--- 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_STREAMS;i++)
pf_lastblock[i] = pf_seqblocks[i] = 0;
}
/*
* HW Prefetch emulation
* Start prefetching when detecting sequential access to 3 memory blocks.
*/
static Int prefetch_L2_doref(Addr a, UChar size)
{
UInt stream, block;
if (!SK_(clo).simulate_hwpref)
return cachesim_L2_doref(a, size);
stream = (a >> 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 ---*/
/*--------------------------------------------------------------------*/
syntax highlighted by Code2HTML, v. 0.9.1