split eval systems, to decouple types/exprs/parsing (debug info dependent), from irization (macro map dependent), from interpretation (completely independent of the former two stages & all their dependencies)

This commit is contained in:
Ryan Fleury
2024-08-06 13:57:48 -07:00
parent c1cea82ae7
commit 5a682e5888
21 changed files with 6753 additions and 6486 deletions
-5603
View File
File diff suppressed because it is too large Load Diff
-661
View File
@@ -1,661 +0,0 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef EVAL_H
#define EVAL_H
////////////////////////////////
//~ rjf: Generated Code
#include "generated/eval.meta.h"
////////////////////////////////
//~ rjf: Messages
typedef enum E_MsgKind
{
E_MsgKind_Null,
E_MsgKind_MalformedInput,
E_MsgKind_MissingInfo,
E_MsgKind_ResolutionFailure,
E_MsgKind_InterpretationError,
E_MsgKind_COUNT
}
E_MsgKind;
typedef struct E_Msg E_Msg;
struct E_Msg
{
E_Msg *next;
E_MsgKind kind;
void *location;
String8 text;
};
typedef struct E_MsgList E_MsgList;
struct E_MsgList
{
E_Msg *first;
E_Msg *last;
E_MsgKind max_kind;
U64 count;
};
////////////////////////////////
//~ rjf: Token Types
typedef enum E_TokenKind
{
E_TokenKind_Null,
E_TokenKind_Identifier,
E_TokenKind_Numeric,
E_TokenKind_StringLiteral,
E_TokenKind_CharLiteral,
E_TokenKind_Symbol,
E_TokenKind_COUNT
}
E_TokenKind;
typedef struct E_Token E_Token;
struct E_Token
{
E_TokenKind kind;
Rng1U64 range;
};
typedef struct E_TokenChunkNode E_TokenChunkNode;
struct E_TokenChunkNode
{
E_TokenChunkNode *next;
E_Token *v;
U64 count;
U64 cap;
};
typedef struct E_TokenChunkList E_TokenChunkList;
struct E_TokenChunkList
{
E_TokenChunkNode *first;
E_TokenChunkNode *last;
U64 node_count;
U64 total_count;
};
typedef struct E_TokenArray E_TokenArray;
struct E_TokenArray
{
E_Token *v;
U64 count;
};
////////////////////////////////
//~ rjf: Implicit Type Graph Key Types
typedef enum E_TypeKeyKind
{
E_TypeKeyKind_Null,
E_TypeKeyKind_Basic,
E_TypeKeyKind_Ext,
E_TypeKeyKind_Cons,
E_TypeKeyKind_Reg,
E_TypeKeyKind_RegAlias,
}
E_TypeKeyKind;
typedef struct E_TypeKey E_TypeKey;
struct E_TypeKey
{
E_TypeKeyKind kind;
U32 u32[3];
// [0] -> E_TypeKind (Basic, Cons, Ext); Architecture (Reg, RegAlias)
// [1] -> Type Index In RDI (Cons, Ext); Code (Reg, RegAlias)
// [2] -> RDI Index (Cons, Ext)
};
typedef struct E_TypeKeyNode E_TypeKeyNode;
struct E_TypeKeyNode
{
E_TypeKeyNode *next;
E_TypeKey v;
};
typedef struct E_TypeKeyList E_TypeKeyList;
struct E_TypeKeyList
{
E_TypeKeyNode *first;
E_TypeKeyNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Full Extracted Type Information Types
typedef enum E_MemberKind
{
E_MemberKind_Null,
E_MemberKind_DataField,
E_MemberKind_StaticData,
E_MemberKind_Method,
E_MemberKind_StaticMethod,
E_MemberKind_VirtualMethod,
E_MemberKind_VTablePtr,
E_MemberKind_Base,
E_MemberKind_VirtualBase,
E_MemberKind_NestedType,
E_MemberKind_Padding,
E_MemberKind_COUNT
}
E_MemberKind;
typedef U32 E_TypeFlags;
enum
{
E_TypeFlag_Const = (1<<0),
E_TypeFlag_Volatile = (1<<1),
};
typedef struct E_Member E_Member;
struct E_Member
{
E_MemberKind kind;
E_TypeKey type_key;
String8 name;
U64 off;
E_TypeKeyList inheritance_key_chain;
};
typedef struct E_MemberNode E_MemberNode;
struct E_MemberNode
{
E_MemberNode *next;
E_Member v;
};
typedef struct E_MemberList E_MemberList;
struct E_MemberList
{
E_MemberNode *first;
E_MemberNode *last;
U64 count;
};
typedef struct E_MemberArray E_MemberArray;
struct E_MemberArray
{
E_Member *v;
U64 count;
};
typedef struct E_EnumVal E_EnumVal;
struct E_EnumVal
{
String8 name;
U64 val;
};
typedef struct E_EnumValArray E_EnumValArray;
struct E_EnumValArray
{
E_EnumVal *v;
U64 count;
};
typedef struct E_Type E_Type;
struct E_Type
{
E_TypeKind kind;
E_TypeFlags flags;
String8 name;
U64 byte_size;
U64 count;
U32 off;
E_TypeKey direct_type_key;
E_TypeKey owner_type_key;
E_TypeKey *param_type_keys;
E_Member *members;
E_EnumVal *enum_vals;
};
////////////////////////////////
//~ rjf: Expression Tree Types
typedef enum E_Mode
{
E_Mode_Null,
E_Mode_Value,
E_Mode_Addr,
E_Mode_Reg,
}
E_Mode;
typedef struct E_Expr E_Expr;
struct E_Expr
{
E_Expr *first;
E_Expr *last;
E_Expr *next;
void *location;
E_ExprKind kind;
E_Mode mode;
E_TypeKey type_key;
U32 u32;
F32 f32;
U64 u64;
F64 f64;
String8 string;
};
////////////////////////////////
//~ rjf: IR Tree Types
typedef struct E_IRNode E_IRNode;
struct E_IRNode
{
E_IRNode *first;
E_IRNode *last;
E_IRNode *next;
RDI_EvalOp op;
String8 bytecode;
U64 u64;
};
typedef struct E_IRTreeAndType E_IRTreeAndType;
struct E_IRTreeAndType
{
E_IRNode *root;
E_TypeKey type_key;
E_Mode mode;
E_MsgList msgs;
};
////////////////////////////////
//~ rjf: Bytecode Operation Types
enum
{
E_IRExtKind_Bytecode = RDI_EvalOp_COUNT,
E_IRExtKind_COUNT
};
typedef struct E_Op E_Op;
struct E_Op
{
E_Op *next;
RDI_EvalOp opcode;
union
{
U64 p;
String8 bytecode;
};
};
typedef struct E_OpList E_OpList;
struct E_OpList
{
E_Op *first;
E_Op *last;
U64 op_count;
U64 encoded_size;
};
////////////////////////////////
//~ rjf: Map Types
//- rjf: string -> num
typedef struct E_String2NumMapNode E_String2NumMapNode;
struct E_String2NumMapNode
{
E_String2NumMapNode *order_next;
E_String2NumMapNode *hash_next;
String8 string;
U64 num;
};
typedef struct E_String2NumMapNodeArray E_String2NumMapNodeArray;
struct E_String2NumMapNodeArray
{
E_String2NumMapNode **v;
U64 count;
};
typedef struct E_String2NumMapSlot E_String2NumMapSlot;
struct E_String2NumMapSlot
{
E_String2NumMapNode *first;
E_String2NumMapNode *last;
};
typedef struct E_String2NumMap E_String2NumMap;
struct E_String2NumMap
{
U64 slots_count;
U64 node_count;
E_String2NumMapSlot *slots;
E_String2NumMapNode *first;
E_String2NumMapNode *last;
};
//- rjf: string -> expr
typedef struct E_String2ExprMapNode E_String2ExprMapNode;
struct E_String2ExprMapNode
{
E_String2ExprMapNode *hash_next;
String8 string;
E_Expr *expr;
U64 poison_count;
};
typedef struct E_String2ExprMapSlot E_String2ExprMapSlot;
struct E_String2ExprMapSlot
{
E_String2ExprMapNode *first;
E_String2ExprMapNode *last;
};
typedef struct E_String2ExprMap E_String2ExprMap;
struct E_String2ExprMap
{
U64 slots_count;
E_String2ExprMapSlot *slots;
};
////////////////////////////////
//~ rjf: Evaluation Context
typedef struct E_ConsTypeNode E_ConsTypeNode;
struct E_ConsTypeNode
{
E_ConsTypeNode *key_next;
E_ConsTypeNode *content_next;
E_TypeKey key;
E_TypeKey direct_key;
U64 u64;
};
typedef struct E_ConsTypeSlot E_ConsTypeSlot;
struct E_ConsTypeSlot
{
E_ConsTypeNode *first;
E_ConsTypeNode *last;
};
typedef B32 E_MemoryReadFunction(void *user_data, void *out, Rng1U64 vaddr_range);
typedef struct E_Ctx E_Ctx;
struct E_Ctx
{
// rjf: architecture
Architecture arch;
// rjf: instruction pointer info
U64 ip_vaddr;
U64 ip_voff; // (within module, which uses `rdis[0]` for debug info)
// rjf: debug info
RDI_Parsed **rdis;
Rng1U64 *rdis_vaddr_ranges;
U64 rdis_count;
// rjf: identifier resolution maps
E_String2NumMap *regs_map;
E_String2NumMap *reg_alias_map;
E_String2NumMap *locals_map; // (within `rdis[0]`)
E_String2NumMap *member_map; // (within `rdis[0]`)
E_String2ExprMap *macro_map;
// rjf: interpretation environment info
void *memory_read_user_data;
E_MemoryReadFunction *memory_read;
void *reg_data;
U64 reg_size;
U64 *module_base;
U64 *frame_base;
U64 *tls_base;
};
typedef struct E_State E_State;
struct E_State
{
Arena *arena;
U64 arena_eval_start_pos;
// rjf: evaluation context
E_Ctx *ctx;
// rjf: JIT-constructed types tables
U64 cons_id_gen;
U64 cons_content_slots_count;
U64 cons_key_slots_count;
E_ConsTypeSlot *cons_content_slots;
E_ConsTypeSlot *cons_key_slots;
};
////////////////////////////////
//~ rjf: Parse Results
typedef struct E_Parse E_Parse;
struct E_Parse
{
E_Token *last_token;
E_Expr *expr;
E_MsgList msgs;
};
////////////////////////////////
//~ rjf: Bytecode Interpretation Types
typedef union E_Value E_Value;
union E_Value
{
U64 u512[8];
U64 u256[4];
U64 u128[2];
U64 u64;
S64 s64;
F64 f64;
F32 f32;
};
typedef struct E_Interpretation E_Interpretation;
struct E_Interpretation
{
E_Value value;
E_InterpretationCode code;
};
////////////////////////////////
//~ rjf: Bundled Evaluation Path Types
typedef struct E_Eval E_Eval;
struct E_Eval
{
E_Value value;
E_Mode mode;
E_TypeKey type_key;
E_InterpretationCode code;
E_MsgList msgs;
};
////////////////////////////////
//~ rjf: Globals
global read_only E_String2NumMap e_string2num_map_nil = {0};
global read_only E_String2ExprMap e_string2expr_map_nil = {0};
global read_only E_Expr e_expr_nil = {&e_expr_nil, &e_expr_nil, &e_expr_nil};
global read_only E_Type e_type_nil = {E_TypeKind_Null};
global read_only E_IRNode e_irnode_nil = {&e_irnode_nil, &e_irnode_nil, &e_irnode_nil};
thread_static E_State *e_state = 0;
////////////////////////////////
//~ rjf: Basic Helper Functions
internal U64 e_hash_from_string(String8 string);
////////////////////////////////
//~ rjf: Expr Kind Enum Functions
internal RDI_EvalOp e_opcode_from_expr_kind(E_ExprKind kind);
internal B32 e_expr_kind_is_comparison(E_ExprKind kind);
////////////////////////////////
//~ rjf: Type Kind Enum Functions
internal E_TypeKind e_type_kind_from_rdi(RDI_TypeKind kind);
internal E_MemberKind e_member_kind_from_rdi(RDI_MemberKind kind);
internal RDI_EvalTypeGroup e_type_group_from_kind(E_TypeKind kind);
internal B32 e_type_kind_is_integer(E_TypeKind kind);
internal B32 e_type_kind_is_signed(E_TypeKind kind);
internal B32 e_type_kind_is_basic_or_enum(E_TypeKind kind);
////////////////////////////////
//~ rjf: Message Functions
internal void e_msg(Arena *arena, E_MsgList *msgs, E_MsgKind kind, void *location, String8 text);
internal void e_msgf(Arena *arena, E_MsgList *msgs, E_MsgKind kind, void *location, char *fmt, ...);
internal void e_msg_list_concat_in_place(E_MsgList *dst, E_MsgList *to_push);
////////////////////////////////
//~ rjf: Basic Map Functions
//- rjf: string -> num
internal E_String2NumMap e_string2num_map_make(Arena *arena, U64 slot_count);
internal void e_string2num_map_insert(Arena *arena, E_String2NumMap *map, String8 string, U64 num);
internal U64 e_num_from_string(E_String2NumMap *map, String8 string);
internal E_String2NumMapNodeArray e_string2num_map_node_array_from_map(Arena *arena, E_String2NumMap *map);
internal int e_string2num_map_node_qsort_compare__num_ascending(E_String2NumMapNode **a, E_String2NumMapNode **b);
internal void e_string2num_map_node_array_sort__in_place(E_String2NumMapNodeArray *array);
//- rjf: string -> expr
internal E_String2ExprMap e_string2expr_map_make(Arena *arena, U64 slot_count);
internal void e_string2expr_map_insert(Arena *arena, E_String2ExprMap *map, String8 string, E_Expr *expr);
internal void e_string2expr_map_inc_poison(E_String2ExprMap *map, String8 string);
internal void e_string2expr_map_dec_poison(E_String2ExprMap *map, String8 string);
internal E_Expr *e_expr_from_string(E_String2ExprMap *map, String8 string);
////////////////////////////////
//~ rjf: Debug-Info-Driven Map Building Functions
internal E_String2NumMap *e_push_locals_map_from_rdi_voff(Arena *arena, RDI_Parsed *rdi, U64 voff);
internal E_String2NumMap *e_push_member_map_from_rdi_voff(Arena *arena, RDI_Parsed *rdi, U64 voff);
////////////////////////////////
//~ rjf: Tokenization Functions
#define e_token_at_it(it, arr) (((it) < (arr)->v+(arr)->count) ? (*(it)) : e_token_zero())
internal E_Token e_token_zero(void);
internal void e_token_chunk_list_push(Arena *arena, E_TokenChunkList *list, U64 chunk_size, E_Token *token);
internal E_TokenArray e_token_array_from_chunk_list(Arena *arena, E_TokenChunkList *list);
internal E_TokenArray e_token_array_from_text(Arena *arena, String8 text);
internal E_TokenArray e_token_array_make_first_opl(E_Token *first, E_Token *opl);
////////////////////////////////
//~ rjf: Expression Tree Building Functions
internal E_Expr *e_push_expr(Arena *arena, E_ExprKind kind, void *location);
internal void e_expr_push_child(E_Expr *parent, E_Expr *child);
////////////////////////////////
//~ rjf: Context Selection Functions (Selection Required For All Subsequent APIs)
internal E_Ctx *e_ctx(void);
internal void e_select_ctx(E_Ctx *ctx);
internal U32 e_idx_from_rdi(RDI_Parsed *rdi);
////////////////////////////////
//~ rjf: Type Operation Functions
//- rjf: key constructors
internal E_TypeKey e_type_key_zero(void);
internal E_TypeKey e_type_key_basic(E_TypeKind kind);
internal E_TypeKey e_type_key_ext(E_TypeKind kind, U32 type_idx, U32 rdi_idx);
internal E_TypeKey e_type_key_reg(Architecture arch, REGS_RegCode code);
internal E_TypeKey e_type_key_reg_alias(Architecture arch, REGS_AliasCode code);
internal E_TypeKey e_type_key_cons(E_TypeKind kind, E_TypeKey direct_key, U64 u64);
//- rjf: basic type key functions
internal B32 e_type_key_match(E_TypeKey l, E_TypeKey r);
//- rjf: key -> info extraction
internal E_TypeKind e_type_kind_from_key(E_TypeKey key);
internal U64 e_type_byte_size_from_key(E_TypeKey key);
internal E_Type *e_type_from_key(Arena *arena, E_TypeKey key);
internal E_TypeKey e_type_direct_from_key(E_TypeKey key);
internal E_TypeKey e_type_owner_from_key(E_TypeKey key);
internal E_TypeKey e_type_ptee_from_key(E_TypeKey key);
internal E_TypeKey e_type_unwrap_enum(E_TypeKey key);
internal E_TypeKey e_type_unwrap(E_TypeKey key);
internal E_TypeKey e_type_promote(E_TypeKey key);
internal B32 e_type_match(E_TypeKey l, E_TypeKey r);
internal E_Member *e_type_member_copy(Arena *arena, E_Member *src);
internal int e_type_qsort_compare_members_offset(E_Member *a, E_Member *b);
internal E_MemberArray e_type_data_members_from_key(Arena *arena, E_TypeKey key);
internal void e_type_lhs_string_from_key(Arena *arena, E_TypeKey key, String8List *out, U32 prec, B32 skip_return);
internal void e_type_rhs_string_from_key(Arena *arena, E_TypeKey key, String8List *out, U32 prec);
internal String8 e_type_string_from_key(Arena *arena, E_TypeKey key);
//- rjf: type key data structures
internal void e_type_key_list_push(Arena *arena, E_TypeKeyList *list, E_TypeKey key);
internal E_TypeKeyList e_type_key_list_copy(Arena *arena, E_TypeKeyList *src);
////////////////////////////////
//~ rjf: Parsing Functions
internal E_TypeKey e_leaf_type_from_name(String8 name);
internal E_TypeKey e_type_from_expr(E_Expr *expr);
internal void e_push_leaf_ident_exprs_from_expr__in_place(Arena *arena, E_String2ExprMap *map, E_Expr *expr);
internal E_Parse e_parse_type_from_text_tokens(Arena *arena, String8 text, E_TokenArray *tokens);
internal E_Parse e_parse_expr_from_text_tokens__prec(Arena *arena, String8 text, E_TokenArray *tokens, S64 max_precedence);
internal E_Parse e_parse_expr_from_text_tokens(Arena *arena, String8 text, E_TokenArray *tokens);
////////////////////////////////
//~ rjf: IR-ization Functions
//- rjf: op list functions
internal void e_oplist_push_op(Arena *arena, E_OpList *list, RDI_EvalOp opcode, U64 p);
internal void e_oplist_push_uconst(Arena *arena, E_OpList *list, U64 x);
internal void e_oplist_push_sconst(Arena *arena, E_OpList *list, S64 x);
internal void e_oplist_push_bytecode(Arena *arena, E_OpList *list, String8 bytecode);
internal void e_oplist_concat_in_place(E_OpList *dst, E_OpList *to_push);
//- rjf: ir tree core building helpers
internal E_IRNode *e_push_irnode(Arena *arena, RDI_EvalOp op);
internal void e_irnode_push_child(E_IRNode *parent, E_IRNode *child);
//- rjf: ir subtree building helpers
internal E_IRNode *e_irtree_const_u(Arena *arena, U64 v);
internal E_IRNode *e_irtree_unary_op(Arena *arena, RDI_EvalOp op, RDI_EvalTypeGroup group, E_IRNode *c);
internal E_IRNode *e_irtree_binary_op(Arena *arena, RDI_EvalOp op, RDI_EvalTypeGroup group, E_IRNode *l, E_IRNode *r);
internal E_IRNode *e_irtree_binary_op_u(Arena *arena, RDI_EvalOp op, E_IRNode *l, E_IRNode *r);
internal E_IRNode *e_irtree_conditional(Arena *arena, E_IRNode *c, E_IRNode *l, E_IRNode *r);
internal E_IRNode *e_irtree_bytecode_no_copy(Arena *arena, String8 bytecode);
internal E_IRNode *e_irtree_string_literal(Arena *arena, String8 string);
internal E_IRNode *e_irtree_mem_read_type(Arena *arena, E_IRNode *c, E_TypeKey type_key);
internal E_IRNode *e_irtree_convert_lo(Arena *arena, E_IRNode *c, RDI_EvalTypeGroup out, RDI_EvalTypeGroup in);
internal E_IRNode *e_irtree_trunc(Arena *arena, E_IRNode *c, E_TypeKey type_key);
internal E_IRNode *e_irtree_convert_hi(Arena *arena, E_IRNode *c, E_TypeKey out, E_TypeKey in);
internal E_IRNode *e_irtree_resolve_to_value(Arena *arena, E_Mode from_mode, E_IRNode *tree, E_TypeKey type_key);
//- rjf: top-level irtree/type extraction
internal E_IRTreeAndType e_irtree_and_type_from_expr(Arena *arena, E_Expr *expr);
//- rjf: irtree -> linear ops/bytecode
internal void e_append_oplist_from_irtree(Arena *arena, E_IRNode *root, E_OpList *out);
internal E_OpList e_oplist_from_irtree(Arena *arena, E_IRNode *root);
internal String8 e_bytecode_from_oplist(Arena *arena, E_OpList *oplist);
////////////////////////////////
//~ rjf: Interpretation Functions
internal E_Interpretation e_interpret(String8 bytecode);
////////////////////////////////
//~ rjf: Bundled Evaluation Functions
internal E_Eval e_eval_from_string(Arena *arena, String8 string);
internal E_Eval e_autoresolved_eval_from_eval(E_Eval eval);
internal E_Eval e_dynamically_typed_eval_from_eval(E_Eval eval);
internal E_Eval e_value_eval_from_eval(E_Eval eval);
#endif // EVAL_H
+202
View File
@@ -0,0 +1,202 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Bundled Evaluation Functions
internal E_Eval
e_eval_from_string(Arena *arena, String8 string)
{
E_TokenArray tokens = e_token_array_from_text(arena, string);
E_Parse parse = e_parse_expr_from_text_tokens(arena, string, &tokens);
E_IRTreeAndType irtree = e_irtree_and_type_from_expr(arena, parse.expr);
E_OpList oplist = e_oplist_from_irtree(arena, irtree.root);
String8 bytecode = e_bytecode_from_oplist(arena, &oplist);
E_Interpretation interp = e_interpret(bytecode);
E_Eval eval =
{
.value = interp.value,
.mode = irtree.mode,
.type_key = irtree.type_key,
.code = interp.code,
};
e_msg_list_concat_in_place(&eval.msgs, &parse.msgs);
e_msg_list_concat_in_place(&eval.msgs, &irtree.msgs);
if(E_InterpretationCode_Good < eval.code && eval.code < E_InterpretationCode_COUNT)
{
e_msg(arena, &eval.msgs, E_MsgKind_InterpretationError, 0, e_interpretation_code_display_strings[eval.code]);
}
return eval;
}
internal E_Eval
e_autoresolved_eval_from_eval(E_Eval eval)
{
if(e_parse_ctx &&
e_interpret_ctx &&
e_parse_ctx->rdis_count > 0 &&
e_interpret_ctx->module_base != 0 &&
(eval.mode == E_Mode_Value || eval.mode == E_Mode_Reg) &&
(e_type_key_match(eval.type_key, e_type_key_basic(E_TypeKind_S64)) ||
e_type_key_match(eval.type_key, e_type_key_basic(E_TypeKind_U64)) ||
e_type_key_match(eval.type_key, e_type_key_basic(E_TypeKind_S32)) ||
e_type_key_match(eval.type_key, e_type_key_basic(E_TypeKind_U32))))
{
U64 vaddr = eval.value.u64;
U64 voff = vaddr - e_interpret_ctx->module_base[0];
RDI_Parsed *rdi = e_parse_ctx->rdi_primary;
RDI_Scope *scope = rdi_scope_from_voff(rdi, voff);
RDI_Procedure *procedure = rdi_procedure_from_voff(rdi, voff);
RDI_GlobalVariable *gvar = rdi_global_variable_from_voff(rdi, voff);
U32 string_idx = 0;
if(string_idx == 0) { string_idx = procedure->name_string_idx; }
if(string_idx == 0) { string_idx = gvar->name_string_idx; }
if(string_idx != 0)
{
eval.type_key = e_type_key_cons(E_TypeKind_Ptr, e_type_key_basic(E_TypeKind_Void), 0);
}
}
return eval;
}
internal E_Eval
e_dynamically_typed_eval_from_eval(E_Eval eval)
{
E_TypeKey type_key = eval.type_key;
E_TypeKind type_kind = e_type_kind_from_key(type_key);
if(e_type_state != 0 &&
e_interpret_ctx != 0 &&
e_interpret_ctx->memory_read != 0 &&
e_interpret_ctx->module_base != 0 &&
type_kind == E_TypeKind_Ptr)
{
Temp scratch = scratch_begin(0, 0);
E_TypeKey ptee_type_key = e_type_unwrap(e_type_direct_from_key(e_type_unwrap(type_key)));
E_TypeKind ptee_type_kind = e_type_kind_from_key(ptee_type_key);
if(ptee_type_kind == E_TypeKind_Struct || ptee_type_kind == E_TypeKind_Class)
{
E_Type *ptee_type = e_type_from_key(scratch.arena, ptee_type_key);
B32 has_vtable = 0;
for(U64 idx = 0; idx < ptee_type->count; idx += 1)
{
if(ptee_type->members[idx].kind == E_MemberKind_VirtualMethod)
{
has_vtable = 1;
break;
}
}
if(has_vtable)
{
U64 ptr_vaddr = eval.value.u64;
U64 addr_size = bit_size_from_arch(e_interpret_ctx->arch)/8;
U64 class_base_vaddr = 0;
U64 vtable_vaddr = 0;
if(e_interpret_ctx->memory_read(e_interpret_ctx->memory_read_user_data, &class_base_vaddr, r1u64(ptr_vaddr, ptr_vaddr+addr_size)) &&
e_interpret_ctx->memory_read(e_interpret_ctx->memory_read_user_data, &vtable_vaddr, r1u64(class_base_vaddr, class_base_vaddr+addr_size)))
{
U32 rdi_idx = 0;
RDI_Parsed *rdi = 0;
U64 module_base = 0;
for(U64 idx = 0; idx < e_type_state->ctx->rdis_count; idx += 1)
{
if(contains_1u64(e_type_state->ctx->rdis_vaddr_ranges[idx], vtable_vaddr))
{
rdi_idx = (U32)idx;
rdi = e_type_state->ctx->rdis[idx];
module_base = e_type_state->ctx->rdis_vaddr_ranges[idx].min;
break;
}
}
if(rdi != 0)
{
U64 vtable_voff = vtable_vaddr - module_base;
U64 global_idx = rdi_vmap_idx_from_section_kind_voff(rdi, RDI_SectionKind_GlobalVMap, vtable_voff);
RDI_GlobalVariable *global_var = rdi_element_from_name_idx(rdi, GlobalVariables, global_idx);
if(global_var->link_flags & RDI_LinkFlag_TypeScoped)
{
RDI_UDT *udt = rdi_element_from_name_idx(rdi, UDTs, global_var->container_idx);
RDI_TypeNode *type = rdi_element_from_name_idx(rdi, TypeNodes, udt->self_type_idx);
E_TypeKey derived_type_key = e_type_key_ext(e_type_kind_from_rdi(type->kind), udt->self_type_idx, rdi_idx);
E_TypeKey ptr_to_derived_type_key = e_type_key_cons(E_TypeKind_Ptr, derived_type_key, 0);
eval.type_key = ptr_to_derived_type_key;
}
}
}
}
}
scratch_end(scratch);
}
return eval;
}
internal E_Eval
e_value_eval_from_eval(E_Eval eval)
{
switch(eval.mode)
{
//- rjf: no work to be done. already in value mode
default:
case E_Mode_Value:{}break;
//- rjf: address => resolve into value, if leaf
case E_Mode_Addr:
{
E_TypeKey type_key = e_type_unwrap(eval.type_key);
E_TypeKind type_kind = e_type_kind_from_key(type_key);
if(type_kind == E_TypeKind_Array)
{
eval.mode = E_Mode_Value;
}
else if(e_interpret_ctx->memory_read != 0)
{
U64 type_byte_size = e_type_byte_size_from_key(type_key);
Rng1U64 value_vaddr_range = r1u64(eval.value.u64, eval.value.u64 + type_byte_size);
MemoryZeroStruct(&eval.value);
if(!e_type_key_match(type_key, e_type_key_zero()) &&
type_byte_size <= sizeof(E_Value) &&
e_interpret_ctx->memory_read(e_interpret_ctx->memory_read_user_data, &eval.value, value_vaddr_range))
{
eval.mode = E_Mode_Value;
// rjf: mask&shift, for bitfields
if(type_kind == E_TypeKind_Bitfield && type_byte_size <= sizeof(U64))
{
Temp scratch = scratch_begin(0, 0);
E_Type *type = e_type_from_key(scratch.arena, type_key);
U64 valid_bits_mask = 0;
for(U64 idx = 0; idx < type->count; idx += 1)
{
valid_bits_mask |= (1<<idx);
}
eval.value.u64 = eval.value.u64 >> type->off;
eval.value.u64 = eval.value.u64 & valid_bits_mask;
eval.type_key = type->direct_type_key;
scratch_end(scratch);
}
// rjf: manually sign-extend
switch(type_kind)
{
default: break;
case E_TypeKind_S8: {eval.value.s64 = (S64)*((S8 *)&eval.value.u64);}break;
case E_TypeKind_S16: {eval.value.s64 = (S64)*((S16 *)&eval.value.u64);}break;
case E_TypeKind_S32: {eval.value.s64 = (S64)*((S32 *)&eval.value.u64);}break;
}
}
}
}break;
//- rjf: register => resolve into value
case E_Mode_Reg:
{
E_TypeKey type_key = eval.type_key;
U64 type_byte_size = e_type_byte_size_from_key(type_key);
U64 reg_off = eval.value.u64;
MemoryZeroStruct(&eval.value);
MemoryCopy(&eval.value.u256, ((U8 *)e_interpret_ctx->reg_data + reg_off), Min(type_byte_size, sizeof(U64)*4));
eval.mode = E_Mode_Value;
}break;
}
return eval;
}
+28
View File
@@ -0,0 +1,28 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef EVAL_BUNDLES_H
#define EVAL_BUNDLES_H
////////////////////////////////
//~ rjf: Bundled Evaluation Path Types
typedef struct E_Eval E_Eval;
struct E_Eval
{
E_Value value;
E_Mode mode;
E_TypeKey type_key;
E_InterpretationCode code;
E_MsgList msgs;
};
////////////////////////////////
//~ rjf: Bundled Evaluation Functions
internal E_Eval e_eval_from_string(Arena *arena, String8 string);
internal E_Eval e_autoresolved_eval_from_eval(E_Eval eval);
internal E_Eval e_dynamically_typed_eval_from_eval(E_Eval eval);
internal E_Eval e_value_eval_from_eval(E_Eval eval);
#endif // EVAL_BUNDLES_H
+9
View File
@@ -0,0 +1,9 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#include "eval/generated/eval.meta.c"
#include "eval/eval_types.c"
#include "eval/eval_parse.c"
#include "eval/eval_ir.c"
#include "eval/eval_interpret.c"
#include "eval/eval_bundles.c"
+14
View File
@@ -0,0 +1,14 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef EVAL_INC_H
#define EVAL_INC_H
#include "eval/generated/eval.meta.h"
#include "eval/eval_types.h"
#include "eval/eval_parse.h"
#include "eval/eval_ir.h"
#include "eval/eval_interpret.h"
#include "eval/eval_bundles.h"
#endif // EVAL_INC_H
+754
View File
@@ -0,0 +1,754 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
////////////////////////////////
//~ rjf: Context Selection Functions (Selection Required For All Subsequent APIs)
internal E_InterpretCtx *
e_selected_interpret_ctx(void)
{
return e_interpret_ctx;
}
internal void
e_select_interpret_ctx(E_InterpretCtx *ctx)
{
e_interpret_ctx = ctx;
}
////////////////////////////////
//~ rjf: Interpretation Functions
internal E_Interpretation
e_interpret(String8 bytecode)
{
E_Interpretation result = {0};
Temp scratch = scratch_begin(0, 0);
//- rjf: allocate stack
U64 stack_cap = 128; // TODO(rjf): scan bytecode; determine maximum stack depth
E_Value *stack = push_array_no_zero(scratch.arena, E_Value, stack_cap);
U64 stack_count = 0;
//- rjf: iterate bytecode & perform ops
U8 *ptr = bytecode.str;
U8 *opl = bytecode.str + bytecode.size;
for(;ptr < opl;)
{
// rjf: consume next opcode
RDI_EvalOp op = (RDI_EvalOp)*ptr;
if(op >= RDI_EvalOp_COUNT)
{
result.code = E_InterpretationCode_BadOp;
goto done;
}
U8 ctrlbits = rdi_eval_op_ctrlbits_table[op];
ptr += 1;
// rjf: decode
U64 imm = 0;
{
U32 decode_size = RDI_DECODEN_FROM_CTRLBITS(ctrlbits);
U8 *next_ptr = ptr + decode_size;
if(next_ptr > opl)
{
result.code = E_InterpretationCode_BadOp;
goto done;
}
// TODO(rjf): guarantee 8 bytes padding after the end of serialized
// bytecode; read 8 bytes and mask
switch(decode_size)
{
case 1:{imm = *ptr;}break;
case 2:{imm = *(U16*)ptr;}break;
case 4:{imm = *(U32*)ptr;}break;
case 8:{imm = *(U64*)ptr;}break;
}
ptr = next_ptr;
}
// rjf: pop
E_Value *svals = 0;
{
U32 pop_count = RDI_POPN_FROM_CTRLBITS(ctrlbits);
if(pop_count > stack_count)
{
result.code = E_InterpretationCode_BadOp;
goto done;
}
if(pop_count <= stack_count)
{
stack_count -= pop_count;
svals = stack + stack_count;
}
}
// rjf: interpret op, given decodes/pops
E_Value nval = {0};
switch(op)
{
case RDI_EvalOp_Stop:
{
goto done;
}break;
case RDI_EvalOp_Noop:
{
// do nothing
}break;
case RDI_EvalOp_Cond:
if(svals[0].u64)
{
ptr += imm;
}break;
case RDI_EvalOp_Skip:
{
ptr += imm;
}break;
case RDI_EvalOp_MemRead:
{
U64 addr = svals[0].u64;
U64 size = imm;
B32 good_read = 0;
if(e_interpret_ctx->memory_read != 0 && e_interpret_ctx->memory_read(e_interpret_ctx->memory_read_user_data, &nval, r1u64(addr, addr+size)))
{
good_read = 1;
}
if(!good_read)
{
result.code = E_InterpretationCode_BadMemRead;
goto done;
}
}break;
case RDI_EvalOp_RegRead:
{
U8 rdi_reg_code = (imm&0x0000FF)>>0;
U8 byte_size = (imm&0x00FF00)>>8;
U8 byte_off = (imm&0xFF0000)>>16;
REGS_RegCode base_reg_code = regs_reg_code_from_arch_rdi_code(e_interpret_ctx->arch, rdi_reg_code);
REGS_Rng rng = regs_reg_code_rng_table_from_architecture(e_interpret_ctx->arch)[base_reg_code];
U64 off = (U64)rng.byte_off + byte_off;
U64 size = (U64)byte_size;
if(off + size <= e_interpret_ctx->reg_size)
{
MemoryCopy(&nval, (U8*)e_interpret_ctx->reg_data + off, size);
}
else
{
result.code = E_InterpretationCode_BadRegRead;
goto done;
}
}break;
case RDI_EvalOp_RegReadDyn:
{
U64 off = svals[0].u64;
U64 size = bit_size_from_arch(e_interpret_ctx->arch)/8;
if(off + size <= e_interpret_ctx->reg_size)
{
MemoryCopy(&nval, (U8*)e_interpret_ctx->reg_data + off, size);
}
else
{
result.code = E_InterpretationCode_BadRegRead;
goto done;
}
}break;
case RDI_EvalOp_FrameOff:
{
if(e_interpret_ctx->frame_base != 0)
{
nval.u64 = *e_interpret_ctx->frame_base + imm;
}
else
{
result.code = E_InterpretationCode_BadFrameBase;
goto done;
}
}break;
case RDI_EvalOp_ModuleOff:
{
if(e_interpret_ctx->module_base != 0)
{
nval.u64 = *e_interpret_ctx->module_base + imm;
}
else
{
result.code = E_InterpretationCode_BadModuleBase;
goto done;
}
}break;
case RDI_EvalOp_TLSOff:
{
if(e_interpret_ctx->tls_base != 0)
{
nval.u64 = *e_interpret_ctx->tls_base + imm;
}
else
{
result.code = E_InterpretationCode_BadTLSBase;
goto done;
}
}break;
case RDI_EvalOp_ConstU8:
case RDI_EvalOp_ConstU16:
case RDI_EvalOp_ConstU32:
case RDI_EvalOp_ConstU64:
{
nval.u64 = imm;
}break;
case RDI_EvalOp_Abs:
{
if(imm == RDI_EvalTypeGroup_F32)
{
nval.f32 = svals[0].f32;
if(svals[0].f32 < 0)
{
nval.f32 = -svals[0].f32;
}
}
else if(imm == RDI_EvalTypeGroup_F64)
{
nval.f64 = svals[0].f64;
if(svals[0].f64 < 0)
{
nval.f64 = -svals[0].f64;
}
}
else
{
nval.s64 = svals[0].s64;
if(svals[0].s64 < 0)
{
nval.s64 = -svals[0].s64;
}
}
}break;
case RDI_EvalOp_Neg:
{
if(imm == RDI_EvalTypeGroup_F32)
{
nval.f32 = -svals[0].f32;
}
else if(imm == RDI_EvalTypeGroup_F64)
{
nval.f64 = -svals[0].f64;
}
else
{
nval.u64 = (~svals[0].u64) + 1;
}
}break;
case RDI_EvalOp_Add:
{
if(imm == RDI_EvalTypeGroup_F32)
{
nval.f32 = svals[0].f32 + svals[1].f32;
}
else if(imm == RDI_EvalTypeGroup_F64)
{
nval.f64 = svals[0].f64 + svals[1].f64;
}
else
{
nval.u64 = svals[0].u64 + svals[1].u64;
}
}break;
case RDI_EvalOp_Sub:
{
if(imm == RDI_EvalTypeGroup_F32)
{
nval.f32 = svals[0].f32 - svals[1].f32;
}
else if(imm == RDI_EvalTypeGroup_F64)
{
nval.f64 = svals[0].f64 - svals[1].f64;
}
else
{
nval.u64 = svals[0].u64 - svals[1].u64;
}
}break;
case RDI_EvalOp_Mul:
{
if(imm == RDI_EvalTypeGroup_F32)
{
nval.f32 = svals[0].f32*svals[1].f32;
}
else if(imm == RDI_EvalTypeGroup_F64)
{
nval.f64 = svals[0].f64*svals[1].f64;
}
else
{
nval.u64 = svals[0].u64*svals[1].u64;
}
}break;
case RDI_EvalOp_Div:
{
if(imm == RDI_EvalTypeGroup_F32)
{
if(svals[1].f32 != 0.f)
{
nval.f32 = svals[0].f32/svals[1].f32;
}
else
{
result.code = E_InterpretationCode_DivideByZero;
goto done;
}
}
else if(imm == RDI_EvalTypeGroup_F64)
{
if(svals[1].f64 != 0.)
{
nval.f64 = svals[0].f64/svals[1].f64;
}
else
{
result.code = E_InterpretationCode_DivideByZero;
goto done;
}
}
else if(imm == RDI_EvalTypeGroup_U ||
imm == RDI_EvalTypeGroup_S)
{
if(svals[1].u64 != 0)
{
nval.u64 = svals[0].u64/svals[1].u64;
}
else
{
result.code = E_InterpretationCode_DivideByZero;
goto done;
}
}
else
{
result.code = E_InterpretationCode_BadOpTypes;
goto done;
}
}break;
case RDI_EvalOp_Mod:
{
if(imm == RDI_EvalTypeGroup_U ||
imm == RDI_EvalTypeGroup_S)
{
if(svals[1].u64 != 0)
{
nval.u64 = svals[0].u64%svals[1].u64;
}
}
else
{
result.code = E_InterpretationCode_BadOpTypes;
goto done;
}
}break;
case RDI_EvalOp_LShift:
{
if(imm == RDI_EvalTypeGroup_U ||
imm == RDI_EvalTypeGroup_S)
{
nval.u64 = svals[0].u64 << svals[1].u64;
}
else
{
result.code = E_InterpretationCode_BadOpTypes;
goto done;
}
}break;
case RDI_EvalOp_RShift:
{
if(imm == RDI_EvalTypeGroup_U)
{
nval.u64 = svals[0].u64 >> svals[1].u64;
}
else if(imm == RDI_EvalTypeGroup_S)
{
nval.u64 = svals[0].s64 >> svals[1].u64;
}
else
{
result.code = E_InterpretationCode_BadOpTypes;
goto done;
}
}break;
case RDI_EvalOp_BitAnd:
{
if(imm == RDI_EvalTypeGroup_U ||
imm == RDI_EvalTypeGroup_S)
{
nval.u64 = svals[0].u64&svals[1].u64;
}
else
{
result.code = E_InterpretationCode_BadOpTypes;
goto done;
}
}break;
case RDI_EvalOp_BitOr:
{
if(imm == RDI_EvalTypeGroup_U ||
imm == RDI_EvalTypeGroup_S)
{
nval.u64 = svals[0].u64|svals[1].u64;
}
else
{
result.code = E_InterpretationCode_BadOpTypes;
goto done;
}
}break;
case RDI_EvalOp_BitXor:
{
if(imm == RDI_EvalTypeGroup_U ||
imm == RDI_EvalTypeGroup_S)
{
nval.u64 = svals[0].u64^svals[1].u64;
}
else
{
result.code = E_InterpretationCode_BadOpTypes;
goto done;
}
}break;
case RDI_EvalOp_BitNot:
{
if(imm == RDI_EvalTypeGroup_U ||
imm == RDI_EvalTypeGroup_S)
{
nval.u64 = ~svals[0].u64;
}
else
{
result.code = E_InterpretationCode_BadOpTypes;
goto done;
}
}break;
case RDI_EvalOp_LogAnd:
{
if(imm == RDI_EvalTypeGroup_U ||
imm == RDI_EvalTypeGroup_S)
{
nval.u64 = (svals[0].u64 && svals[1].u64);
}
else
{
result.code = E_InterpretationCode_BadOpTypes;
goto done;
}
}break;
case RDI_EvalOp_LogOr:
{
if(imm == RDI_EvalTypeGroup_U ||
imm == RDI_EvalTypeGroup_S)
{
nval.u64 = (svals[0].u64 || svals[1].u64);
}
else
{
result.code = E_InterpretationCode_BadOpTypes;
goto done;
}
}break;
case RDI_EvalOp_LogNot:
{
if(imm == RDI_EvalTypeGroup_U ||
imm == RDI_EvalTypeGroup_S)
{
nval.u64 = (!svals[0].u64);
}
else
{
result.code = E_InterpretationCode_BadOpTypes;
goto done;
}
}break;
case RDI_EvalOp_EqEq:
{
nval.u64 = (svals[0].u64 == svals[1].u64);
}break;
case RDI_EvalOp_NtEq:
{
nval.u64 = (svals[0].u64 != svals[1].u64);
}break;
case RDI_EvalOp_LsEq:
{
if(imm == RDI_EvalTypeGroup_F32)
{
nval.u64 = (svals[0].f32 <= svals[1].f32);
}
else if(imm == RDI_EvalTypeGroup_F64)
{
nval.u64 = (svals[0].f64 <= svals[1].f64);
}
else if(imm == RDI_EvalTypeGroup_U)
{
nval.u64 = (svals[0].u64 <= svals[1].u64);
}
else if(imm == RDI_EvalTypeGroup_S)
{
nval.u64 = (svals[0].s64 <= svals[1].s64);
}
else
{
result.code = E_InterpretationCode_BadOpTypes;
goto done;
}
}break;
case RDI_EvalOp_GrEq:
{
if(imm == RDI_EvalTypeGroup_F32)
{
nval.u64 = (svals[0].f32 >= svals[1].f32);
}
else if(imm == RDI_EvalTypeGroup_F64)
{
nval.u64 = (svals[0].f64 >= svals[1].f64);
}
else if(imm == RDI_EvalTypeGroup_U)
{
nval.u64 = (svals[0].u64 >= svals[1].u64);
}
else if(imm == RDI_EvalTypeGroup_S)
{
nval.u64 = (svals[0].s64 >= svals[1].s64);
}
else
{
result.code = E_InterpretationCode_BadOpTypes;
goto done;
}
}break;
case RDI_EvalOp_Less:
{
if(imm == RDI_EvalTypeGroup_F32)
{
nval.u64 = (svals[0].f32 < svals[1].f32);
}
else if(imm == RDI_EvalTypeGroup_F64)
{
nval.u64 = (svals[0].f64 < svals[1].f64);
}
else if(imm == RDI_EvalTypeGroup_U)
{
nval.u64 = (svals[0].u64 < svals[1].u64);
}
else if(imm == RDI_EvalTypeGroup_S)
{
nval.u64 = (svals[0].s64 < svals[1].s64);
}
else
{
result.code = E_InterpretationCode_BadOpTypes;
goto done;
}
}break;
case RDI_EvalOp_Grtr:
{
if(imm == RDI_EvalTypeGroup_F32)
{
nval.u64 = (svals[0].f32 > svals[1].f32);
}
else if(imm == RDI_EvalTypeGroup_F64)
{
nval.u64 = (svals[0].f64 > svals[1].f64);
}
else if(imm == RDI_EvalTypeGroup_U)
{
nval.u64 = (svals[0].u64 > svals[1].u64);
}
else if(imm == RDI_EvalTypeGroup_S)
{
nval.u64 = (svals[0].s64 > svals[1].s64);
}
else
{
result.code = E_InterpretationCode_BadOpTypes;
goto done;
}
}break;
case RDI_EvalOp_Trunc:
{
if(0 < imm)
{
U64 mask = 0;
if(imm < 64)
{
mask = max_U64 >> (64 - imm);
}
nval.u64 = svals[0].u64&mask;
}
}break;
case RDI_EvalOp_TruncSigned:
{
if(0 < imm)
{
U64 mask = 0;
if(imm < 64)
{
mask = max_U64 >> (64 - imm);
}
U64 high = 0;
if(svals[0].u64 & (1 << (imm - 1)))
{
high = ~mask;
}
nval.u64 = high|(svals[0].u64&mask);
}
}break;
case RDI_EvalOp_Convert:
{
U32 in = imm&0xFF;
U32 out = (imm >> 8)&0xFF;
if(in != out)
{
switch(in + out*RDI_EvalTypeGroup_COUNT)
{
case RDI_EvalTypeGroup_F32 + RDI_EvalTypeGroup_U*RDI_EvalTypeGroup_COUNT:
{
nval.u64 = (U64)svals[0].f32;
}break;
case RDI_EvalTypeGroup_F64 + RDI_EvalTypeGroup_U*RDI_EvalTypeGroup_COUNT:
{
nval.u64 = (U64)svals[0].f64;
}break;
case RDI_EvalTypeGroup_F32 + RDI_EvalTypeGroup_S*RDI_EvalTypeGroup_COUNT:
{
nval.s64 = (S64)svals[0].f32;
}break;
case RDI_EvalTypeGroup_F64 + RDI_EvalTypeGroup_S*RDI_EvalTypeGroup_COUNT:
{
nval.s64 = (S64)svals[0].f64;
}break;
case RDI_EvalTypeGroup_U + RDI_EvalTypeGroup_F32*RDI_EvalTypeGroup_COUNT:
{
nval.f32 = (F32)svals[0].u64;
}break;
case RDI_EvalTypeGroup_S + RDI_EvalTypeGroup_F32*RDI_EvalTypeGroup_COUNT:
{
nval.f32 = (F32)svals[0].s64;
}break;
case RDI_EvalTypeGroup_F64 + RDI_EvalTypeGroup_F32*RDI_EvalTypeGroup_COUNT:
{
nval.f32 = (F32)svals[0].f64;
}break;
case RDI_EvalTypeGroup_U + RDI_EvalTypeGroup_F64*RDI_EvalTypeGroup_COUNT:
{
nval.f64 = (F64)svals[0].u64;
}break;
case RDI_EvalTypeGroup_S + RDI_EvalTypeGroup_F64*RDI_EvalTypeGroup_COUNT:
{
nval.f64 = (F64)svals[0].s64;
}break;
case RDI_EvalTypeGroup_F32 + RDI_EvalTypeGroup_F64*RDI_EvalTypeGroup_COUNT:
{
nval.f64 = (F64)svals[0].f32;
}break;
}
}
}break;
case RDI_EvalOp_Pick:
{
if(stack_count > imm)
{
nval = stack[stack_count - imm - 1];
}
else
{
result.code = E_InterpretationCode_BadOp;
goto done;
}
}break;
case RDI_EvalOp_Pop:
{
// do nothing - the pop is handled by the control bits
}break;
case RDI_EvalOp_Insert:
{
if(stack_count > imm)
{
if(imm > 0)
{
E_Value tval = stack[stack_count - 1];
E_Value *dst = stack + stack_count - 1 - imm;
E_Value *shift = dst + 1;
MemoryCopy(shift, dst, imm*sizeof(E_Value));
*dst = tval;
}
}
else
{
result.code = E_InterpretationCode_BadOp;
goto done;
}
}break;
}
// rjf: push
{
U64 push_count = RDI_PUSHN_FROM_CTRLBITS(ctrlbits);
if(push_count == 1)
{
if(stack_count < stack_cap)
{
stack[stack_count] = nval;
stack_count += 1;
}
else
{
result.code = E_InterpretationCode_InsufficientStackSpace;
goto done;
}
}
}
}
done:;
if(stack_count >= 1)
{
result.value = stack[0];
}
scratch_end(scratch);
return result;
}
+63
View File
@@ -0,0 +1,63 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef EVAL_INTERPRET_H
#define EVAL_INTERPRET_H
////////////////////////////////
//~ rjf: Bytecode Interpretation Types
typedef union E_Value E_Value;
union E_Value
{
U64 u512[8];
U64 u256[4];
U64 u128[2];
U64 u64;
S64 s64;
F64 f64;
F32 f32;
};
typedef struct E_Interpretation E_Interpretation;
struct E_Interpretation
{
E_Value value;
E_InterpretationCode code;
};
////////////////////////////////
//~ rjf: Interpretation Context
typedef B32 E_MemoryReadFunction(void *user_data, void *out, Rng1U64 vaddr_range);
typedef struct E_InterpretCtx E_InterpretCtx;
struct E_InterpretCtx
{
Architecture arch;
void *memory_read_user_data;
E_MemoryReadFunction *memory_read;
void *reg_data;
U64 reg_size;
U64 *module_base;
U64 *frame_base;
U64 *tls_base;
};
////////////////////////////////
//~ rjf: Globals
thread_static E_InterpretCtx *e_interpret_ctx = 0;
////////////////////////////////
//~ rjf: Context Selection Functions (Selection Required For All Subsequent APIs)
internal E_InterpretCtx *e_selected_interpret_ctx(void);
internal void e_select_interpret_ctx(E_InterpretCtx *ctx);
////////////////////////////////
//~ rjf: Interpretation Functions
internal E_Interpretation e_interpret(String8 bytecode);
#endif //EVAL_INTERPRET_H
+1302
View File
File diff suppressed because it is too large Load Diff
+126
View File
@@ -0,0 +1,126 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef EVAL_IR_H
#define EVAL_IR_H
////////////////////////////////
//~ rjf: Bytecode Operation Types
enum
{
E_IRExtKind_Bytecode = RDI_EvalOp_COUNT,
E_IRExtKind_Data,
E_IRExtKind_COUNT
};
typedef struct E_Op E_Op;
struct E_Op
{
E_Op *next;
RDI_EvalOp opcode;
union
{
U64 p;
String8 bytecode;
String8 data;
};
};
typedef struct E_OpList E_OpList;
struct E_OpList
{
E_Op *first;
E_Op *last;
U64 op_count;
U64 encoded_size;
};
////////////////////////////////
//~ rjf: IR Tree Types
typedef struct E_IRNode E_IRNode;
struct E_IRNode
{
E_IRNode *first;
E_IRNode *last;
E_IRNode *next;
RDI_EvalOp op;
String8 string;
U64 u64;
};
typedef struct E_IRTreeAndType E_IRTreeAndType;
struct E_IRTreeAndType
{
E_IRNode *root;
E_TypeKey type_key;
E_Mode mode;
E_MsgList msgs;
};
////////////////////////////////
//~ rjf: Parse Context
typedef struct E_IRCtx E_IRCtx;
struct E_IRCtx
{
E_String2ExprMap *macro_map;
};
////////////////////////////////
//~ rjf: Globals
global read_only E_IRNode e_irnode_nil = {&e_irnode_nil, &e_irnode_nil, &e_irnode_nil};
thread_static E_IRCtx *e_ir_ctx = 0;
////////////////////////////////
//~ rjf: Expr Kind Enum Functions
internal RDI_EvalOp e_opcode_from_expr_kind(E_ExprKind kind);
internal B32 e_expr_kind_is_comparison(E_ExprKind kind);
////////////////////////////////
//~ rjf: Context Selection Functions (Selection Required For All Subsequent APIs)
internal E_IRCtx *e_selected_ir_ctx(void);
internal void e_select_ir_ctx(E_IRCtx *ctx);
////////////////////////////////
//~ rjf: IR-ization Functions
//- rjf: op list functions
internal void e_oplist_push_op(Arena *arena, E_OpList *list, RDI_EvalOp opcode, U64 p);
internal void e_oplist_push_uconst(Arena *arena, E_OpList *list, U64 x);
internal void e_oplist_push_sconst(Arena *arena, E_OpList *list, S64 x);
internal void e_oplist_push_bytecode(Arena *arena, E_OpList *list, String8 bytecode);
internal void e_oplist_push_data(Arena *arena, E_OpList *list, String8 data);
internal void e_oplist_concat_in_place(E_OpList *dst, E_OpList *to_push);
//- rjf: ir tree core building helpers
internal E_IRNode *e_push_irnode(Arena *arena, RDI_EvalOp op);
internal void e_irnode_push_child(E_IRNode *parent, E_IRNode *child);
//- rjf: ir subtree building helpers
internal E_IRNode *e_irtree_const_u(Arena *arena, U64 v);
internal E_IRNode *e_irtree_unary_op(Arena *arena, RDI_EvalOp op, RDI_EvalTypeGroup group, E_IRNode *c);
internal E_IRNode *e_irtree_binary_op(Arena *arena, RDI_EvalOp op, RDI_EvalTypeGroup group, E_IRNode *l, E_IRNode *r);
internal E_IRNode *e_irtree_binary_op_u(Arena *arena, RDI_EvalOp op, E_IRNode *l, E_IRNode *r);
internal E_IRNode *e_irtree_conditional(Arena *arena, E_IRNode *c, E_IRNode *l, E_IRNode *r);
internal E_IRNode *e_irtree_bytecode_no_copy(Arena *arena, String8 bytecode);
internal E_IRNode *e_irtree_string_literal(Arena *arena, String8 string);
internal E_IRNode *e_irtree_mem_read_type(Arena *arena, E_IRNode *c, E_TypeKey type_key);
internal E_IRNode *e_irtree_convert_lo(Arena *arena, E_IRNode *c, RDI_EvalTypeGroup out, RDI_EvalTypeGroup in);
internal E_IRNode *e_irtree_trunc(Arena *arena, E_IRNode *c, E_TypeKey type_key);
internal E_IRNode *e_irtree_convert_hi(Arena *arena, E_IRNode *c, E_TypeKey out, E_TypeKey in);
internal E_IRNode *e_irtree_resolve_to_value(Arena *arena, E_Mode from_mode, E_IRNode *tree, E_TypeKey type_key);
//- rjf: top-level irtree/type extraction
internal E_IRTreeAndType e_irtree_and_type_from_expr(Arena *arena, E_Expr *expr);
//- rjf: irtree -> linear ops/bytecode
internal void e_append_oplist_from_irtree(Arena *arena, E_IRNode *root, E_OpList *out);
internal E_OpList e_oplist_from_irtree(Arena *arena, E_IRNode *root);
internal String8 e_bytecode_from_oplist(Arena *arena, E_OpList *oplist);
#endif // EVAL_IR_H
File diff suppressed because it is too large Load Diff
+298
View File
@@ -0,0 +1,298 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef EVAL_PARSE_H
#define EVAL_PARSE_H
////////////////////////////////
//~ rjf: Generated Code
#include "generated/eval.meta.h"
////////////////////////////////
//~ rjf: Messages
typedef enum E_MsgKind
{
E_MsgKind_Null,
E_MsgKind_MalformedInput,
E_MsgKind_MissingInfo,
E_MsgKind_ResolutionFailure,
E_MsgKind_InterpretationError,
E_MsgKind_COUNT
}
E_MsgKind;
typedef struct E_Msg E_Msg;
struct E_Msg
{
E_Msg *next;
E_MsgKind kind;
void *location;
String8 text;
};
typedef struct E_MsgList E_MsgList;
struct E_MsgList
{
E_Msg *first;
E_Msg *last;
E_MsgKind max_kind;
U64 count;
};
////////////////////////////////
//~ rjf: Token Types
typedef enum E_TokenKind
{
E_TokenKind_Null,
E_TokenKind_Identifier,
E_TokenKind_Numeric,
E_TokenKind_StringLiteral,
E_TokenKind_CharLiteral,
E_TokenKind_Symbol,
E_TokenKind_COUNT
}
E_TokenKind;
typedef struct E_Token E_Token;
struct E_Token
{
E_TokenKind kind;
Rng1U64 range;
};
typedef struct E_TokenChunkNode E_TokenChunkNode;
struct E_TokenChunkNode
{
E_TokenChunkNode *next;
E_Token *v;
U64 count;
U64 cap;
};
typedef struct E_TokenChunkList E_TokenChunkList;
struct E_TokenChunkList
{
E_TokenChunkNode *first;
E_TokenChunkNode *last;
U64 node_count;
U64 total_count;
};
typedef struct E_TokenArray E_TokenArray;
struct E_TokenArray
{
E_Token *v;
U64 count;
};
////////////////////////////////
//~ rjf: Expression Tree Types
typedef enum E_Mode
{
E_Mode_Null,
E_Mode_Value,
E_Mode_Addr,
E_Mode_Reg,
E_Mode_InlineData,
}
E_Mode;
typedef struct E_Expr E_Expr;
struct E_Expr
{
E_Expr *first;
E_Expr *last;
E_Expr *next;
void *location;
E_ExprKind kind;
E_Mode mode;
E_TypeKey type_key;
U32 u32;
F32 f32;
U64 u64;
F64 f64;
String8 string;
};
////////////////////////////////
//~ rjf: Map Types
//- rjf: string -> num
typedef struct E_String2NumMapNode E_String2NumMapNode;
struct E_String2NumMapNode
{
E_String2NumMapNode *order_next;
E_String2NumMapNode *hash_next;
String8 string;
U64 num;
};
typedef struct E_String2NumMapNodeArray E_String2NumMapNodeArray;
struct E_String2NumMapNodeArray
{
E_String2NumMapNode **v;
U64 count;
};
typedef struct E_String2NumMapSlot E_String2NumMapSlot;
struct E_String2NumMapSlot
{
E_String2NumMapNode *first;
E_String2NumMapNode *last;
};
typedef struct E_String2NumMap E_String2NumMap;
struct E_String2NumMap
{
U64 slots_count;
U64 node_count;
E_String2NumMapSlot *slots;
E_String2NumMapNode *first;
E_String2NumMapNode *last;
};
//- rjf: string -> expr
typedef struct E_String2ExprMapNode E_String2ExprMapNode;
struct E_String2ExprMapNode
{
E_String2ExprMapNode *hash_next;
String8 string;
E_Expr *expr;
U64 poison_count;
};
typedef struct E_String2ExprMapSlot E_String2ExprMapSlot;
struct E_String2ExprMapSlot
{
E_String2ExprMapNode *first;
E_String2ExprMapNode *last;
};
typedef struct E_String2ExprMap E_String2ExprMap;
struct E_String2ExprMap
{
U64 slots_count;
E_String2ExprMapSlot *slots;
};
////////////////////////////////
//~ rjf: Parse Context
typedef struct E_ParseCtx E_ParseCtx;
struct E_ParseCtx
{
// rjf: architecture
Architecture arch;
// rjf: instruction pointer info
U64 ip_vaddr;
U64 ip_voff; // (within module, which uses `rdi_primary` for debug info)
// rjf: debug info
RDI_Parsed *rdi_primary;
RDI_Parsed **rdis;
Rng1U64 *rdis_vaddr_ranges;
U64 rdis_count;
// rjf: identifier resolution maps
E_String2NumMap *regs_map;
E_String2NumMap *reg_alias_map;
E_String2NumMap *locals_map; // (within `rdi_primary`)
E_String2NumMap *member_map; // (within `rdi_primary`)
};
////////////////////////////////
//~ rjf: Parse Results
typedef struct E_Parse E_Parse;
struct E_Parse
{
E_Token *last_token;
E_Expr *expr;
E_MsgList msgs;
};
////////////////////////////////
//~ rjf: Globals
global read_only E_String2NumMap e_string2num_map_nil = {0};
global read_only E_String2ExprMap e_string2expr_map_nil = {0};
global read_only E_Expr e_expr_nil = {&e_expr_nil, &e_expr_nil, &e_expr_nil};
thread_static E_ParseCtx *e_parse_ctx = 0;
////////////////////////////////
//~ rjf: Basic Helper Functions
internal U64 e_hash_from_string(String8 string);
////////////////////////////////
//~ rjf: Basic Map Functions
//- rjf: string -> num
internal E_String2NumMap e_string2num_map_make(Arena *arena, U64 slot_count);
internal void e_string2num_map_insert(Arena *arena, E_String2NumMap *map, String8 string, U64 num);
internal U64 e_num_from_string(E_String2NumMap *map, String8 string);
internal E_String2NumMapNodeArray e_string2num_map_node_array_from_map(Arena *arena, E_String2NumMap *map);
internal int e_string2num_map_node_qsort_compare__num_ascending(E_String2NumMapNode **a, E_String2NumMapNode **b);
internal void e_string2num_map_node_array_sort__in_place(E_String2NumMapNodeArray *array);
//- rjf: string -> expr
internal E_String2ExprMap e_string2expr_map_make(Arena *arena, U64 slot_count);
internal void e_string2expr_map_insert(Arena *arena, E_String2ExprMap *map, String8 string, E_Expr *expr);
internal void e_string2expr_map_inc_poison(E_String2ExprMap *map, String8 string);
internal void e_string2expr_map_dec_poison(E_String2ExprMap *map, String8 string);
internal E_Expr *e_expr_from_string(E_String2ExprMap *map, String8 string);
////////////////////////////////
//~ rjf: Debug-Info-Driven Map Building Functions
internal E_String2NumMap *e_push_locals_map_from_rdi_voff(Arena *arena, RDI_Parsed *rdi, U64 voff);
internal E_String2NumMap *e_push_member_map_from_rdi_voff(Arena *arena, RDI_Parsed *rdi, U64 voff);
////////////////////////////////
//~ rjf: Message Functions
internal void e_msg(Arena *arena, E_MsgList *msgs, E_MsgKind kind, void *location, String8 text);
internal void e_msgf(Arena *arena, E_MsgList *msgs, E_MsgKind kind, void *location, char *fmt, ...);
internal void e_msg_list_concat_in_place(E_MsgList *dst, E_MsgList *to_push);
////////////////////////////////
//~ rjf: Tokenization Functions
#define e_token_at_it(it, arr) (((it) < (arr)->v+(arr)->count) ? (*(it)) : e_token_zero())
internal E_Token e_token_zero(void);
internal void e_token_chunk_list_push(Arena *arena, E_TokenChunkList *list, U64 chunk_size, E_Token *token);
internal E_TokenArray e_token_array_from_chunk_list(Arena *arena, E_TokenChunkList *list);
internal E_TokenArray e_token_array_from_text(Arena *arena, String8 text);
internal E_TokenArray e_token_array_make_first_opl(E_Token *first, E_Token *opl);
////////////////////////////////
//~ rjf: Context Selection Functions (Selection Required For All Subsequent APIs)
internal E_ParseCtx *e_selected_parse_ctx(void);
internal void e_select_parse_ctx(E_ParseCtx *ctx);
internal U32 e_parse_ctx_idx_from_rdi(RDI_Parsed *rdi);
////////////////////////////////
//~ rjf: Expression Tree Building Functions
internal E_Expr *e_push_expr(Arena *arena, E_ExprKind kind, void *location);
internal void e_expr_push_child(E_Expr *parent, E_Expr *child);
////////////////////////////////
//~ rjf: Parsing Functions
internal E_TypeKey e_leaf_type_from_name(String8 name);
internal E_TypeKey e_type_from_expr(E_Expr *expr);
internal void e_push_leaf_ident_exprs_from_expr__in_place(Arena *arena, E_String2ExprMap *map, E_Expr *expr);
internal E_Parse e_parse_type_from_text_tokens(Arena *arena, String8 text, E_TokenArray *tokens);
internal E_Parse e_parse_expr_from_text_tokens__prec(Arena *arena, String8 text, E_TokenArray *tokens, S64 max_precedence);
internal E_Parse e_parse_expr_from_text_tokens(Arena *arena, String8 text, E_TokenArray *tokens);
#endif // EVAL_PARSE_H
File diff suppressed because it is too large Load Diff
+247
View File
@@ -0,0 +1,247 @@
// Copyright (c) 2024 Epic Games Tools
// Licensed under the MIT license (https://opensource.org/license/mit/)
#ifndef EVAL_TYPES_H
#define EVAL_TYPES_H
////////////////////////////////
//~ rjf: Implicit Type Graph Key Types
typedef enum E_TypeKeyKind
{
E_TypeKeyKind_Null,
E_TypeKeyKind_Basic,
E_TypeKeyKind_Ext,
E_TypeKeyKind_Cons,
E_TypeKeyKind_Reg,
E_TypeKeyKind_RegAlias,
}
E_TypeKeyKind;
typedef struct E_TypeKey E_TypeKey;
struct E_TypeKey
{
E_TypeKeyKind kind;
U32 u32[3];
// [0] -> E_TypeKind (Basic, Cons, Ext); Architecture (Reg, RegAlias)
// [1] -> Type Index In RDI (Cons, Ext); Code (Reg, RegAlias)
// [2] -> RDI Index (Cons, Ext)
};
typedef struct E_TypeKeyNode E_TypeKeyNode;
struct E_TypeKeyNode
{
E_TypeKeyNode *next;
E_TypeKey v;
};
typedef struct E_TypeKeyList E_TypeKeyList;
struct E_TypeKeyList
{
E_TypeKeyNode *first;
E_TypeKeyNode *last;
U64 count;
};
////////////////////////////////
//~ rjf: Full Extracted Type Information Types
typedef enum E_MemberKind
{
E_MemberKind_Null,
E_MemberKind_DataField,
E_MemberKind_StaticData,
E_MemberKind_Method,
E_MemberKind_StaticMethod,
E_MemberKind_VirtualMethod,
E_MemberKind_VTablePtr,
E_MemberKind_Base,
E_MemberKind_VirtualBase,
E_MemberKind_NestedType,
E_MemberKind_Padding,
E_MemberKind_COUNT
}
E_MemberKind;
typedef U32 E_TypeFlags;
enum
{
E_TypeFlag_Const = (1<<0),
E_TypeFlag_Volatile = (1<<1),
};
typedef struct E_Member E_Member;
struct E_Member
{
E_MemberKind kind;
E_TypeKey type_key;
String8 name;
U64 off;
E_TypeKeyList inheritance_key_chain;
};
typedef struct E_MemberNode E_MemberNode;
struct E_MemberNode
{
E_MemberNode *next;
E_Member v;
};
typedef struct E_MemberList E_MemberList;
struct E_MemberList
{
E_MemberNode *first;
E_MemberNode *last;
U64 count;
};
typedef struct E_MemberArray E_MemberArray;
struct E_MemberArray
{
E_Member *v;
U64 count;
};
typedef struct E_EnumVal E_EnumVal;
struct E_EnumVal
{
String8 name;
U64 val;
};
typedef struct E_EnumValArray E_EnumValArray;
struct E_EnumValArray
{
E_EnumVal *v;
U64 count;
};
typedef struct E_Type E_Type;
struct E_Type
{
E_TypeKind kind;
E_TypeFlags flags;
String8 name;
U64 byte_size;
U64 count;
U32 off;
E_TypeKey direct_type_key;
E_TypeKey owner_type_key;
E_TypeKey *param_type_keys;
E_Member *members;
E_EnumVal *enum_vals;
};
////////////////////////////////
//~ rjf: Evaluation Context
typedef struct E_ConsTypeNode E_ConsTypeNode;
struct E_ConsTypeNode
{
E_ConsTypeNode *key_next;
E_ConsTypeNode *content_next;
E_TypeKey key;
E_TypeKey direct_key;
U64 u64;
};
typedef struct E_ConsTypeSlot E_ConsTypeSlot;
struct E_ConsTypeSlot
{
E_ConsTypeNode *first;
E_ConsTypeNode *last;
};
typedef struct E_TypeCtx E_TypeCtx;
struct E_TypeCtx
{
// rjf: architecture
Architecture arch;
// rjf: instruction pointer info
U64 ip_vaddr;
U64 ip_voff; // (within module, which uses `rdi_primary` for debug info)
// rjf: debug info
RDI_Parsed *rdi_primary;
RDI_Parsed **rdis;
Rng1U64 *rdis_vaddr_ranges;
U64 rdis_count;
};
typedef struct E_TypeState E_TypeState;
struct E_TypeState
{
Arena *arena;
U64 arena_eval_start_pos;
// rjf: evaluation context
E_TypeCtx *ctx;
// rjf: JIT-constructed types tables
U64 cons_id_gen;
U64 cons_content_slots_count;
U64 cons_key_slots_count;
E_ConsTypeSlot *cons_content_slots;
E_ConsTypeSlot *cons_key_slots;
};
////////////////////////////////
//~ rjf: Globals
global read_only E_Type e_type_nil = {E_TypeKind_Null};
thread_static E_TypeState *e_type_state = 0;
////////////////////////////////
//~ rjf: Type Kind Enum Functions
internal E_TypeKind e_type_kind_from_rdi(RDI_TypeKind kind);
internal E_MemberKind e_member_kind_from_rdi(RDI_MemberKind kind);
internal RDI_EvalTypeGroup e_type_group_from_kind(E_TypeKind kind);
internal B32 e_type_kind_is_integer(E_TypeKind kind);
internal B32 e_type_kind_is_signed(E_TypeKind kind);
internal B32 e_type_kind_is_basic_or_enum(E_TypeKind kind);
////////////////////////////////
//~ rjf: Context Selection Functions (Selection Required For All Subsequent APIs)
internal E_TypeCtx *e_selected_type_ctx(void);
internal void e_select_type_ctx(E_TypeCtx *ctx);
////////////////////////////////
//~ rjf: Type Operation Functions
//- rjf: key constructors
internal E_TypeKey e_type_key_zero(void);
internal E_TypeKey e_type_key_basic(E_TypeKind kind);
internal E_TypeKey e_type_key_ext(E_TypeKind kind, U32 type_idx, U32 rdi_idx);
internal E_TypeKey e_type_key_reg(Architecture arch, REGS_RegCode code);
internal E_TypeKey e_type_key_reg_alias(Architecture arch, REGS_AliasCode code);
internal E_TypeKey e_type_key_cons(E_TypeKind kind, E_TypeKey direct_key, U64 u64);
//- rjf: basic type key functions
internal B32 e_type_key_match(E_TypeKey l, E_TypeKey r);
//- rjf: key -> info extraction
internal E_TypeKind e_type_kind_from_key(E_TypeKey key);
internal U64 e_type_byte_size_from_key(E_TypeKey key);
internal E_Type *e_type_from_key(Arena *arena, E_TypeKey key);
internal E_TypeKey e_type_direct_from_key(E_TypeKey key);
internal E_TypeKey e_type_owner_from_key(E_TypeKey key);
internal E_TypeKey e_type_ptee_from_key(E_TypeKey key);
internal E_TypeKey e_type_unwrap_enum(E_TypeKey key);
internal E_TypeKey e_type_unwrap(E_TypeKey key);
internal E_TypeKey e_type_promote(E_TypeKey key);
internal B32 e_type_match(E_TypeKey l, E_TypeKey r);
internal E_Member *e_type_member_copy(Arena *arena, E_Member *src);
internal int e_type_qsort_compare_members_offset(E_Member *a, E_Member *b);
internal E_MemberArray e_type_data_members_from_key(Arena *arena, E_TypeKey key);
internal void e_type_lhs_string_from_key(Arena *arena, E_TypeKey key, String8List *out, U32 prec, B32 skip_return);
internal void e_type_rhs_string_from_key(Arena *arena, E_TypeKey key, String8List *out, U32 prec);
internal String8 e_type_string_from_key(Arena *arena, E_TypeKey key);
//- rjf: type key data structures
internal void e_type_key_list_push(Arena *arena, E_TypeKeyList *list, E_TypeKey key);
internal E_TypeKeyList e_type_key_list_copy(Arena *arena, E_TypeKeyList *src);
#endif // EVAL_TYPES_H