1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
|
/*-----------------------------------------------------------------*-C-*---
* File: handc/runtime/applyg.c
*
* Copyright (C)1997 Donovan Kolbly <d.kolbly@rscheme.org>
* as part of the RScheme project, licensed for free use.
* See <http://www.rscheme.org/> for the latest information.
*
* File version: 1.5
* File mod date: 1997.11.29 23:10:51
* System build: v0.7.2, 97.12.21
*
* Purpose: Generic function dispatch code (using a tiny hash table cache)
*------------------------------------------------------------------------*/
#include <rscheme/vinsns.h>
#include <rscheme/runtime.h>
#include <rscheme/linktype.h>
#include <rscheme/scheme.h>
#include <rscheme/allocns.h>
#define CLASS_HASH_CODE SLOT(5)
#define GF_CACHE_0_K SLOT(4)
#define GF_VICTIM_K SLOT(12)
#define GF_VICTIM_V SLOT(13)
#define GF_CACHE_OVERFLOW SLOT(14)
#ifdef GF_DEBUG
FILE *foo = NULL;
static void gf_stat( obj gf )
{
int i;
obj p, k;
fprintf( foo, "%s status: ",
symbol_text( gvec_ref( gf, SLOT(3) ) ) );
for (i=0; i<4; i++)
{
k = gvec_ref( gf, SLOT(i*2) + GF_CACHE_0_K );
if (truish(k))
fprintf( foo, " %s", symbol_text(class_name(k)) );
else
fprintf( foo, " #f" );
}
fprintf( foo, " |" );
k = gvec_ref( gf, GF_VICTIM_K );
if (truish(k))
fprintf( foo, " %s", symbol_text(class_name(k)) );
else
fprintf( foo, " #f" );
fprintf( foo, " |" );
for (p=gvec_ref(gf,GF_CACHE_OVERFLOW);truish(p);p=gvec_ref(p,0))
{
k = gvec_ref(p,SLOT(1));
fprintf( foo, " %s", symbol_text(class_name(k)) );
}
fprintf( foo, "\n" );
}
#endif /* GF_DEBUG */
obj rs_gf_find_method( obj gf, obj rcvr )
{
obj c, m, impl, h, k;
UINT_32 k_ix, v_ix;
#ifdef GF_DEBUG
if (!foo)
foo = fopen( "/tmp/gf.trc", "w" );
#endif /* GF_DEBUG */
c = object_class(rcvr);
h = FX_AND(gvec_ref(c, CLASS_HASH_CODE),int2fx(3*2));
k_ix = GF_CACHE_0_K + FXWORDS_TO_RIBYTES(h);
v_ix = k_ix + SLOT(1);
k = gvec_ref( gf, k_ix );
#ifdef GF_DEBUG
gf_stat(gf);
#endif /* GF_DEBUG */
if (EQ(k,c))
return gvec_ref( gf, v_ix );
else
{
/* check the victim entry */
k = gvec_ref( gf, GF_VICTIM_K );
if (EQ(k,c))
{
m = gvec_ref( gf, GF_VICTIM_V );
/* a hit -- victimize the primary entry
* (note: there is no way you can hit in the victim cache
* if your primary entry is #f)
*/
gvec_write_ptr( gf, GF_VICTIM_K, gvec_ref( gf, k_ix ) );
gvec_write_ptr( gf, GF_VICTIM_V, gvec_ref( gf, v_ix ) );
gvec_write_ptr( gf, k_ix, k );
gvec_write_ptr( gf, v_ix, m );
return m;
}
else
{
obj ov, new_ov, prev = ZERO;
#ifdef GF_DEBUG
fprintf( foo, "%s: check overflow for key = %s\n",
symbol_text( gvec_ref( gf, SLOT(3) ) ),
symbol_text( class_name( c ) ) );
if (truish( gvec_ref( gf, k_ix ) ))
fprintf( foo, " primary[%d] => %s\n",
fx2int(h),
symbol_text( class_name( gvec_ref( gf, k_ix ) ) ) );
if (truish(k))
fprintf( foo, " victim => %s\n",
symbol_text( class_name( k ) ) );
#endif /* GF_DEBUG */
/* a primary miss -- check the overflow list */
for (ov = gvec_ref( gf, GF_CACHE_OVERFLOW );
!EQ(ov,FALSE_OBJ);
ov = gvec_ref( ov, SLOT(0) ) )
{
k = gvec_ref( ov, SLOT(1) );
#ifdef GF_DEBUG
fprintf( foo, " overflow {%#x} => %s\n", ov,
symbol_text( class_name( k ) ) );
#endif /* GF_DEBUG */
if (EQ(k,c))
{
#ifdef GF_DEBUG
fprintf( foo, " HIT (prev = {%#x})\n", prev );
#endif /* GF_DEBUG */
m = gvec_ref( ov, SLOT(2) );
/* found it in the overflow list... move this entry
* to the primary cache area and spill the victim cache
*/
new_ov = make3( vector_class,
(EQ(prev,ZERO)
? gvec_ref( ov, SLOT(0) )
: gvec_ref( gf, GF_CACHE_OVERFLOW )),
gvec_ref( gf, GF_VICTIM_K ),
gvec_ref( gf, GF_VICTIM_V ) );
gvec_write_ptr( gf, GF_VICTIM_K, gvec_ref( gf, k_ix ) );
gvec_write_ptr( gf, GF_VICTIM_V, gvec_ref( gf, v_ix ) );
if (!EQ(prev,ZERO))
gvec_write( prev, SLOT(0), gvec_ref( ov, SLOT(0) ) );
gvec_write_ptr( gf, GF_CACHE_OVERFLOW, new_ov );
gvec_write_ptr( gf, k_ix, k );
gvec_write_ptr( gf, v_ix, m );
#ifdef GF_DEBUG
gf_stat(gf);
#endif /* GF_DEBUG */
return m;
}
prev = ov;
}
#ifdef GF_DEBUG
fprintf( foo, " MISS\n" );
#endif /* GF_DEBUG */
return FALSE_OBJ;
}
}
}
jump_addr rs_gf_dispatch( obj gf )
{
obj m;
if (arg_count_reg < 1)
{
scheme_error( "GF ~s called with no arguments", 1, gf );
}
m = rs_gf_find_method( gf, REG0 );
if (EQ(m,FALSE_OBJ))
{
/* a miss -- call the fallback function */
COLLECT0();
REG1 = REG0;
REG0 = gf;
arg_count_reg = 2;
return apply( load_cache_and_call_proc );
}
else
{
return apply(m);
}
}
jump_addr applyg( obj gf )
{
/* if the template has been changed, call the full procedure
* (this is for debugging, tracing, and introspection purposes...
* we want to keep the protocol that pretends like the template
* is always called, even if we optimize away that call in certain
* cases
*/
if (!EQ(gvec_ref(gf,SLOT(0)),gf_dispatch_template))
return apply(gf);
else
return rs_gf_dispatch(gf);
}
|