mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-06-15 08:32:22 -07:00
first pass at unified evaluation cache
This commit is contained in:
@@ -4819,6 +4819,11 @@ ctrl_thread__eval_scope_begin(Arena *arena, CTRL_Entity *thread)
|
||||
}
|
||||
e_select_interpret_ctx(&scope->interpret_ctx, eval_modules_primary->rdi, thread_rip_voff);
|
||||
|
||||
////////////////////////////
|
||||
//- rjf: begin cached evaluations
|
||||
//
|
||||
e_cache_eval_begin();
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Key Helpers
|
||||
|
||||
internal B32
|
||||
e_key_match(E_Key a, E_Key b)
|
||||
{
|
||||
B32 result = (a.u64 == b.u64);
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Initialization (Required For All Subsequent APIs)
|
||||
|
||||
internal void
|
||||
e_cache_eval_begin(void)
|
||||
{
|
||||
if(e_cache == 0)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
e_cache = push_array(arena, E_Cache, 1);
|
||||
e_cache->arena = arena;
|
||||
e_cache->arena_eval_start_pos = arena_pos(arena);
|
||||
}
|
||||
arena_pop_to(e_cache->arena, e_cache->arena_eval_start_pos);
|
||||
e_cache->key_id_gen = 0;
|
||||
e_cache->key_slots_count = 4096;
|
||||
e_cache->key_slots = push_array(e_cache->arena, E_CacheSlot, e_cache->key_slots_count);
|
||||
e_cache->string_slots_count = 4096;
|
||||
e_cache->string_slots = push_array(e_cache->arena, E_CacheSlot, e_cache->string_slots_count);
|
||||
e_cache->free_parent_node = 0;
|
||||
e_cache->top_parent_node = 0;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Accessing Functions
|
||||
|
||||
//- rjf: parent key stack
|
||||
|
||||
internal E_Key
|
||||
e_parent_key_push(E_Key key)
|
||||
{
|
||||
E_Key top = {0};
|
||||
if(e_cache->top_parent_node != 0)
|
||||
{
|
||||
top = e_cache->top_parent_node->key;
|
||||
}
|
||||
E_CacheParentNode *n = e_cache->free_parent_node;
|
||||
if(n != 0)
|
||||
{
|
||||
SLLStackPop(e_cache->free_parent_node);
|
||||
}
|
||||
else
|
||||
{
|
||||
n = push_array(e_cache->arena, E_CacheParentNode, 1);
|
||||
}
|
||||
SLLStackPush(e_cache->top_parent_node, n);
|
||||
n->key = key;
|
||||
return top;
|
||||
}
|
||||
|
||||
internal E_Key
|
||||
e_parent_key_pop(void)
|
||||
{
|
||||
E_CacheParentNode *n = e_cache->top_parent_node;
|
||||
SLLStackPop(e_cache->top_parent_node);
|
||||
SLLStackPush(e_cache->free_parent_node, n);
|
||||
E_Key popped = n->key;
|
||||
return popped;
|
||||
}
|
||||
|
||||
//- rjf: key construction
|
||||
|
||||
internal E_Key
|
||||
e_key_from_string(String8 string)
|
||||
{
|
||||
E_Key parent_key = {0};
|
||||
if(e_cache->top_parent_node)
|
||||
{
|
||||
parent_key = e_cache->top_parent_node->key;
|
||||
}
|
||||
U64 hash = e_hash_from_string(parent_key.u64, string);
|
||||
U64 slot_idx = hash%e_cache->string_slots_count;
|
||||
E_CacheSlot *slot = &e_cache->string_slots[slot_idx];
|
||||
E_CacheNode *node = 0;
|
||||
for(E_CacheNode *n = slot->first; n != 0; n = n->string_next)
|
||||
{
|
||||
if(e_key_match(parent_key, n->bundle.parent_key) && str8_match(n->bundle.string, string, 0))
|
||||
{
|
||||
node = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(node == 0)
|
||||
{
|
||||
e_cache->key_id_gen += 1;
|
||||
E_Key key = {e_cache->key_id_gen};
|
||||
U64 key_hash = e_hash_from_string(5381, str8_struct(&key));
|
||||
U64 key_slot_idx = key_hash%e_cache->key_slots_count;
|
||||
E_CacheSlot *key_slot = &e_cache->key_slots[key_slot_idx];
|
||||
node = push_array(e_cache->arena, E_CacheNode, 1);
|
||||
node->key = key;
|
||||
SLLQueuePush_N(slot->first, slot->last, node, string_next);
|
||||
SLLQueuePush_N(key_slot->first, key_slot->last, node, key_next);
|
||||
node->bundle.parent_key = parent_key;
|
||||
node->bundle.string = push_str8_copy(e_cache->arena, string);
|
||||
}
|
||||
return node->key;
|
||||
}
|
||||
|
||||
internal E_Key
|
||||
e_key_from_stringf(char *fmt, ...)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
String8 string = push_str8fv(scratch.arena, fmt, args);
|
||||
E_Key result = e_key_from_string(string);
|
||||
va_end(args);
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
//- rjf: base key -> node helper
|
||||
|
||||
internal E_CacheBundle *
|
||||
e_cache_bundle_from_key(E_Key key)
|
||||
{
|
||||
U64 hash = e_hash_from_string(5381, str8_struct(&key));
|
||||
U64 slot_idx = hash%e_cache->key_slots_count;
|
||||
E_CacheSlot *slot = &e_cache->key_slots[slot_idx];
|
||||
E_CacheNode *node = 0;
|
||||
for(E_CacheNode *n = slot->first; n != 0; n = n->key_next)
|
||||
{
|
||||
if(e_key_match(n->key, key))
|
||||
{
|
||||
node = n;
|
||||
break;
|
||||
}
|
||||
}
|
||||
E_CacheBundle *bundle = &e_cache_bundle_nil;
|
||||
if(node != 0)
|
||||
{
|
||||
bundle = &node->bundle;
|
||||
}
|
||||
return bundle;
|
||||
}
|
||||
|
||||
//- rjf: bundle -> pipeline stage outputs
|
||||
|
||||
internal E_Parse
|
||||
e_parse_from_bundle(E_CacheBundle *bundle)
|
||||
{
|
||||
if(bundle != &e_cache_bundle_nil && !(bundle->flags & E_CacheBundleFlag_Parse))
|
||||
{
|
||||
bundle->flags |= E_CacheBundleFlag_Parse;
|
||||
bundle->parse = e_push_parse_from_string(e_cache->arena, bundle->string);
|
||||
}
|
||||
E_Parse parse = bundle->parse;
|
||||
return parse;
|
||||
}
|
||||
|
||||
internal E_IRTreeAndType
|
||||
e_irtree_from_bundle(E_CacheBundle *bundle)
|
||||
{
|
||||
if(bundle != &e_cache_bundle_nil && !(bundle->flags & E_CacheBundleFlag_IRTree))
|
||||
{
|
||||
bundle->flags |= E_CacheBundleFlag_IRTree;
|
||||
E_IRTreeAndType overridden = e_irtree_from_key(bundle->parent_key);
|
||||
E_IRTreeAndType *prev_overridden = e_ir_state->overridden_irtree;
|
||||
e_ir_state->overridden_irtree = &overridden;
|
||||
E_Parse parse = e_parse_from_bundle(bundle);
|
||||
bundle->irtree = e_push_irtree_and_type_from_expr(e_cache->arena, parse.expr);
|
||||
e_ir_state->overridden_irtree = prev_overridden;
|
||||
}
|
||||
E_IRTreeAndType result = bundle->irtree;
|
||||
return result;
|
||||
}
|
||||
|
||||
internal String8
|
||||
e_bytecode_from_bundle(E_CacheBundle *bundle)
|
||||
{
|
||||
if(bundle != &e_cache_bundle_nil && !(bundle->flags & E_CacheBundleFlag_Bytecode))
|
||||
{
|
||||
bundle->flags |= E_CacheBundleFlag_Bytecode;
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
E_IRTreeAndType irtree = e_irtree_from_bundle(bundle);
|
||||
E_OpList oplist = e_oplist_from_irtree(scratch.arena, irtree.root);
|
||||
bundle->bytecode = e_bytecode_from_oplist(e_cache->arena, &oplist);
|
||||
scratch_end(scratch);
|
||||
}
|
||||
String8 result = bundle->bytecode;
|
||||
return result;
|
||||
}
|
||||
|
||||
internal E_Interpretation
|
||||
e_interpretation_from_bundle(E_CacheBundle *bundle)
|
||||
{
|
||||
if(bundle != &e_cache_bundle_nil && !(bundle->flags & E_CacheBundleFlag_Interpret))
|
||||
{
|
||||
bundle->flags |= E_CacheBundleFlag_Interpret;
|
||||
String8 bytecode = e_bytecode_from_bundle(bundle);
|
||||
E_Interpretation interpret = e_interpret(bytecode);
|
||||
bundle->interpretation = interpret;
|
||||
}
|
||||
E_Interpretation interpret = bundle->interpretation;
|
||||
return interpret;
|
||||
}
|
||||
|
||||
//- rjf: comprehensive bundle
|
||||
|
||||
internal E_Eval
|
||||
e_eval_from_bundle(E_CacheBundle *bundle)
|
||||
{
|
||||
E_Eval eval =
|
||||
{
|
||||
.expr = e_parse_from_bundle(bundle).expr,
|
||||
.irtree = e_irtree_from_bundle(bundle),
|
||||
.bytecode = e_bytecode_from_bundle(bundle),
|
||||
};
|
||||
E_Interpretation interpretation = e_interpretation_from_bundle(bundle);
|
||||
eval.code = interpretation.code;
|
||||
eval.value = interpretation.value;
|
||||
eval.space = interpretation.space;
|
||||
return eval;
|
||||
}
|
||||
|
||||
//- rjf: string-based helpers
|
||||
// TODO(rjf): (replace the old bundle APIs here)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Key Extension Functions
|
||||
|
||||
internal E_Key
|
||||
e_key_wrap(E_Key key, String8 string)
|
||||
{
|
||||
e_parent_key_push(key);
|
||||
E_Key result = e_key_from_string(string);
|
||||
e_parent_key_pop();
|
||||
return result;
|
||||
}
|
||||
|
||||
internal E_Key
|
||||
e_key_wrapf(E_Key key, char *fmt, ...)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
String8 string = push_str8fv(scratch.arena, fmt, args);
|
||||
E_Key result = e_key_wrap(key, string);
|
||||
va_end(args);
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef EVAL_CACHE_H
|
||||
#define EVAL_CACHE_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Key Type
|
||||
|
||||
typedef struct E_Key E_Key;
|
||||
struct E_Key
|
||||
{
|
||||
U64 u64;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Types
|
||||
|
||||
typedef U32 E_CacheBundleFlags;
|
||||
enum
|
||||
{
|
||||
E_CacheBundleFlag_Parse = (1<<0),
|
||||
E_CacheBundleFlag_IRTree = (1<<1),
|
||||
E_CacheBundleFlag_Bytecode = (1<<2),
|
||||
E_CacheBundleFlag_Interpret = (1<<3),
|
||||
};
|
||||
|
||||
typedef struct E_CacheBundle E_CacheBundle;
|
||||
struct E_CacheBundle
|
||||
{
|
||||
E_CacheBundleFlags flags;
|
||||
E_Key parent_key;
|
||||
String8 string;
|
||||
E_Parse parse;
|
||||
E_IRTreeAndType irtree;
|
||||
String8 bytecode;
|
||||
E_Interpretation interpretation;
|
||||
};
|
||||
|
||||
typedef struct E_CacheNode E_CacheNode;
|
||||
struct E_CacheNode
|
||||
{
|
||||
E_CacheNode *string_next;
|
||||
E_CacheNode *key_next;
|
||||
E_Key key;
|
||||
E_CacheBundle bundle;
|
||||
};
|
||||
|
||||
typedef struct E_CacheLookup E_CacheLookup;
|
||||
struct E_CacheLookup
|
||||
{
|
||||
E_CacheNode *node;
|
||||
U64 hash;
|
||||
};
|
||||
|
||||
typedef struct E_CacheSlot E_CacheSlot;
|
||||
struct E_CacheSlot
|
||||
{
|
||||
E_CacheNode *first;
|
||||
E_CacheNode *last;
|
||||
};
|
||||
|
||||
typedef struct E_CacheParentNode E_CacheParentNode;
|
||||
struct E_CacheParentNode
|
||||
{
|
||||
E_CacheParentNode *next;
|
||||
E_Key key;
|
||||
};
|
||||
|
||||
typedef struct E_Cache E_Cache;
|
||||
struct E_Cache
|
||||
{
|
||||
Arena *arena;
|
||||
U64 arena_eval_start_pos;
|
||||
U64 key_id_gen;
|
||||
U64 key_slots_count;
|
||||
E_CacheSlot *key_slots;
|
||||
U64 string_slots_count;
|
||||
E_CacheSlot *string_slots;
|
||||
E_CacheParentNode *top_parent_node;
|
||||
E_CacheParentNode *free_parent_node;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
thread_static E_Cache *e_cache = 0;
|
||||
read_only global E_CacheBundle e_cache_bundle_nil = {0, {0}, {0}, {{0}, 0, &e_expr_nil, &e_expr_nil}, {&e_irnode_nil}};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Key Helpers
|
||||
|
||||
internal B32 e_key_match(E_Key a, E_Key b);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Initialization (Required For All Subsequent APIs)
|
||||
|
||||
internal void e_cache_eval_begin(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Cache Accessing Functions
|
||||
//
|
||||
// The cache uses a unique keying mechanism to refer to some evaluation at
|
||||
// many layers of analysis.
|
||||
//
|
||||
// key
|
||||
// ________________________________________________
|
||||
// / / | \
|
||||
// text -> expression -> ir tree and type -> interpretation result
|
||||
//
|
||||
// Each one of these calls refers to one stage in this pipeline. The cache will
|
||||
// only compute what is needed on-demand. If you ask for the full evaluation,
|
||||
// which is a bundle of artifacts at all layers of analysis, then all stages
|
||||
// will be computed.
|
||||
//
|
||||
// One wrinkle here is that the IR tree generation stage is implicitly
|
||||
// parameterized by the "overridden" IR tree - this is to enable "parent
|
||||
// expressions", e.g. `$.x`, or simply `x` assuming `foo` has such a member,
|
||||
// in the context of some struct `foo` evaluates to the same thing as `foo.x`.
|
||||
// So even though the primary API shape is based around singular keys, the
|
||||
// "parent key stack" also implicitly parameterizes all of these (partly
|
||||
// because it is not relevant in 99% of cases).
|
||||
|
||||
//- rjf: parent key stack
|
||||
internal E_Key e_parent_key_push(E_Key key);
|
||||
internal E_Key e_parent_key_pop(void);
|
||||
|
||||
//- rjf: key construction
|
||||
internal E_Key e_key_from_string(String8 string);
|
||||
internal E_Key e_key_from_stringf(char *fmt, ...);
|
||||
|
||||
//- rjf: base key -> bundle helper
|
||||
internal E_CacheBundle *e_cache_bundle_from_key(E_Key key);
|
||||
|
||||
//- rjf: bundle -> pipeline stage outputs
|
||||
internal E_Parse e_parse_from_bundle(E_CacheBundle *bundle);
|
||||
internal E_IRTreeAndType e_irtree_from_bundle(E_CacheBundle *bundle);
|
||||
internal String8 e_bytecode_from_bundle(E_CacheBundle *bundle);
|
||||
internal E_Interpretation e_interpretation_from_bundle(E_CacheBundle *bundle);
|
||||
#define e_parse_from_key(key) e_parse_from_bundle(e_cache_bundle_from_key(key))
|
||||
#define e_irtree_from_key(key) e_irtree_from_bundle(e_cache_bundle_from_key(key))
|
||||
#define e_bytecode_from_key(key) e_bytecode_from_bundle(e_cache_bundle_from_key(key))
|
||||
#define e_interpretation_from_key(key) e_interpretation_from_bundle(e_cache_bundle_from_key(key))
|
||||
|
||||
//- rjf: comprehensive bundle
|
||||
internal E_Eval e_eval_from_bundle(E_CacheBundle *bundle);
|
||||
#define e_eval_from_key(key) e_eval_from_bundle(e_cache_bundle_from_key(key))
|
||||
|
||||
//- rjf: string-based helpers
|
||||
// TODO(rjf): (replace the old bundle APIs here)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Key Extension Functions
|
||||
|
||||
internal E_Key e_key_wrap(E_Key key, String8 string);
|
||||
internal E_Key e_key_wrapf(E_Key key, char *fmt, ...);
|
||||
|
||||
#endif // EVAL_CACHE_H
|
||||
@@ -7,3 +7,4 @@
|
||||
#include "eval/eval_ir.c"
|
||||
#include "eval/eval_interpret.c"
|
||||
#include "eval/eval_bundles.c"
|
||||
#include "eval/eval_cache.c"
|
||||
|
||||
@@ -10,5 +10,6 @@
|
||||
#include "eval/eval_ir.h"
|
||||
#include "eval/eval_interpret.h"
|
||||
#include "eval/eval_bundles.h"
|
||||
#include "eval/eval_cache.h"
|
||||
|
||||
#endif // EVAL_INC_H
|
||||
|
||||
@@ -12460,6 +12460,11 @@ rd_frame(void)
|
||||
}
|
||||
e_select_interpret_ctx(interpret_ctx, eval_modules_primary->rdi, rip_voff);
|
||||
|
||||
////////////////////////////
|
||||
//- rjf: begin cached evaluations
|
||||
//
|
||||
e_cache_eval_begin();
|
||||
|
||||
////////////////////////////
|
||||
//- rjf: autosave if needed
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user