typedef struct {
uint32_t ch_version;
uint16_t ch_index;
uint8_t ch_counter;
uint8_t ch_oparg;
} HotPyVersionAndCounters;
typedef union {
PyObject *object;
HotPyVersionAndCounters version;
} HotPyCacheEntry;
TARGET(LOAD_GLOBAL_ADAPTIVE) {
HotPyCacheEntry *cache = GET_CACHE();
assert(cache->ch_kind == CACHE_KIND_UNUSED);
HotPyVersionAndCounters *version = &cache[0].version;
if (version->ch_counter == 0) {
PyObject *name = GETITEM(names, version->ch_oparg);
next_instr--;
hotpy_adapt_load_global(next_instr, f, name, cache);
assert(!_PyErr_Occurred(tstate));
}
else {
version->ch_counter--;
oparg = cache->ch_oparg;
JUMP_TO_INSTRUCTION(LOAD_GLOBAL);
}
}
TARGET(LOAD_GLOBAL_FROM_MODULE) {
PyObject *v;
HotPyCacheEntry *cache = GET_CACHE();
HotPyVersionAndCounters *version = &cache[0].version;
PyObject *globals = f->f_globals;
MISS_IF(!PyDict_CheckExact(globals), LOAD_GLOBAL);
MISS_IF(((PyDictObject *)globals)->ma_keys->dk_version != version->ch_version, LOAD_GLOBAL);
v = ((PyDictObject *)globals)->ma_values[version->ch_index];
record_cache_hit(cache);
if (v == NULL) {
/* Error -- handle it in LOAD_GLOBAL */
oparg = version->ch_oparg;
JUMP_TO_INSTRUCTION(LOAD_GLOBAL);
}
Py_INCREF(v);
PUSH(v);
}
TARGET(LOAD_GLOBAL_FROM_BUILTIN) {
PyObject *v;
HotPyCacheEntry *cache = GET_CACHE();
HotPyVersionAndCounters *version = &cache[0].version;
PyObject *globals = f->f_globals;
PyObject *builtins = f->f_builtins;
MISS_IF(!PyDict_CheckExact(globals), LOAD_GLOBAL);
MISS_IF(((PyDictObject *)globals)->ma_keys->dk_version != version.ch_version, LOAD_GLOBAL);
MISS_IF(!PyDict_CheckExact(builtins), LOAD_GLOBAL);
MISS_IF(((PyDictObject *)builtins)->ma_keys->dk_version != cache[1].version.ch_version, LOAD_GLOBAL);
v = ((PyDictObject *)builtins)->ma_values[version->ch_index];
if (v == NULL) {
/* Error -- handle it in LOAD_GLOBAL */
oparg = version->ch_oparg;
JUMP_TO_INSTRUCTION(LOAD_GLOBAL);
}
record_cache_hit(cache);
Py_INCREF(v);
PUSH(v);
}
The macro MISS_IF(condition, op)
expands to:
if (UNLIKELY(condition)) goto op ## miss;
And the code for handling misses looks like this:
LOAD_GLOBAL_miss:
HotPyCacheEntry *cache = GET_CACHE();
record_cache_miss(cache);
if (too_many_cache_misses(cache)) {
cache_deopt(next_instr-1, cache, LOAD_GLOBAL);
}
oparg = cache->version.ch_oparg;
JUMP_TO_INSTRUCTION(LOAD_GLOBAL);
}
#define GET_CACHE() (&caches[((next_instr-first_instr)>>1) + oparg])
#define JUMP_TO_INSTRUCTION(op) goto target_ ## op
void record_cache_hit(HotPyCacheEntry *entry) {
saturating_increment(&entry->version.ch_counter);
}
void record_cache_miss(HotPyCacheEntry *entry) {
saturating_decrement(&entry->version.ch_counter);
}
int too_many_cache_misses(HotPyCacheEntry *entry) {
return entry->version.ch_counter == saturating_minimum();
}
void cache_deopt(Instruction *i, HotPyCacheEntry *entry, int opcode) {
i->opcode = opcode;
entry->version.ch_counter = BACKOFF;
i->oparg = entry->version.ch_oparg;
}