// Copyright (c) 2024 Epic Games Tools // Licensed under the MIT license (https://opensource.org/license/mit/) //////////////////////////////// //~ rjf: Expr Kind Enum Functions internal RDI_EvalOp e_opcode_from_expr_kind(E_ExprKind kind) { RDI_EvalOp result = RDI_EvalOp_Stop; switch(kind) { case E_ExprKind_Neg: result = RDI_EvalOp_Neg; break; case E_ExprKind_LogNot: result = RDI_EvalOp_LogNot; break; case E_ExprKind_BitNot: result = RDI_EvalOp_BitNot; break; case E_ExprKind_Mul: result = RDI_EvalOp_Mul; break; case E_ExprKind_Div: result = RDI_EvalOp_Div; break; case E_ExprKind_Mod: result = RDI_EvalOp_Mod; break; case E_ExprKind_Add: result = RDI_EvalOp_Add; break; case E_ExprKind_Sub: result = RDI_EvalOp_Sub; break; case E_ExprKind_LShift: result = RDI_EvalOp_LShift; break; case E_ExprKind_RShift: result = RDI_EvalOp_RShift; break; case E_ExprKind_Less: result = RDI_EvalOp_Less; break; case E_ExprKind_LsEq: result = RDI_EvalOp_LsEq; break; case E_ExprKind_Grtr: result = RDI_EvalOp_Grtr; break; case E_ExprKind_GrEq: result = RDI_EvalOp_GrEq; break; case E_ExprKind_EqEq: result = RDI_EvalOp_EqEq; break; case E_ExprKind_NtEq: result = RDI_EvalOp_NtEq; break; case E_ExprKind_BitAnd: result = RDI_EvalOp_BitAnd; break; case E_ExprKind_BitXor: result = RDI_EvalOp_BitXor; break; case E_ExprKind_BitOr: result = RDI_EvalOp_BitOr; break; case E_ExprKind_LogAnd: result = RDI_EvalOp_LogAnd; break; case E_ExprKind_LogOr: result = RDI_EvalOp_LogOr; break; } return result; } internal B32 e_expr_kind_is_comparison(E_ExprKind kind) { B32 result = 0; switch(kind) { default:{}break; case E_ExprKind_EqEq: case E_ExprKind_NtEq: case E_ExprKind_Less: case E_ExprKind_Grtr: case E_ExprKind_LsEq: case E_ExprKind_GrEq: { result = 1; }break; } return result; } //////////////////////////////// //~ rjf: Context Selection Functions (Selection Required For All Subsequent APIs) internal E_IRCtx * e_selected_ir_ctx(void) { return e_ir_ctx; } internal void e_select_ir_ctx(E_IRCtx *ctx) { e_ir_ctx = ctx; } //////////////////////////////// //~ rjf: IR-ization Functions //- rjf: op list functions internal void e_oplist_push_op(Arena *arena, E_OpList *list, RDI_EvalOp opcode, E_Value value) { U16 ctrlbits = rdi_eval_op_ctrlbits_table[opcode]; U32 p_size = RDI_DECODEN_FROM_CTRLBITS(ctrlbits); E_Op *node = push_array_no_zero(arena, E_Op, 1); node->opcode = opcode; node->value = value; SLLQueuePush(list->first, list->last, node); list->op_count += 1; list->encoded_size += 1 + p_size; } internal void e_oplist_push_uconst(Arena *arena, E_OpList *list, U64 x) { if(0){} else if(x <= 0xFF) { e_oplist_push_op(arena, list, RDI_EvalOp_ConstU8, e_value_u64(x)); } else if(x <= 0xFFFF) { e_oplist_push_op(arena, list, RDI_EvalOp_ConstU16, e_value_u64(x)); } else if(x <= 0xFFFFFFFF) { e_oplist_push_op(arena, list, RDI_EvalOp_ConstU32, e_value_u64(x)); } else { e_oplist_push_op(arena, list, RDI_EvalOp_ConstU64, e_value_u64(x)); } } internal void e_oplist_push_sconst(Arena *arena, E_OpList *list, S64 x) { if(-0x80 <= x && x <= 0x7F) { e_oplist_push_op(arena, list, RDI_EvalOp_ConstU8, e_value_u64((U64)x)); e_oplist_push_op(arena, list, RDI_EvalOp_TruncSigned, e_value_u64(8)); } else if(-0x8000 <= x && x <= 0x7FFF) { e_oplist_push_op(arena, list, RDI_EvalOp_ConstU16, e_value_u64((U64)x)); e_oplist_push_op(arena, list, RDI_EvalOp_TruncSigned, e_value_u64(16)); } else if(-0x80000000ll <= x && x <= 0x7FFFFFFFll) { e_oplist_push_op(arena, list, RDI_EvalOp_ConstU32, e_value_u64((U64)x)); e_oplist_push_op(arena, list, RDI_EvalOp_TruncSigned, e_value_u64(32)); } else { e_oplist_push_op(arena, list, RDI_EvalOp_ConstU64, e_value_u64((U64)x)); } } internal void e_oplist_push_bytecode(Arena *arena, E_OpList *list, String8 bytecode) { E_Op *node = push_array_no_zero(arena, E_Op, 1); node->opcode = E_IRExtKind_Bytecode; node->string = bytecode; SLLQueuePush(list->first, list->last, node); list->op_count += 1; list->encoded_size += bytecode.size; } internal void e_oplist_push_set_space(Arena *arena, E_OpList *list, E_Space space) { E_Op *node = push_array_no_zero(arena, E_Op, 1); node->opcode = E_IRExtKind_SetSpace; StaticAssert(sizeof(E_Space) <= sizeof(E_Value), space_size_check); MemoryCopy(&node->value, &space, sizeof(space)); SLLQueuePush(list->first, list->last, node); list->op_count += 1; list->encoded_size += 1 + sizeof(space); } internal void e_oplist_push_string_literal(Arena *arena, E_OpList *list, String8 string) { RDI_EvalOp opcode = RDI_EvalOp_ConstString; U16 ctrlbits = rdi_eval_op_ctrlbits_table[opcode]; U32 p_size = RDI_DECODEN_FROM_CTRLBITS(ctrlbits); E_Op *node = push_array_no_zero(arena, E_Op, 1); node->opcode = opcode; node->string = string; node->value.u64 = Min(string.size, 64); SLLQueuePush(list->first, list->last, node); list->op_count += 1; list->encoded_size += 1 + p_size + node->value.u64; } internal void e_oplist_concat_in_place(E_OpList *dst, E_OpList *to_push) { if(to_push->first && dst->first) { dst->last->next = to_push->first; dst->last = to_push->last; dst->op_count += to_push->op_count; dst->encoded_size += to_push->encoded_size; } else if(!dst->first) { MemoryCopyStruct(dst, to_push); } MemoryZeroStruct(to_push); } //- rjf: ir tree core building helpers internal E_IRNode * e_push_irnode(Arena *arena, RDI_EvalOp op) { E_IRNode *n = push_array(arena, E_IRNode, 1); n->first = n->last = n->next = &e_irnode_nil; n->op = op; return n; } internal void e_irnode_push_child(E_IRNode *parent, E_IRNode *child) { SLLQueuePush_NZ(&e_irnode_nil, parent->first, parent->last, child, next); } //- rjf: ir subtree building helpers internal E_IRNode * e_irtree_const_u(Arena *arena, U64 v) { // rjf: choose op RDI_EvalOp op = RDI_EvalOp_ConstU64; if (v < 0x100) { op = RDI_EvalOp_ConstU8; } else if(v < 0x10000) { op = RDI_EvalOp_ConstU16; } else if(v < 0x100000000) { op = RDI_EvalOp_ConstU32; } // rjf: build E_IRNode *n = e_push_irnode(arena, op); n->value.u64 = v; return n; } internal E_IRNode * e_irtree_unary_op(Arena *arena, RDI_EvalOp op, RDI_EvalTypeGroup group, E_IRNode *c) { E_IRNode *n = e_push_irnode(arena, op); n->value.u64 = group; e_irnode_push_child(n, c); return n; } internal E_IRNode * e_irtree_binary_op(Arena *arena, RDI_EvalOp op, RDI_EvalTypeGroup group, E_IRNode *l, E_IRNode *r) { E_IRNode *n = e_push_irnode(arena, op); n->value.u64 = group; e_irnode_push_child(n, l); e_irnode_push_child(n, r); return n; } internal E_IRNode * e_irtree_binary_op_u(Arena *arena, RDI_EvalOp op, E_IRNode *l, E_IRNode *r) { E_IRNode *n = e_irtree_binary_op(arena, op, RDI_EvalTypeGroup_U, l, r); return n; } internal E_IRNode * e_irtree_conditional(Arena *arena, E_IRNode *c, E_IRNode *l, E_IRNode *r) { E_IRNode *n = e_push_irnode(arena, RDI_EvalOp_Cond); e_irnode_push_child(n, c); e_irnode_push_child(n, l); e_irnode_push_child(n, r); return n; } internal E_IRNode * e_irtree_bytecode_no_copy(Arena *arena, String8 bytecode) { E_IRNode *n = e_push_irnode(arena, E_IRExtKind_Bytecode); n->string = bytecode; return n; } internal E_IRNode * e_irtree_string_literal(Arena *arena, String8 string) { E_IRNode *root = e_push_irnode(arena, RDI_EvalOp_ConstString); root->string = string; return root; } internal E_IRNode * e_irtree_set_space(Arena *arena, E_Space space, E_IRNode *c) { E_IRNode *root = e_push_irnode(arena, E_IRExtKind_SetSpace); StaticAssert(sizeof(E_Space) <= sizeof(E_Value), space_size_check); MemoryCopy(&root->value, &space, sizeof(space)); e_irnode_push_child(root, c); return root; } internal E_IRNode * e_irtree_mem_read_type(Arena *arena, E_Space space, E_IRNode *c, E_TypeKey type_key) { E_IRNode *result = &e_irnode_nil; U64 byte_size = e_type_byte_size_from_key(type_key); byte_size = Min(64, byte_size); // rjf: build the read node E_IRNode *read_node = e_push_irnode(arena, RDI_EvalOp_MemRead); read_node->value.u64 = byte_size; e_irnode_push_child(read_node, c); // rjf: build a signed trunc node if needed U64 bit_size = byte_size << 3; E_IRNode *with_trunc = read_node; E_TypeKind kind = e_type_kind_from_key(type_key); if(bit_size < 64 && e_type_kind_is_signed(kind)) { with_trunc = e_push_irnode(arena, RDI_EvalOp_TruncSigned); with_trunc->value.u64 = bit_size; e_irnode_push_child(with_trunc, read_node); } // rjf: set space for this mem read E_IRNode *set_space_node = e_irtree_set_space(arena, space, with_trunc); // rjf: fill result = set_space_node; return result; } internal E_IRNode * e_irtree_convert_lo(Arena *arena, E_IRNode *c, RDI_EvalTypeGroup out, RDI_EvalTypeGroup in) { E_IRNode *n = e_push_irnode(arena, RDI_EvalOp_Convert); n->value.u64 = in | (out << 8); e_irnode_push_child(n, c); return n; } internal E_IRNode * e_irtree_trunc(Arena *arena, E_IRNode *c, E_TypeKey type_key) { E_IRNode *result = c; U64 byte_size = e_type_byte_size_from_key(type_key); if(byte_size < 64) { RDI_EvalOp op = RDI_EvalOp_Trunc; E_TypeKind kind = e_type_kind_from_key(type_key); if(e_type_kind_is_signed(kind)) { op = RDI_EvalOp_TruncSigned; } U64 bit_size = byte_size << 3; result = e_push_irnode(arena, op); result->value.u64 = bit_size; e_irnode_push_child(result, c); } return result; } internal E_IRNode * e_irtree_convert_hi(Arena *arena, E_IRNode *c, E_TypeKey out, E_TypeKey in) { E_IRNode *result = c; E_TypeKind in_kind = e_type_kind_from_key(in); E_TypeKind out_kind = e_type_kind_from_key(out); U8 in_group = e_type_group_from_kind(in_kind); U8 out_group = e_type_group_from_kind(out_kind); U32 conversion_rule = rdi_eval_conversion_kind_from_typegroups(in_group, out_group); if(conversion_rule == RDI_EvalConversionKind_Legal) { result = e_irtree_convert_lo(arena, result, out_group, in_group); } U64 in_byte_size = e_type_byte_size_from_key(in); U64 out_byte_size = e_type_byte_size_from_key(out); if(out_byte_size < in_byte_size && e_type_kind_is_integer(out_kind)) { result = e_irtree_trunc(arena, result, out); } return result; } internal E_IRNode * e_irtree_resolve_to_value(Arena *arena, E_Space from_space, E_Mode from_mode, E_IRNode *tree, E_TypeKey type_key) { E_IRNode *result = tree; if(from_mode == E_Mode_Offset) { result = e_irtree_mem_read_type(arena, from_space, tree, type_key); } return result; } //- rjf: top-level irtree/type extraction internal E_IRTreeAndType e_irtree_and_type_from_expr(Arena *arena, E_Expr *expr) { E_IRTreeAndType result = {&e_irnode_nil}; E_ExprKind kind = expr->kind; switch(kind) { default:{}break; //- rjf: references -> just descend to sub-expr case E_ExprKind_Ref: { result = e_irtree_and_type_from_expr(arena, expr->ref); }break; //- rjf: array indices case E_ExprKind_ArrayIndex: { // rjf: unpack left/right expressions E_Expr *exprl = expr->first; E_Expr *exprr = exprl->next; E_IRTreeAndType l = e_irtree_and_type_from_expr(arena, exprl); E_IRTreeAndType r = e_irtree_and_type_from_expr(arena, exprr); E_TypeKey l_restype = e_type_unwrap(l.type_key); E_TypeKey r_restype = e_type_unwrap(r.type_key); E_TypeKind l_restype_kind = e_type_kind_from_key(l_restype); E_TypeKind r_restype_kind = e_type_kind_from_key(r_restype); if(e_type_kind_is_basic_or_enum(r_restype_kind)) { r_restype = e_type_unwrap_enum(r_restype); r_restype_kind = e_type_kind_from_key(r_restype); } E_TypeKey direct_type = e_type_unwrap(l_restype); direct_type = e_type_direct_from_key(direct_type); direct_type = e_type_unwrap(direct_type); U64 direct_type_size = e_type_byte_size_from_key(direct_type); e_msg_list_concat_in_place(&result.msgs, &l.msgs); e_msg_list_concat_in_place(&result.msgs, &r.msgs); // rjf: bad conditions? -> error if applicable, exit if(r.root->op == 0) { break; } else if(l_restype_kind != E_TypeKind_Ptr && l_restype_kind != E_TypeKind_Array) { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, exprl->location, "Cannot index into this type."); break; } else if(!e_type_kind_is_integer(r_restype_kind)) { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, exprr->location, "Cannot index with this type."); break; } else if(l_restype_kind == E_TypeKind_Ptr && direct_type_size == 0) { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, exprr->location, "Cannot index into pointers of zero-sized types."); break; } else if(l_restype_kind == E_TypeKind_Array && direct_type_size == 0) { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, exprr->location, "Cannot index into arrays of zero-sized types."); break; } // rjf: generate E_IRNode *new_tree = &e_irnode_nil; { switch(l.mode) { // rjf: offsets -> read from base offset default: case E_Mode_Null: case E_Mode_Offset: { // rjf: ops to compute the offset E_IRNode *offset_tree = e_irtree_resolve_to_value(arena, r.space, r.mode, r.root, r_restype); if(direct_type_size > 1) { E_IRNode *const_tree = e_irtree_const_u(arena, direct_type_size); offset_tree = e_irtree_binary_op_u(arena, RDI_EvalOp_Mul, offset_tree, const_tree); } // rjf: ops to compute the base offset (resolve to value if addr-of-pointer) E_IRNode *base_tree = l.root; if(l_restype_kind == E_TypeKind_Ptr && l.mode != E_Mode_Value) { base_tree = e_irtree_resolve_to_value(arena, l.space, l.mode, base_tree, l_restype); } // rjf: ops to compute the final address new_tree = e_irtree_binary_op_u(arena, RDI_EvalOp_Add, offset_tree, base_tree); }break; // rjf: values -> read from stack value case E_Mode_Value: { // rjf: ops to compute the offset E_IRNode *offset_tree = e_irtree_resolve_to_value(arena, r.space, r.mode, r.root, r_restype); if(direct_type_size > 1) { E_IRNode *const_tree = e_irtree_const_u(arena, direct_type_size); offset_tree = e_irtree_binary_op_u(arena, RDI_EvalOp_Mul, offset_tree, const_tree); } // rjf: ops to push stack value, push offset, + read from stack value new_tree = e_push_irnode(arena, RDI_EvalOp_ValueRead); new_tree->value.u64 = direct_type_size; e_irnode_push_child(new_tree, offset_tree); e_irnode_push_child(new_tree, l.root); }break; } } // rjf: fill result.root = new_tree; result.type_key = direct_type; result.mode = l.mode; result.space = l.space; }break; //- rjf: member accesses case E_ExprKind_MemberAccess: { // rjf: unpack left/right expressions E_Expr *exprl = expr->first; E_Expr *exprr = exprl->next; E_IRTreeAndType l = e_irtree_and_type_from_expr(arena, exprl); E_TypeKey l_restype = e_type_unwrap(l.type_key); E_TypeKind l_restype_kind = e_type_kind_from_key(l_restype); E_TypeKey check_type_key = l_restype; E_TypeKind check_type_kind = l_restype_kind; if(l_restype_kind == E_TypeKind_Ptr || l_restype_kind == E_TypeKind_LRef || l_restype_kind == E_TypeKind_RRef) { check_type_key = e_type_unwrap(e_type_direct_from_key(e_type_unwrap(l_restype))); check_type_kind = e_type_kind_from_key(check_type_key); } e_msg_list_concat_in_place(&result.msgs, &l.msgs); // rjf: look up member B32 r_found = 0; E_TypeKey r_type = zero_struct; U64 r_value = 0; B32 r_is_constant_value = 0; { Temp scratch = scratch_begin(&arena, 1); E_Member match = e_type_member_from_key_name__cached(check_type_key, exprr->string); if(match.kind != E_MemberKind_Null) { r_found = 1; r_type = match.type_key; r_value = match.off; } if(match.kind == E_MemberKind_Null) { E_Type *type = e_type_from_key(scratch.arena, check_type_key); if(type->enum_vals != 0) { String8 lookup_string = exprr->string; String8 lookup_string_append_1 = push_str8f(scratch.arena, "%S_%S", type->name, lookup_string); String8 lookup_string_append_2 = push_str8f(scratch.arena, "%S%S", type->name, lookup_string); E_EnumVal *enum_val_match = 0; for EachIndex(idx, type->count) { if(str8_match(type->enum_vals[idx].name, lookup_string, 0) || str8_match(type->enum_vals[idx].name, lookup_string_append_1, 0) || str8_match(type->enum_vals[idx].name, lookup_string_append_2, 0)) { enum_val_match = &type->enum_vals[idx]; break; } } if(enum_val_match != 0) { r_found = 1; r_type = check_type_key; r_value = enum_val_match->val; r_is_constant_value = 1; } } } scratch_end(scratch); } // rjf: bad conditions? -> error if applicable, exit if(e_type_key_match(e_type_key_zero(), check_type_key)) { break; } else if(exprr->kind != E_ExprKind_LeafMember) { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, exprl->location, "Expected member name."); break; } else if(!r_found) { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, exprr->location, "Could not find a member named `%S`.", exprr->string); break; } else if(check_type_kind != E_TypeKind_Struct && check_type_kind != E_TypeKind_Class && check_type_kind != E_TypeKind_Union && check_type_kind != E_TypeKind_Enum) { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, exprl->location, "Cannot perform member access on this type."); break; } // rjf: generate { // rjf: build tree E_IRNode *new_tree = l.root; E_Mode mode = l.mode; if(l_restype_kind == E_TypeKind_Ptr || l_restype_kind == E_TypeKind_LRef || l_restype_kind == E_TypeKind_RRef) { new_tree = e_irtree_resolve_to_value(arena, l.space, l.mode, new_tree, l_restype); mode = E_Mode_Offset; } if(r_value != 0 && !r_is_constant_value) { E_IRNode *const_tree = e_irtree_const_u(arena, r_value); new_tree = e_irtree_binary_op_u(arena, RDI_EvalOp_Add, new_tree, const_tree); } else if(r_is_constant_value) { new_tree = e_irtree_const_u(arena, r_value); mode = E_Mode_Value; } // rjf: fill result.root = new_tree; result.type_key = r_type; result.mode = mode; result.space = l.space; } }break; //- rjf: dereference case E_ExprKind_Deref: { // rjf: unpack operand E_Expr *r_expr = expr->first; E_IRTreeAndType r_tree = e_irtree_and_type_from_expr(arena, r_expr); E_TypeKey r_type = e_type_unwrap(r_tree.type_key); E_TypeKind r_type_kind = e_type_kind_from_key(r_type); E_TypeKey r_type_direct = e_type_direct_from_key(r_type); U64 r_type_direct_size = e_type_byte_size_from_key(r_type_direct); e_msg_list_concat_in_place(&result.msgs, &r_tree.msgs); // rjf: bad conditions? -> error if applicable, exit if(r_tree.root->op == 0) { break; } else if(r_type_direct_size == 0 && (r_type_kind == E_TypeKind_Ptr || r_type_kind == E_TypeKind_LRef || r_type_kind == E_TypeKind_RRef)) { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, r_expr->location, "Cannot dereference pointers of zero-sized types."); break; } else if(r_type_direct_size == 0 && r_type_kind == E_TypeKind_Array) { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, r_expr->location, "Cannot dereference arrays of zero-sized types."); break; } else if(r_type_kind != E_TypeKind_Array && r_type_kind != E_TypeKind_Ptr && r_type_kind != E_TypeKind_LRef && r_type_kind != E_TypeKind_RRef) { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, r_expr->location, "Cannot dereference this type."); break; } // rjf: generate { E_IRNode *new_tree = r_tree.root; if(r_tree.mode != E_Mode_Value && (r_type_kind == E_TypeKind_Ptr || r_type_kind == E_TypeKind_LRef || r_type_kind == E_TypeKind_RRef)) { new_tree = e_irtree_resolve_to_value(arena, r_tree.space, r_tree.mode, r_tree.root, r_type); } result.root = new_tree; result.type_key = r_type_direct; result.mode = E_Mode_Offset; result.space = r_tree.space; } }break; //- rjf: address-of case E_ExprKind_Address: { // rjf: unpack operand E_Expr *r_expr = expr->first; E_IRTreeAndType r_tree = e_irtree_and_type_from_expr(arena, r_expr); E_TypeKey r_type = r_tree.type_key; E_TypeKey r_type_unwrapped = e_type_unwrap(r_type); E_TypeKind r_type_unwrapped_kind = e_type_kind_from_key(r_type_unwrapped); e_msg_list_concat_in_place(&result.msgs, &r_tree.msgs); // rjf: bad conditions? -> error if applicable, exit if(r_tree.root->op == 0) { break; } else if(e_type_key_match(e_type_key_zero(), r_type_unwrapped)) { break; } // rjf: generate result.root = r_tree.root; result.type_key = e_type_key_cons_ptr(e_type_state->ctx->primary_module->arch, r_type_unwrapped, 0); result.mode = E_Mode_Value; result.space = r_tree.space; }break; //- rjf: cast case E_ExprKind_Cast: { // rjf: unpack operands E_Expr *cast_type_expr = expr->first; E_Expr *casted_expr = cast_type_expr->next; E_TypeKey cast_type = e_type_from_expr(cast_type_expr); E_TypeKind cast_type_kind = e_type_kind_from_key(cast_type); U64 cast_type_byte_size = e_type_byte_size_from_key(cast_type); E_IRTreeAndType casted_tree = e_irtree_and_type_from_expr(arena, casted_expr); e_msg_list_concat_in_place(&result.msgs, &casted_tree.msgs); E_TypeKey casted_type = e_type_unwrap(casted_tree.type_key); E_TypeKind casted_type_kind = e_type_kind_from_key(casted_type); U64 casted_type_byte_size = e_type_byte_size_from_key(casted_type); U8 in_group = e_type_group_from_kind(casted_type_kind); U8 out_group = e_type_group_from_kind(cast_type_kind); RDI_EvalConversionKind conversion_rule = rdi_eval_conversion_kind_from_typegroups(in_group, out_group); // rjf: bad conditions? -> error if applicable, exit if(casted_tree.root->op == 0) { break; } else if(cast_type_kind == E_TypeKind_Null) { break; } else if(conversion_rule != RDI_EvalConversionKind_Noop && conversion_rule != RDI_EvalConversionKind_Legal) { String8 text = str8_lit("Unknown cast conversion rule."); if(conversion_rule < RDI_EvalConversionKind_COUNT) { text.str = rdi_explanation_string_from_eval_conversion_kind(conversion_rule, &text.size); } e_msg(arena, &result.msgs, E_MsgKind_MalformedInput, expr->location, text); break; } // rjf: generate { E_IRNode *in_tree = e_irtree_resolve_to_value(arena, casted_tree.space, casted_tree.mode, casted_tree.root, casted_type); E_IRNode *new_tree = in_tree; if(conversion_rule == RDI_EvalConversionKind_Legal) { new_tree = e_irtree_convert_lo(arena, in_tree, out_group, in_group); } if(cast_type_byte_size < casted_type_byte_size && e_type_kind_is_integer(cast_type_kind)) { new_tree = e_irtree_trunc(arena, in_tree, cast_type); } result.root = new_tree; result.type_key = cast_type; result.mode = E_Mode_Value; result.space = casted_tree.space; } }break; //- rjf: sizeof case E_ExprKind_Sizeof: { // rjf: unpack operand E_Expr *r_expr = expr->first; E_TypeKey r_type = zero_struct; E_Space space = r_expr->space; switch(r_expr->kind) { case E_ExprKind_TypeIdent: case E_ExprKind_Ptr: case E_ExprKind_Array: case E_ExprKind_Func: { r_type = e_type_from_expr(r_expr); }break; default: { E_IRTreeAndType r_tree = e_irtree_and_type_from_expr(arena, r_expr); e_msg_list_concat_in_place(&result.msgs, &r_tree.msgs); r_type = r_tree.type_key; space = r_tree.space; }break; } // rjf: bad conditions? -> error if applicable, exit if(e_type_key_match(r_type, e_type_key_zero())) { break; } else if(e_type_kind_from_key(r_type) == E_TypeKind_Null) { break; } // rjf: generate { U64 r_type_byte_size = e_type_byte_size_from_key(r_type); result.root = e_irtree_const_u(arena, r_type_byte_size); result.type_key = e_type_key_basic(E_TypeKind_U64); result.mode = E_Mode_Value; result.space = space; } }break; //- rjf: typeof case E_ExprKind_Typeof: { // rjf: evaluate operand tree E_Expr *r_expr = expr->first; E_IRTreeAndType r_tree = e_irtree_and_type_from_expr(arena, r_expr); e_msg_list_concat_in_place(&result.msgs, &r_tree.msgs); // rjf: fill output result.root = e_irtree_const_u(arena, 0); result.type_key = r_tree.type_key; result.mode = E_Mode_Null; result.space = r_tree.space; }break; //- rjf: byteswap case E_ExprKind_ByteSwap: { // rjf: unpack operand E_Expr *r_expr = expr->first; E_IRTreeAndType r_tree = e_irtree_and_type_from_expr(arena, r_expr); e_msg_list_concat_in_place(&result.msgs, &r_tree.msgs); E_TypeKey r_type = e_type_unwrap(r_tree.type_key); E_TypeKind r_type_kind = e_type_kind_from_key(r_type); U64 r_type_size = e_type_byte_size_from_key(r_type); E_Space space = r_tree.space; // rjf: bad conditions? -> error if applicable, exit if(!e_type_kind_is_integer(r_type_kind) || (r_type_size != 8 && r_type_size != 4 && r_type_size != 2)) { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, expr->location, "Byteswapping this type is not supported."); break; } // rjf: generate { E_IRNode *node = e_push_irnode(arena, RDI_EvalOp_ByteSwap); E_IRNode *rhs = e_irtree_resolve_to_value(arena, space, r_tree.mode, r_tree.root, r_type); e_irnode_push_child(node, rhs); node->value.u64 = r_type_size; result.root = node; result.mode = E_Mode_Value; result.space = space; result.type_key = r_type; } }break; //- rjf: unary operations case E_ExprKind_Pos: { result = e_irtree_and_type_from_expr(arena, expr->first); }break; case E_ExprKind_Neg: case E_ExprKind_LogNot: case E_ExprKind_BitNot: { // rjf: unpack operand E_Expr *r_expr = expr->first; E_IRTreeAndType r_tree = e_irtree_and_type_from_expr(arena, r_expr); E_TypeKey r_type = e_type_unwrap(r_tree.type_key); E_TypeKind r_type_kind = e_type_kind_from_key(r_type); RDI_EvalTypeGroup r_type_group = e_type_group_from_kind(r_type_kind); E_TypeKey r_type_promoted = e_type_promote(r_type); RDI_EvalOp op = e_opcode_from_expr_kind(kind); e_msg_list_concat_in_place(&result.msgs, &r_tree.msgs); // rjf: bad conditions? -> error if applicable, exit if(r_tree.root->op == 0) { break; } else if(!rdi_eval_op_typegroup_are_compatible(op, r_type_group)) { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, expr->location, "Cannot use this operator on this type."); break; } // rjf: generate { E_IRNode *in_tree = e_irtree_resolve_to_value(arena, r_tree.space, r_tree.mode, r_tree.root, r_type); in_tree = e_irtree_convert_hi(arena, in_tree, r_type_promoted, r_type); E_IRNode *new_tree = e_irtree_unary_op(arena, op, r_type_group, in_tree); result.root = new_tree; result.type_key = r_type_promoted; result.mode = E_Mode_Value; result.space = r_tree.space; } }break; //- rjf: binary operations case E_ExprKind_Mul: case E_ExprKind_Div: case E_ExprKind_Mod: case E_ExprKind_Add: case E_ExprKind_Sub: case E_ExprKind_LShift: case E_ExprKind_RShift: case E_ExprKind_Less: case E_ExprKind_LsEq: case E_ExprKind_Grtr: case E_ExprKind_GrEq: case E_ExprKind_EqEq: case E_ExprKind_NtEq: case E_ExprKind_BitAnd: case E_ExprKind_BitXor: case E_ExprKind_BitOr: case E_ExprKind_LogAnd: case E_ExprKind_LogOr: { // rjf: unpack operands RDI_EvalOp op = e_opcode_from_expr_kind(kind); B32 is_comparison = e_expr_kind_is_comparison(kind); E_Expr *l_expr = expr->first; E_Expr *r_expr = l_expr->next; E_IRTreeAndType l_tree = e_irtree_and_type_from_expr(arena, l_expr); E_IRTreeAndType r_tree = e_irtree_and_type_from_expr(arena, r_expr); e_msg_list_concat_in_place(&result.msgs, &l_tree.msgs); e_msg_list_concat_in_place(&result.msgs, &r_tree.msgs); E_TypeKey l_type = e_type_unwrap_enum(e_type_unwrap(l_tree.type_key)); E_TypeKey r_type = e_type_unwrap_enum(e_type_unwrap(r_tree.type_key)); E_TypeKind l_type_kind = e_type_kind_from_key(l_type); E_TypeKind r_type_kind = e_type_kind_from_key(r_type); if(l_type.kind == E_TypeKeyKind_Reg) { l_type_kind = E_TypeKind_U64; l_type = e_type_key_basic(l_type_kind); } if(r_type.kind == E_TypeKeyKind_Reg) { r_type_kind = E_TypeKind_U64; r_type = e_type_key_basic(r_type_kind); } B32 l_is_pointer = (l_type_kind == E_TypeKind_Ptr); B32 l_is_decay = (l_type_kind == E_TypeKind_Array && l_tree.mode == E_Mode_Offset); B32 l_is_pointer_like = (l_is_pointer || l_is_decay); B32 r_is_pointer = (r_type_kind == E_TypeKind_Ptr); B32 r_is_decay = (r_type_kind == E_TypeKind_Array && r_tree.mode == E_Mode_Offset); B32 r_is_pointer_like = (r_is_pointer || r_is_decay); RDI_EvalTypeGroup l_type_group = e_type_group_from_kind(l_type_kind); RDI_EvalTypeGroup r_type_group = e_type_group_from_kind(r_type_kind); // rjf: bad conditions? -> error if applicable, exit if(l_tree.root->op == 0 || r_tree.root->op == 0) { break; } // rjf: determine arithmetic path #define E_ArithPath_Normal 0 #define E_ArithPath_PtrAdd 1 #define E_ArithPath_PtrSub 2 #define E_ArithPath_PtrArrayCompare 3 B32 ptr_arithmetic_mul_rptr = 0; U32 arith_path = E_ArithPath_Normal; if(kind == E_ExprKind_Add) { if(l_is_pointer_like && e_type_kind_is_integer(r_type_kind)) { arith_path = E_ArithPath_PtrAdd; } if(l_is_pointer_like && e_type_kind_is_integer(l_type_kind)) { arith_path = E_ArithPath_PtrAdd; ptr_arithmetic_mul_rptr = 1; } } else if(kind == E_ExprKind_Sub) { if(l_is_pointer_like && e_type_kind_is_integer(r_type_kind)) { arith_path = E_ArithPath_PtrAdd; } if(l_is_pointer_like && r_is_pointer_like) { E_TypeKey l_type_direct = e_type_unwrap(e_type_direct_from_key(e_type_unwrap(l_type))); E_TypeKey r_type_direct = e_type_unwrap(e_type_direct_from_key(e_type_unwrap(r_type))); U64 l_type_direct_byte_size = e_type_byte_size_from_key(l_type_direct); U64 r_type_direct_byte_size = e_type_byte_size_from_key(r_type_direct); if(l_type_direct_byte_size == r_type_direct_byte_size) { arith_path = E_ArithPath_PtrSub; } } } else if(kind == E_ExprKind_EqEq) { if(l_type_kind == E_TypeKind_Array && (r_type_kind == E_TypeKind_Ptr || r_is_decay)) { arith_path = E_ArithPath_PtrArrayCompare; } if(r_type_kind == E_TypeKind_Array && (l_type_kind == E_TypeKind_Ptr || l_is_decay)) { arith_path = E_ArithPath_PtrArrayCompare; } } // rjf: generate according to arithmetic path switch(arith_path) { //- rjf: normal arithmetic case E_ArithPath_Normal: { // rjf: bad conditions? -> error if applicable, exit if(!rdi_eval_op_typegroup_are_compatible(op, l_type_group) || !rdi_eval_op_typegroup_are_compatible(op, r_type_group)) { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, expr->location, "Cannot use this operator on this type."); break; } // rjf: generate { E_TypeKey final_type_key = is_comparison ? e_type_key_basic(E_TypeKind_Bool) : l_type; E_IRNode *l_value_tree = e_irtree_resolve_to_value(arena, l_tree.space, l_tree.mode, l_tree.root, l_type); E_IRNode *r_value_tree = e_irtree_resolve_to_value(arena, r_tree.space, r_tree.mode, r_tree.root, r_type); l_value_tree = e_irtree_convert_hi(arena, l_value_tree, l_type, l_type); r_value_tree = e_irtree_convert_hi(arena, r_value_tree, l_type, r_type); E_IRNode *new_tree = e_irtree_binary_op(arena, op, l_type_group, l_value_tree, r_value_tree); result.root = new_tree; result.type_key = final_type_key; result.mode = E_Mode_Value; result.space = l_tree.space; E_Space zero_space = {0}; if(MemoryMatchStruct(&result.space, &zero_space)) { result.space = r_tree.space; } } }break; //- rjf: pointer addition case E_ArithPath_PtrAdd: { // rjf: map l/r to ptr/int E_IRTreeAndType *ptr_tree = &l_tree; E_IRTreeAndType *int_tree = &r_tree; B32 ptr_is_decay = l_is_decay; if(ptr_arithmetic_mul_rptr) { ptr_tree = &r_tree; int_tree = &l_tree; ptr_is_decay = r_is_decay; } // rjf: unpack type E_TypeKey direct_type = e_type_unwrap(e_type_direct_from_key(e_type_unwrap(ptr_tree->type_key))); U64 direct_type_size = e_type_byte_size_from_key(direct_type); // rjf: generate { E_IRNode *ptr_root = ptr_tree->root; if(!ptr_is_decay) { ptr_root = e_irtree_resolve_to_value(arena, ptr_tree->space, ptr_tree->mode, ptr_root, ptr_tree->type_key); } E_IRNode *int_root = int_tree->root; int_root = e_irtree_resolve_to_value(arena, int_tree->space, int_tree->mode, int_root, int_tree->type_key); if(direct_type_size > 1) { E_IRNode *const_root = e_irtree_const_u(arena, direct_type_size); int_root = e_irtree_binary_op_u(arena, RDI_EvalOp_Mul, int_root, const_root); } E_TypeKey ptr_type = ptr_tree->type_key; if(ptr_is_decay) { ptr_type = e_type_key_cons_ptr(e_type_state->ctx->primary_module->arch, direct_type, 0); } E_IRNode *new_root = e_irtree_binary_op_u(arena, op, ptr_root, int_root); result.root = new_root; result.type_key = ptr_type; result.mode = E_Mode_Value; result.space = l_tree.space; E_Space zero_space = {0}; if(MemoryMatchStruct(&result.space, &zero_space)) { result.space = r_tree.space; } } }break; //- rjf: pointer subtraction case E_ArithPath_PtrSub: { // rjf: unpack type E_TypeKey direct_type = e_type_unwrap(e_type_direct_from_key(e_type_unwrap(l_type))); U64 direct_type_size = e_type_byte_size_from_key(direct_type); // rjf: generate E_IRNode *l_root = l_tree.root; E_IRNode *r_root = r_tree.root; if(!l_is_decay) { l_root = e_irtree_resolve_to_value(arena, l_tree.space, l_tree.mode, l_root, l_type); } if(!r_is_decay) { r_root = e_irtree_resolve_to_value(arena, r_tree.space, r_tree.mode, r_root, r_type); } E_IRNode *op_tree = e_irtree_binary_op_u(arena, op, l_root, r_root); E_IRNode *new_tree = op_tree; if(direct_type_size > 1) { E_IRNode *const_tree = e_irtree_const_u(arena, direct_type_size); new_tree = e_irtree_binary_op_u(arena, RDI_EvalOp_Div, new_tree, const_tree); } result.root = new_tree; result.type_key = e_type_key_basic(E_TypeKind_U64); result.mode = E_Mode_Value; result.space = l_tree.space; E_Space zero_space = {0}; if(MemoryMatchStruct(&result.space, &zero_space)) { result.space = r_tree.space; } }break; //- rjf: pointer array comparison case E_ArithPath_PtrArrayCompare: { // rjf: map l/r to pointer/array B32 ptr_is_decay = l_is_decay; E_IRTreeAndType *ptr_tree = &l_tree; E_IRTreeAndType *arr_tree = &r_tree; if(l_type_kind == E_TypeKind_Array && l_tree.mode == E_Mode_Value) { ptr_is_decay = r_is_decay; ptr_tree = &r_tree; arr_tree = &l_tree; } // rjf: resolve pointer to value, sized same as array E_IRNode *ptr_root = ptr_tree->root; E_IRNode *arr_root = arr_tree->root; if(!ptr_is_decay) { ptr_root = e_irtree_resolve_to_value(arena, ptr_tree->space, ptr_tree->mode, ptr_tree->root, ptr_tree->type_key); } // rjf: read from pointer into value, to compare with array E_IRNode *mem_root = e_irtree_mem_read_type(arena, ptr_tree->space, ptr_root, arr_tree->type_key); // rjf: generate result.root = e_irtree_binary_op(arena, op, RDI_EvalTypeGroup_Other, mem_root, arr_root); result.type_key = e_type_key_basic(E_TypeKind_Bool); result.mode = E_Mode_Value; result.space = ptr_tree->space; E_Space zero_space = {0}; if(MemoryMatchStruct(&result.space, &zero_space)) { result.space = arr_tree->space; } }break; } }break; //- rjf: ternary operators case E_ExprKind_Ternary: { // rjf: unpack operands E_Expr *c_expr = expr->first; E_Expr *l_expr = c_expr->next; E_Expr *r_expr = l_expr->next; E_IRTreeAndType c_tree = e_irtree_and_type_from_expr(arena, c_expr); E_IRTreeAndType l_tree = e_irtree_and_type_from_expr(arena, l_expr); E_IRTreeAndType r_tree = e_irtree_and_type_from_expr(arena, r_expr); e_msg_list_concat_in_place(&result.msgs, &c_tree.msgs); e_msg_list_concat_in_place(&result.msgs, &l_tree.msgs); e_msg_list_concat_in_place(&result.msgs, &r_tree.msgs); E_TypeKey c_type = e_type_unwrap(c_tree.type_key); E_TypeKey l_type = e_type_unwrap(l_tree.type_key); E_TypeKey r_type = e_type_unwrap(r_tree.type_key); E_TypeKind c_type_kind = e_type_kind_from_key(c_type); E_TypeKind l_type_kind = e_type_kind_from_key(l_type); E_TypeKind r_type_kind = e_type_kind_from_key(r_type); E_TypeKey result_type = l_type; // rjf: bad conditions? -> error if applicable, exit if(c_tree.root->op == 0 || l_tree.root->op == 0 || r_tree.root->op == 0) { break; } else if(!e_type_kind_is_integer(c_type_kind) && c_type_kind != E_TypeKind_Bool) { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, expr->location, "Conditional term must be an integer or boolean type."); } else if(!e_type_match(l_type, r_type)) { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, expr->location, "Left and right terms must have matching types."); } // rjf: generate { E_IRNode *c_value_tree = e_irtree_resolve_to_value(arena, c_tree.space, c_tree.mode, c_tree.root, c_type); E_IRNode *l_value_tree = e_irtree_resolve_to_value(arena, l_tree.space, l_tree.mode, l_tree.root, l_type); E_IRNode *r_value_tree = e_irtree_resolve_to_value(arena, r_tree.space, r_tree.mode, r_tree.root, r_type); l_value_tree = e_irtree_convert_hi(arena, l_value_tree, result_type, l_type); r_value_tree = e_irtree_convert_hi(arena, r_value_tree, result_type, r_type); E_IRNode *new_tree = e_irtree_conditional(arena, c_value_tree, l_value_tree, r_value_tree); result.root = new_tree; result.type_key = result_type; result.mode = E_Mode_Value; result.space = l_expr->space; E_Space zero_space = {0}; if(MemoryMatchStruct(&result.space, &zero_space)) { result.space = r_expr->space; } } }break; //- rjf: leaf bytecode case E_ExprKind_LeafBytecode: { E_IRNode *new_tree = e_irtree_bytecode_no_copy(arena, expr->bytecode); E_TypeKey final_type_key = expr->type_key; result.root = new_tree; result.type_key = final_type_key; result.mode = expr->mode; result.space = expr->space; }break; //- rjf: (unexpected) leaf member case E_ExprKind_LeafMember: { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, expr->location, "(internal) Leaf member not expected here."); }break; //- rjf: leaf string literal case E_ExprKind_LeafStringLiteral: { String8 string = expr->string; E_TypeKey type_key = e_type_key_cons_array(e_type_key_basic(E_TypeKind_UChar8), string.size); E_IRNode *new_tree = e_irtree_string_literal(arena, string); result.root = new_tree; result.type_key = type_key; result.mode = E_Mode_Value; }break; //- rjf: leaf bools case E_ExprKind_LeafBool: { result.root = e_irtree_const_u(arena, expr->value.u64); result.type_key = expr->type_key; result.mode = E_Mode_Value; }break; //- rjf: leaf U64s case E_ExprKind_LeafU64: { U64 val = expr->value.u64; E_IRNode *new_tree = e_irtree_const_u(arena, val); E_TypeKey type_key = zero_struct; if(0){} else if(val <= max_S32){type_key = e_type_key_basic(E_TypeKind_S32);} else if(val <= max_S64){type_key = e_type_key_basic(E_TypeKind_S64);} else {type_key = e_type_key_basic(E_TypeKind_U64);} result.root = new_tree; result.type_key = type_key; result.mode = E_Mode_Value; }break; //- rjf: leaf F64s case E_ExprKind_LeafF64: { U64 val = expr->value.u64; E_IRNode *new_tree = e_irtree_const_u(arena, val); result.root = new_tree; result.type_key = e_type_key_basic(E_TypeKind_F64); result.mode = E_Mode_Value; }break; //- rjf: leaf F32s case E_ExprKind_LeafF32: { U32 val = expr->value.u32; E_IRNode *new_tree = e_irtree_const_u(arena, val); result.root = new_tree; result.type_key = e_type_key_basic(E_TypeKind_F32); result.mode = E_Mode_Value; }break; //- rjf: leaf identifiers case E_ExprKind_LeafIdent: { E_Expr *macro_expr = e_string2expr_lookup(e_ir_ctx->macro_map, expr->string); if(macro_expr == &e_expr_nil) { e_msgf(arena, &result.msgs, E_MsgKind_ResolutionFailure, expr->location, "`%S` could not be found.", expr->string); } else { e_string2expr_map_inc_poison(e_ir_ctx->macro_map, expr->string); result = e_irtree_and_type_from_expr(arena, macro_expr); e_string2expr_map_dec_poison(e_ir_ctx->macro_map, expr->string); } }break; //- rjf: leaf offsets case E_ExprKind_LeafOffset: { E_IRNode *new_tree = e_push_irnode(arena, RDI_EvalOp_ConstU64); new_tree->value.u64 = expr->value.u64; result.root = new_tree; result.type_key = expr->type_key; result.mode = E_Mode_Offset; result.space = expr->space; }break; //- rjf: leaf file paths case E_ExprKind_LeafFilePath: { U128 key = fs_key_from_path_range(expr->string, r1u64(0, max_U64)); E_Space space = {E_SpaceKind_FileSystem, .u128 = key}; U64 size = fs_size_from_path(expr->string); E_IRNode *base_offset = e_irtree_const_u(arena, 0); E_IRNode *set_space = e_irtree_set_space(arena, space, base_offset); result.root = set_space; result.type_key = e_type_key_cons_array(e_type_key_basic(E_TypeKind_U8), size); result.mode = E_Mode_Offset; result.space = space; }break; //- rjf: types case E_ExprKind_TypeIdent: { result.root = e_irtree_const_u(arena, 0); result.type_key = expr->type_key; result.mode = E_Mode_Null; result.space = expr->space; }break; //- rjf: (unexpected) type expressions case E_ExprKind_Ptr: case E_ExprKind_Array: case E_ExprKind_Func: { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, expr->location, "Type expression not expected."); }break; //- rjf: definitions case E_ExprKind_Define: { E_Expr *lhs = expr->first; E_Expr *rhs = lhs->next; result = e_irtree_and_type_from_expr(arena, rhs); if(lhs->kind != E_ExprKind_LeafIdent) { e_msgf(arena, &result.msgs, E_MsgKind_MalformedInput, expr->location, "Left side of assignment must be an unused identifier."); } }break; } return result; } //- rjf: irtree -> linear ops/bytecode internal void e_append_oplist_from_irtree(Arena *arena, E_IRNode *root, E_OpList *out) { U32 op = root->op; switch(op) { case RDI_EvalOp_Stop: case RDI_EvalOp_Skip: { // TODO: error - invalid ir-tree op }break; case E_IRExtKind_Bytecode: { e_oplist_push_bytecode(arena, out, root->string); }break; case E_IRExtKind_SetSpace: { E_Space space = {0}; MemoryCopy(&space, &root->value, sizeof(space)); e_oplist_push_set_space(arena, out, space); for(E_IRNode *child = root->first; child != &e_irnode_nil; child = child->next) { e_append_oplist_from_irtree(arena, child, out); } }break; case RDI_EvalOp_Cond: { // rjf: generate oplists for each child E_OpList prt_cond = e_oplist_from_irtree(arena, root->first); E_OpList prt_left = e_oplist_from_irtree(arena, root->first->next); E_OpList prt_right = e_oplist_from_irtree(arena, root->first->next->next); // rjf: put together like so: // 1. , Op_Cond (sizeof(2)) // 2. , Op_Skip (sizeof(3)) // 3. // rjf: modify prt_right in place to create step 2 e_oplist_push_op(arena, &prt_right, RDI_EvalOp_Skip, e_value_u64(prt_left.encoded_size)); // rjf: merge 1 into out e_oplist_concat_in_place(out, &prt_cond); e_oplist_push_op(arena, out, RDI_EvalOp_Cond, e_value_u64(prt_right.encoded_size)); // rjf: merge 2 into out e_oplist_concat_in_place(out, &prt_right); // rjf: merge 3 into out e_oplist_concat_in_place(out, &prt_left); }break; case RDI_EvalOp_ConstString: { e_oplist_push_string_literal(arena, out, root->string); }break; default: { if(op >= RDI_EvalOp_COUNT) { // TODO: error - invalid ir-tree op } else { // rjf: append ops for all children U16 ctrlbits = rdi_eval_op_ctrlbits_table[op]; U64 child_count = RDI_POPN_FROM_CTRLBITS(ctrlbits); U64 idx = 0; for(E_IRNode *child = root->first; child != &e_irnode_nil && idx < child_count; child = child->next, idx += 1) { e_append_oplist_from_irtree(arena, child, out); } // rjf: emit op to compute this node e_oplist_push_op(arena, out, (RDI_EvalOp)root->op, root->value); } }break; } } internal E_OpList e_oplist_from_irtree(Arena *arena, E_IRNode *root) { E_OpList ops = {0}; e_append_oplist_from_irtree(arena, root, &ops); return ops; } internal String8 e_bytecode_from_oplist(Arena *arena, E_OpList *oplist) { // rjf: allocate buffer U64 size = oplist->encoded_size; U8 *str = push_array_no_zero(arena, U8, size); // rjf: iterate loose op nodes; fill buffer U8 *ptr = str; U8 *opl = str + size; for(E_Op *op = oplist->first; op != 0; op = op->next) { U32 opcode = op->opcode; switch(opcode) { default: { // rjf: compute bytecode advance U16 ctrlbits = rdi_eval_op_ctrlbits_table[opcode]; U64 extra_byte_count = RDI_DECODEN_FROM_CTRLBITS(ctrlbits); U8 *next_ptr = ptr + 1 + extra_byte_count; Assert(next_ptr <= opl); // rjf: fill bytecode ptr[0] = opcode; MemoryCopy(ptr + 1, &op->value.u64, extra_byte_count); // rjf: advance ptr = next_ptr; }break; case RDI_EvalOp_ConstString: { // rjf: compute bytecode advance U8 *next_ptr = ptr + 2 + op->value.u64; Assert(next_ptr <= opl); // rjf: fill ptr[0] = opcode; ptr[1] = (U8)op->value.u64; MemoryCopy(ptr+2, op->string.str, op->value.u64); // rjf: advance ptr = next_ptr; }break; case E_IRExtKind_Bytecode: { // rjf: compute bytecode advance U64 size = op->string.size; U8 *next_ptr = ptr + size; Assert(next_ptr <= opl); // rjf: fill bytecode MemoryCopy(ptr, op->string.str, size); // rjf: advance ptr = next_ptr; }break; case E_IRExtKind_SetSpace: { // rjf: compute bytecode advance U64 extra_byte_count = sizeof(E_Space); U8 *next_ptr = ptr + 1 + extra_byte_count; Assert(next_ptr <= opl); // rjf: fill bytecode ptr[0] = opcode; MemoryCopy(ptr + 1, &op->value.u128, extra_byte_count); // rjf: advance ptr = next_ptr; }break; } } // rjf: fill result String8 result = {0}; result.size = size; result.str = str; return result; }